blob: 3da4fc46a91aa20c0afce98d914d19bba16ed219 [file] [log] [blame] [view]
giolekvaeea069c2021-07-19 18:13:08 +04001# ACME webhook for Gandi (cert-manager-webhook-gandi)
2`cert-manager-webhook-gandi` is an ACME webhook for [cert-manager]. It provides an ACME (read: Let's Encrypt) webhook for [cert-manager], which allows to use a `DNS-01` challenge with [Gandi]. This allows to provide Let's Encrypt certificates to [Kubernetes] for service protocols other than HTTP and furthermore to request wildcard certificates. Internally it uses the [Gandi LiveDNS API] to communicate with Gandi.
3
4Quoting the [ACME DNS-01 challenge]:
5
6> This challenge asks you to prove that you control the DNS for your domain name by putting a specific value in a TXT record under that domain name. It is harder to configure than HTTP-01, but can work in scenarios that HTTP-01 cant. It also allows you to issue wildcard certificates. After Lets Encrypt gives your ACME client a token, your client will create a TXT record derived from that token and your account key, and put that record at _acme-challenge.<YOUR_DOMAIN>. Then Lets Encrypt will query the DNS system for that record. If it finds a match, you can proceed to issue a certificate!
7
8
9## Building
10Build the container image `cert-manager-webhook-gandi:latest`:
11
12 make build
13
14
15## Image
16Ready made images are hosted on Docker Hub ([image tags]). Use at your own risk:
17
18 bwolf/cert-manager-webhook-gandi
19
20
21### Release History
22Refer to the [ChangeLog](ChangeLog.md) file.
23
24
25## Compatibility
26This webhook has been tested with [cert-manager] v0.13.1 and Kubernetes v0.17.x on `amd64`. In theory it should work on other hardware platforms as well but no steps have been taken to verify this. Please drop me a note if you had success.
27
28
29## Testing with Minikube
301. Build this webhook in Minikube:
31
32 minikube start --memory=4G --more-options
33 eval $(minikube docker-env)
34 make build
35 docker images | grep webhook
36
372. Install [cert-manager] with [Helm]:
38
39 kubectl create namespace cert-manager
40 kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/v0.13.1/deploy/manifests/00-crds.yaml
41
42 helm repo add jetstack https://charts.jetstack.io
43 helm install cert-manager --namespace cert-manager \
44 --set 'extraArgs={--dns01-recursive-nameservers=8.8.8.8:53\,1.1.1.1:53}' \
45 jetstack/cert-manager
46
47 kubectl get pods --namespace cert-manager --watch
48
49 **Note**: refer to Name servers in the official [documentation][setting-nameservers-for-dns01-self-check] according the `extraArgs`.
50
51 **Note**: ensure that the custom CRDS of cert-manager match the major version of the cert-manager release by comparing the URL of the CRDS with the helm info of the charts app version:
52
53 helm search repo jetstack
54
55 Example output:
56
57 NAME CHART VERSION APP VERSION DESCRIPTION
58 jetstack/cert-manager v0.13.1 v0.13.1 A Helm chart for cert-manager
59
60 Check the state and ensure that all pods are running fine (watch out for any issues regarding the `cert-manager-webhook-` pod and its volume mounts):
61
62 kubectl describe pods -n cert-manager | less
63
64
653. Create the secret to keep the Gandi API key in the default namespace, where later on the Issuer and the Certificate are created:
66
67 kubectl create secret generic gandi-credentials \
68 --from-literal=api-token='<GANDI-API-KEY>'
69
70 **Note**: See [RBAC Authorization]:
71
72 > A Role can only be used to grant access to resources within a single namespace.
73
74 *As far as I understand cert-manager, the `Secret` must reside in the same namespace as the `Issuer` and `Certificate` resource.*
75
764. Grant permission for the service-account to access the secret holding the Gandi API key:
77
78 kubectl apply -f rbac.yaml
79
805. Deploy this locally built webhook (add `--dry-run` to try it and `--debug` to inspect the rendered manifests; Set `logLevel` to 6 for verbose logs):
81
82 helm install cert-manager-webhook-gandi \
83 --namespace cert-manager \
84 --set image.repository=cert-manager-webhook-gandi \
85 --set image.tag=latest \
86 --set logLevel=2 \
87 ./deploy/cert-manager-webhook-gandi
88
89 To deploy using the image from Docker Hub (for example using the `v0.1.1` tag):
90
91 helm install cert-manager-webhook-gandi \
92 --namespace cert-manager \
93 --set image.tag=v0.1.1 \
94 --set logLevel=2 \
95 ./deploy/cert-manager-webhook-gandi
96
97 Check the logs
98
99 kubectl get pods -n cert-manager --watch
100 kubectl logs -n cert-manager cert-manager-webhook-gandi-XYZ
101
1026. Create a staging issuer (email addresses with the suffix `example.com` are forbidden):
103
104 cat << EOF | sed "s/invalid@example.com/$email/" | kubectl apply -f -
105 apiVersion: cert-manager.io/v1alpha2
106 kind: Issuer
107 metadata:
108 name: letsencrypt-staging
109 namespace: default
110 spec:
111 acme:
112 # The ACME server URL
113 server: https://acme-staging-v02.api.letsencrypt.org/directory
114 # Email address used for ACME registration
115 email: invalid@example.com
116 # Name of a secret used to store the ACME account private key
117 privateKeySecretRef:
118 name: letsencrypt-staging
119 solvers:
120 - dns01:
121 webhook:
122 groupName: acme.bwolf.me
123 solverName: gandi
124 config:
125 apiKeySecretRef:
126 key: api-token
127 name: gandi-credentials
128 EOF
129
130 Check status of the Issuer:
131
132 kubectl describe issuer letsencrypt-staging
133
134 *Note*: The production Issuer is [similar][ACME documentation].
135
1367. Issue a [Certificate] for your `$DOMAIN`:
137
138 cat << EOF | sed "s/example-com/$DOMAIN/" | kubectl apply -f -
139 apiVersion: cert-manager.io/v1alpha2
140 kind: Certificate
141 metadata:
142 name: example-com
143 spec:
144 dnsNames:
145 - example-com
146 issuerRef:
147 name: letsencrypt-staging
148 secretName: example-com-tls
149 EOF
150
151 Check the status of the Certificate:
152
153 kubectl describe certificate $DOMAIN
154
155 Display the details like the common name and subject alternative names:
156
157 kubectl get secret $DOMAIN-tls -o yaml
158
1598. Issue a wildcard Certificate for your `$DOMAIN`:
160
161 cat << EOF | sed "s/example-com/$DOMAIN/" | kubectl apply -f -
162 apiVersion: cert-manager.io/v1alpha2
163 kind: Certificate
164 metadata:
165 name: wildcard-example-com
166 spec:
167 dnsNames:
168 - '*.example-com'
169 issuerRef:
170 name: letsencrypt-staging
171 secretName: wildcard-example-com-tls
172 EOF
173
174 Check the status of the Certificate:
175
176 kubectl describe certificate $DOMAIN
177
178 Display the details like the common name and subject alternative names:
179
180 kubectl get secret wildcard-$DOMAIN-tls -o yaml
181
18299. Uninstall this webhook:
183
184 helm uninstall cert-manager-webhook-gandi --namespace cert-manager
185 kubectl delete -f rbac.yaml
186 kubectl delete gandi-credentials
187
188100. Uninstalling cert-manager:
189This is out of scope here. Refer to the official [documentation][cert-manager-uninstall].
190
191
192## Development
193**Note**: If some tool (IDE or build process) fails resolving a dependency, it may be the cause that a indirect dependency uses `bzr` for versioning. In such a case it may help to put the `bzr` binary into `$PATH` or `$GOPATH/bin`.
194
195
196## Release process
197- Code changes result in a new image version and Git tag
198- Helm chart changes result in a new chart version
199- All other changes are pushed to master
200- All versions are to be documented in [ChangeLog](ChangeLog.md)
201
202
203## Conformance test
204Please note that the test is not a typical unit or integration test. Instead it invokes the web hook in a Kubernetes-like environment which asks the web hook to really call the DNS provider (.i.e. Gandi). It attempts to create an `TXT` entry like `cert-manager-dns01-tests.example.com`, verifies the presence of the entry via Google DNS. Finally it removes the entry by calling the cleanup method of web hook.
205
206**Note**: Replace the string `darwin` in the URL below with an OS matching your system (e.g. `linux`).
207
208As said above, the conformance test is run against the real Gandi API. Therefore you *must* have a Gandi account, a domain and an API key.
209
210``` shell
211cp testdata/gandi/api-key.yaml.sample testdata/gandi/api-key.yaml
212echo -n $YOUR_GANDI_API_KEY | base64 | pbcopy # or xclip
213$EDITOR testdata/gandi/api-key.yaml
214./scripts/fetch-test-binaries.sh
215TEST_ZONE_NAME=example.com. go test -v .
216```
217
218
219[ACME DNS-01 challenge]: https://letsencrypt.org/docs/challenge-types/#dns-01-challenge
220[ACME documentation]: https://cert-manager.io/docs/configuration/acme/
221[Certificate]: https://cert-manager.io/docs/usage/certificate/
222[cert-manager]: https://cert-manager.io/
223[Gandi]: https://gandi.net/
224[Gandi LiveDNS API]: https://doc.livedns.gandi.net
225[Helm]: https://helm.sh
226[image tags]: https://hub.docker.com/r/bwolf/cert-manager-webhook-gandi
227[Kubernetes]: https://kubernetes.io/
228[RBAC Authorization]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
229[setting-nameservers-for-dns01-self-check]: https://cert-manager.io/docs/configuration/acme/dns01/#setting-nameservers-for-dns01-self-check
230[cert-manager-uninstall]: https://cert-manager.io/docs/installation/uninstall/kubernetes/