...
NOTE - These instructions were taken mostly from some random blog post: https://itnext.io/automated-tls-with-cert-manager-and-letsencrypt-for-kubernetes-7daaa5e0cae4
Prerequisites
- A 1+ node Kubernetes cluster
- Helm and Tiller installed on Kubernetes cluster
- NGINX Ingress Controller running in Kubernetes cluster
- A DNS record setup to point at the IP of the NGINX controller
Let's Encrypt: Staging vs Production
Before we get into any of this, I'd like to point out that we used the staging server for this example because it is much more forgiving about how often you can request a new certificate.
...
For more information on the rate limits in place on LetsEncrypt, see https://letsencrypt.org/docs/rate-limits/
Step 1: Install cert-manager via helm
Create cert-manager-values.yaml
on disk:
...
Code Block | ||
---|---|---|
| ||
$ helm- install --name my-release -f cert-manager-values.yaml cert-manager |
...
Code Block | ||
---|---|---|
| ||
$ kubectl logs -f deploy/cert-manager -n kube-system |
Step 2: Create a ClusterIssuer
An Issuer
or ClusterIssuer
represents the CA that will distribute certificates for your ingress rules - in our case, this is LetsEncrypt.
...
If you see all of the above, then you should be ready to issue some certificates!
Step 3: Annotate Your Ingress
In the startup logs for the cert-manager, you may have seen info message about missing annotations, for example:
...
Simply Edit one of your ingress rules to add a single line - you'll need to point the certmanager.k8s.io/cluster-issuer
annotation at the name of your ClusterIssuer
.
For example, with kubectl edit -n cis-dev ing cis-girder
I would add the following annotation:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
certmanager.k8s.io/cluster-issuer: letsencrypt-staging <---- Add this line
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
name: cis-girder
namespace: cis-dev
spec:
rules:
- host: dev.cis.ndslabs.org
http:
paths:
- backend:
serviceName: cis-girder
servicePort: 80
path: /
- backend:
serviceName: cis-girder
servicePort: 8080
path: /girder
- backend:
serviceName: cis-girder
servicePort: 8080
path: /static
- backend:
serviceName: cis-girder
servicePort: 8080
path: /api
tls:
- hosts:
- dev.cis.ndslabs.org
secretName: cis-tls-secret |
NOTE: The rest of the ingress fields should be able to stay the same. Only those rules with the annotation will be affected by cert-manager, so only add it to ingress rules for which you'd like for it to control/renew TLS.
Step 4: Issue a Certificate
Create dev.cis.ndslabs.org-certificate.yaml
on disk:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: cis-tls-secretcert spec: secretName: cis-tls-secret dnsNames: - dev.cis.ndslabs.org acme: config: - http01: ingressClass: nginx domains: - dev.cis.ndslabs.org issuerRef: name: letsencrypt-staging kind: ClusterIssuer |
Then pass this file to kubectl create
:
Code Block | |||||
---|---|---|---|---|---|
| |||||
$ kubectl create -f dev.cis.ndslabs.org-certificate.yaml |
NOTE: I suspect that this is required, but did not attempt otherwise - as you can see above, I did explicitly specify a namespace here to make sure my certificate/secret was created in the same namespace as my ingress rule. I have no idea how it would otherwise figure out which namespaces needs which secrets.
What should you see? (Console)
That's it! If everything worked correctly, you should see some successful log messages in the cert-manager logs:
Code Block | |||||
---|---|---|---|---|---|
| |||||
$ kubectl logs -f -n kube-system deploy/cert-manager ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... I0726 19:29:40.287760 1 controller.go:152] ingress-shim controller: syncing item 'cis-dev/cis-girder' I0726 19:29:40.287796 1 sync.go:124] Certificate "cis-tls-secret" for ingress "cis-girder" already exists I0726 19:29:40.287857 1 sync.go:127] Certificate "cis-tls-secret" for ingress "cis-girder" is up to date I0726 19:29:40.287914 1 controller.go:166] ingress-shim controller: Finished processing work item "cis-dev/cis-girder" I0726 19:29:44.287733 1 controller.go:177] certificates controller: syncing item 'cis-dev/cis-tls-secret' I0726 19:29:44.287981 1 sync.go:259] Preparing certificate cis-dev/cis-tls-secret with issuer I0726 19:29:44.288047 1 acme.go:162] getting private key (letsencrypt-staging->tls.key) for acme issuer kube-system/letsencrypt-staging I0726 19:29:44.288886 1 prepare.go:247] Cleaning up previous order for certificate cis-dev/cis-tls-secret I0726 19:29:44.288918 1 prepare.go:263] Cleaning up old/expired challenges for Certificate cis-dev/cis-tls-secret I0726 19:29:44.288981 1 logger.go:22] Calling CreateOrder I0726 19:29:44.862177 1 acme.go:196] Created order for domains: [{dns dev.cis.ndslabs.org}] I0726 19:29:44.862261 1 logger.go:52] Calling GetAuthorization I0726 19:29:44.959729 1 prepare.go:263] Cleaning up old/expired challenges for Certificate cis-dev/cis-tls-secret I0726 19:29:44.959768 1 helpers.go:188] Found status change for Certificate "cis-tls-secret" condition "ValidateFailed": "False" -> "False"; setting lastTransitionTime to 2018-07-26 19:29:44.959762093 +0000 UTC m=+273629.459083531 I0726 19:29:44.959795 1 sync.go:266] Issuing certificate... I0726 19:29:44.959830 1 acme.go:162] getting private key (letsencrypt-staging->tls.key) for acme issuer kube-system/letsencrypt-staging I0726 19:29:44.960303 1 logger.go:27] Calling GetOrder I0726 19:29:45.258232 1 logger.go:37] Calling FinalizeOrder I0726 19:29:46.062504 1 issue.go:104] successfully obtained certificate: cn="dev.cis.ndslabs.org" altNames=[dev.cis.ndslabs.org] url="https://acme-staging-v02.api.letsencrypt.org/acme/order/6536636/4852241" I0726 19:29:46.079900 1 sync.go:285] Certificate issued successfully I0726 19:29:46.079951 1 helpers.go:188] Found status change for Certificate "cis-tls-secret" condition "Ready": "False" -> "True"; setting lastTransitionTime to 2018-07-26 19:29:46.079945046 +0000 UTC m=+273630.579266468 I0726 19:29:46.080259 1 sync.go:191] Certificate cis-dev/cis-tls-secret scheduled for renewal in 1438 hours I0726 19:29:46.091762 1 controller.go:191] certificates controller: Finished processing work item "cis-dev/cis-tls-secret" |
You should also see that a new secret has been created for your certificate:
Code Block | ||||
---|---|---|---|---|
| ||||
$ kubectl get certificate,secret -n cis-dev NAME AGE certificate.certmanager.k8s.io/cis-tls-secret 5m NAME TYPE DATA AGE secret/cis-tls-secret kubernetes.io/tls 2 2m secret/default-token-p2sxz kubernetes.io/service-account-token 3 5d |
What should your users see? (Browser)
Navigating to your ingress rule, you should see that you now have a LetsEncrypt TLS certificate.
...
This will show you who issued the certificate, and can also shed light on who is the owner of a particular domain.
Certificate Differences: LetsEncrypt staging vs production
If you are using the LetsEncrypt staging server, the issued certificate will still appear Not Secure
at first glance. Examining it, you should see that it was Issued by: Fake LE Intermediate X1
...
For more information on the differences between staging and production on LetsEncrypt, see https://letsencrypt.org/docs/rate-limits/
Migrating from Staging to Production
If you've made it this far using staging, then you are probably ready to start issuing real certificates - simply change https://acme-staging-v02.api.letsencrypt.org/directory
above to https://acme-v02.api.letsencrypt.org/directory
...