diff --git a/README.md b/README.md index 1fc5031..56b05d5 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,59 @@ -# ACME webhook example +# Introduction +First, [RTFM](https://cert-manager.io/docs/configuration/acme/dns01/). -The ACME issuer type supports an optional 'webhook' solver, which can be used -to implement custom DNS01 challenge solving logic. +Have you read it? If you haven't go read it. Cuz I'll keep everything short. -This is useful if you need to use cert-manager with a DNS provider that is not -officially supported in cert-manager core. +This is a dns01 solver for [FreeDNS](https://freedns.afraid.org/). -## Why not in core? - -As the project & adoption has grown, there has been an influx of DNS provider -pull requests to our core codebase. As this number has grown, the test matrix -has become un-maintainable and so, it's not possible for us to certify that -providers work to a sufficient level. - -By creating this 'interface' between cert-manager and DNS providers, we allow -users to quickly iterate and test out new integrations, and then packaging -those up themselves as 'extensions' to cert-manager. - -We can also then provide a standardised 'testing framework', or set of -conformance tests, which allow us to validate the a DNS provider works as -expected. - -## Creating your own webhook - -Webhook's themselves are deployed as Kubernetes API services, in order to allow -administrators to restrict access to webhooks with Kubernetes RBAC. - -This is important, as otherwise it'd be possible for anyone with access to your -webhook to complete ACME challenge validations and obtain certificates. - -To make the set up of these webhook's easier, we provide a template repository -that can be used to get started quickly. - -### Creating your own repository - -### Running the test suite - -All DNS providers **must** run the DNS01 provider conformance testing suite, -else they will have undetermined behaviour when used with cert-manager. - -**It is essential that you configure and run the test suite when creating a -DNS01 webhook.** - -An example Go test file has been provided in [main_test.go](https://github.com/jetstack/cert-manager-webhook-example/blob/master/main_test.go). - -You can run the test suite with: +Pull requests welcome. I'm completely unfamiliar with golang. I did it by looking at +other webhook repos and this is the result. +## Install ```bash -$ TEST_ZONE_NAME=example.com. make test +$ cd deploy +$ helm show values freedns-webhook > my-values.yaml +$ edit my-values.yaml +$ helm install -n cert-manager [INSTALLATION_NAME] freedns-webhook/ -f my-values.yaml ``` -The example file has a number of areas you must fill in and replace with your -own options in order for tests to pass. +## ClusterIssuer for Let's encrypt staging +```yaml +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-staging +spec: + acme: + email: myemail@example.com + server: https://acme-staging-v02.api.letsencrypt.org/directory + privateKeySecretRef: + name: le-staging + solvers: + - dns01: + webhook: + groupName: acme.freedns.afraid.org + solverName: freedns-solver + config: + secretName: freedns-auth +``` + +## FreeDNS webhook settings +Normally if you haven't changed anything, the default namespace should be +`cert-manager`. It should be within the same namespace for the webhook when +you do `helm install webhook -n cert-manager`. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: freedns-auth + namespace: cert-manager +data: + username: [YOUR_USERNAME_IN_BASE64] + password: [YOUR_PASSWORD_IN_BASE64] +type: Opaque +``` + +Additionally, the following names can be customized +* acme.freedns.afraid.org +* freedns-auth \ No newline at end of file diff --git a/deploy/freedns-webhook/templates/rbac.yaml b/deploy/freedns-webhook/templates/rbac.yaml index cf0650a..42cf507 100644 --- a/deploy/freedns-webhook/templates/rbac.yaml +++ b/deploy/freedns-webhook/templates/rbac.yaml @@ -8,6 +8,45 @@ metadata: release: {{ .Release.Name }} heritage: {{ .Release.Service }} --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "freedns-webhook.fullname" . }}:secret-read + namespace: {{ .Release.Namespace }} + labels: + app: {{ include "freedns-webhook.name" . }} + chart: {{ include "freedns-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +rules: + - apiGroups: + - '' + resources: + - 'secrets' + verbs: + - 'get' +--- +# Grant the webhook permission to read the secret +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "freedns-webhook.fullname" . }}:secret-read + namespace: {{ .Release.Namespace }} + labels: + app: {{ include "freedns-webhook.name" . }} + chart: {{ include "freedns-webhook.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "freedns-webhook.fullname" . }}:secret-read +subjects: + - apiGroup: "" + kind: ServiceAccount + name: {{ include "freedns-webhook.fullname" . }} + namespace: {{ .Release.Namespace }} +--- # Grant the webhook permission to read the ConfigMap containing the Kubernetes # apiserver's requestheader-ca-certificate. # This ConfigMap is automatically created by the Kubernetes apiserver. diff --git a/deploy/freedns-webhook/values.yaml b/deploy/freedns-webhook/values.yaml index a631632..126c186 100644 --- a/deploy/freedns-webhook/values.yaml +++ b/deploy/freedns-webhook/values.yaml @@ -14,7 +14,7 @@ certManager: image: repository: penguinade/cert-manager-webhook-freedns - tag: latest + tag: 2022.03.15 pullPolicy: IfNotPresent nameOverride: "" diff --git a/freedns/freedns.go b/freedns/freedns.go index 2e863b7..2ddb547 100755 --- a/freedns/freedns.go +++ b/freedns/freedns.go @@ -24,6 +24,7 @@ type FreeDNSOperations interface { type FreeDNS struct { AuthCookie *http.Cookie DomainId string + LoggedOut bool } const URI_LOGIN = "https://freedns.afraid.org/zc.php?step=2" @@ -73,6 +74,8 @@ func _HttpRequest(method string, url string, PostData url.Values, ExCookie *http return nil, "", err } + req.Header.Set("User-Agent", "github.com/tgckpg/cert-manager-webhook-freedns (2022.03.15)") + if ExCookie != nil { req.AddCookie(ExCookie) } @@ -112,6 +115,7 @@ func (dnsObj *FreeDNS) Login(Username string, Password string) error { for _, cookie := range resp.Cookies() { if cookie.Name == "dns_cookie" { dnsObj.AuthCookie = cookie + dnsObj.LoggedOut = false } } @@ -119,7 +123,7 @@ func (dnsObj *FreeDNS) Login(Username string, Password string) error { } func (dnsObj *FreeDNS) Logout() error { - if dnsObj.AuthCookie == nil { + if dnsObj.LoggedOut { return nil } @@ -128,7 +132,7 @@ func (dnsObj *FreeDNS) Logout() error { return err } - dnsObj.AuthCookie = nil + dnsObj.LoggedOut = true return nil } diff --git a/freedns/freedns_test.go b/freedns/freedns_test.go new file mode 100755 index 0000000..d1f7cde --- /dev/null +++ b/freedns/freedns_test.go @@ -0,0 +1,42 @@ +package freedns + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +/* +func TestHttpRequest(t *testing.T) { + _HttpRequest("GET", "http://127.0.0.1:12345", nil, nil) +} +*/ + +func TestGetDomainFromZone(t *testing.T) { + assert.Equal(t, GetDomainFromZone("a.b.example.com"), "example.com") + assert.Equal(t, GetDomainFromZone("example.com"), "example.com") +} + +func TestOperations(t *testing.T) { + freeDNS := FreeDNS{} + + var UserName = os.Getenv("FREEDNS_USERNAME") + var Password = os.Getenv("FREEDNS_PASSWORD") + var SelectedDomain = os.Getenv("FREEDNS_DOMAIN") + + require.NotEmpty(t, UserName, "Please set the env vars for FREEDNS_USERNAME") + require.NotEmpty(t, Password, "Please set the env vars for FREEDNS_PASSWORD") + require.NotEmpty(t, SelectedDomain, "Please set the env vars for FREEDNS_DOMAIN") + + require.Nil(t, freeDNS.Login(UserName, Password)) + require.Nil(t, freeDNS.SelectDomain(SelectedDomain)) + require.Nil(t, freeDNS.AddRecord("TXT", "", "\"TEST\"", false, "")) + + id, _ := freeDNS.FindRecord(SelectedDomain, "TXT", "\"TEST\"") + require.NotEmpty(t, id) + require.Nil(t, freeDNS.DeleteRecord(id)) + require.Nil(t, freeDNS.Logout()) + require.Equal(t, freeDNS.LoggedOut, true) +}