Pourquoi faire à la main ce qu’on peut faire avec cert-manager ?
On a vu dans l’article précédent comment utiliser les Ingress permet de simplifier l’accès depuis l’extérieur vers nos applications hébergées dans Kubernetes. Cependant, il manque encore une petite pierre à l’édifice : l’accès en HTTPS et les certificats TLS.
On en est où déjà ?
Maintenant qu’on a notre cluster Kubernetes et nos premier Ingress, quand on appelle l’URL http://blobiblob.zwindler.fr on tombe sur notre application.
Petit rappel, voilà à quoi ressemble notre Ingress :
cat nginx-ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx name: nginx-ingress spec: rules: - host: blobiblob.zwindler.fr http: paths: - path: / backend: serviceName: blobiblob-app-svc servicePort: 8080
So far, so good.
Maintenant, si on veut rajouter du HTTPS, on peut le faire pratiquement sans modification directement avec nos Ingress. En effet, l’Ingress Controller nginx va par défaut utiliser le certificat autosigné généré par Kubernetes. Il suffit de modifier l’Ingress et d’appliquer les modifications suivantes :
cat nginx-ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx name: nginx-ingress spec: tls: - hosts: - blobiblob.zwindler.fr rules: - host: blobiblob.zwindler.fr http: paths: - path: / backend: serviceName: blobiblob-app-svc servicePort: 8080
On remarque que la syntaxe est un poil différente, avec l’ajout d’une section « tls » contenant une liste d’hôtes et indiquant à notre Ingress qu’il s’agit de backend à exposer en HTTPS.
En revanche, même si dans l’absolu ça fonctionnera, on aura quand même une erreur de certificat, ce qui n’est pas forcément agréable et qui peut surtout vous pénaliser en terme de visites, notamment à cause des messages d’erreur bien anxiogènes de nos navigateurs web.
A titre d’exemple, j’ai perdu au minimum entre 20% et 30% de mon trafic quotidien le jour où j’ai oublié (en vrai c’était plus compliqué) de renouveler le certificat de ce blog (oups). Donc en gros, ne le faites pas…
Générer des certificats
Pour faire du HTTPS, il va donc falloir générer des certificats valides et surtout certifiées par une autorité de certification présente sur le poste client. Si on reste sur le cas de l’Ingress Controller Nginx, il existe une page de documentation dédiée à ce sujet.
Mais bon, ça serait quand même bien mieux si on pouvait faire ça automagiquement, quand même, nan ?
Let’s Encrypt + Kubernetes = <3
Je ne vais pas vous faire l’affront de vous expliquer ce qu’est Let’s Encrypt, d’autant qu’en plus, j’ai déjà écris quelques articles sur le sujet (Proxmox).
Vous avez peut être vu une section à ce sujet : il existait un projet kube-lego qui avait été désigné comme le moyen de générer des certificats et de les intégrer au processus de livraison d’applications dans K8s.
Cependant, pas de bol :
The latest Kubernetes release that kube-lego officially supports is 1.8. The officially endorsed successor is cert-manager.
cert-manager
La plupart des tutos que j’ai pu trouver sur le net citent justement kube-lego, mais pas son successeur, cert-manager. C’est d’autant plus dommage que la documentation n’est pas toujours hyper claire ;-).
Quelques liens utiles pour bien démarrer :
- Page principale du projet
- Le chart déployable avec Helm
- Le guide de déploiement de cert-manager
- Et surtout, le schéma qui explique comment cert-manager fonctionne

Helm !
Cette fois ci (contrairement à l’article précédent), on a de la chance, le Chart fonctionne même « on premise » ! On ne se gène donc pas et on le déploie avec Helm fissa :
helm install --name cert-manager --namespace kube-system stable/cert-manager --set ingressShim.extraArgs='{--default-issuer-name=letsencrypt-issuer,--default-issuer-kind=ClusterIssuer}' NAME: cert-manager LAST DEPLOYED: Sun Feb 25 18:27:44 2018 NAMESPACE: kube-system STATUS: DEPLOYED [...] NOTES: cert-manager has been deployed successfully! In order to begin issuing certificates, you will need to set up a ClusterIssuer or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer). More information on the different types of issuers and how to configure them can be found in our documentation: https://github.com/jetstack/cert-manager/tree/v0.2.3/docs/api-types/issuer For information on how to configure cert-manager to automatically provision Certificates for Ingress resources, take a look at the `ingress-shim` documentation: https://github.com/jetstack/cert-manager/blob/v0.2.3/docs/user-guides/ingress-shim.md
Et voilà, vous avez installé cert-manager.
Note: si vous n’avez pas encore installé Helm et Tiller, je vous conseille d’aller lire cet article sur le sujet.
Création d’un ClusterIssuer
Mais qu’est ce que c’est qu’un ClusterIssuer ?
Eh bien en fait, cert-manager étend les concepts de Kubernetes (oui c’est vrai il n’y en avait pas beaucoup) et ajoute ce nouvel objet/concept qui correspond à un fournisseur de certificats. La documentation donne l’exemple de l’API Let’s Encrypt « staging ».
cat certmanager/letsencrypt-staging-cluster-issuer.yaml apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: letsencrypt-staging-cluster-issuer spec: acme: server: https://acme-staging.api.letsencrypt.org/directory email: mon_email_admin@zwindler.fr privateKeySecretRef: name: letsencrypt-staging http01: {} kubectl apply -f certmanager/letsencrypt-staging-cluster-issuer.yaml
On comprend donc bien que cette nouvelle couche d’abstraction sera particulièrement utile dans le cas où de nouveaux types de ClusterIssuers font leur apparition dans le futur.
Faire un test
Si vous êtes pressés de tester, on va commencer par faire le test avec le ClusterIssuer « staging » de Let’s Encrypt, qui permet de réaliser des tests sans risquer de se faire bloquer (car il faut savoir qu’en cas de demandes répétés, Let’s encrypt bloque les demandeurs de certificats).
Maintenant qu’on a notre ClusterIssuer, on peut créer un Certificate (second objet introduit par cert-manager). Le Certificate a un nom, pointe vers un Secret et est lié à notre ClusterIssuer. Il sera demandé pour le sous domaine blobiblob.zwindler.fr, notre application web qui a besoin d’un certificat Let’s Encrypt.
cat certmanager/certmanager-blobiblob.yaml apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: blobiblob.zwindler.fr namespace: default spec: secretName: blobiblob.zwindler.fr-tls issuerRef: name: letsencrypt-staging-cluster-issuer kind: ClusterIssuer commonName: blobiblob.zwindler.fr acme: config: - http01: {} domains: - blobiblob.zwindler.fr kubectl apply -f certmanager/certmanager-blobiblob.yaml
Et dernier point, nous devons mettre à jour notre Ingress pour qu’il tire parti de notre secret et indiquer qu’il utilise tls-acme :
cat ingress/ingress-blobiblob.zwindler.fr.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/tls-acme: "true" kubernetes.io/ingress.class: nginx name: ingress-blobiblob.zwindler.fr spec: tls: - hosts: - blobiblob.zwindler.fr secretName: blobiblob.zwindler.fr-tls rules: - host: blobiblob.zwindler.fr http: paths: - path: / backend: serviceName: blobiblob-app-svc servicePort: 8080 kubectl apply -f certmanager/certmanager-blobiblob.yaml
Youpi, ça marche !
Hum non, pas tout à fait. Le certificat a bien été généré, mais si vous avez suivi, depuis quelque temps je parle de l’environnement « staging » de Let’s Encrypt. Ce service est très pratique car il est beaucoup moins sévère que le service Let’s Encrypt de production dans le blocage des utilisateurs abusifs.
kubectl get certificate NAME AGE blobiblob.zwindler.fr 2d
Cependant, le certificat généré n’est pas vraiment plus utilisable dans l’état que le certificat autosigné de Kubernetes. Et pour cause, il s’agit d’un certificat de Test « Fake LE Intermediate X1 »
IRL
Maintenant qu’on a validé que tout marchait et qu’on allait pas se faire bloquer par LE, on on remplace donc « staging » par « prod » (et on met la bonne URL dans l’attribut « server »)
cat certmanager/letsencrypt-prod-cluster-issuer.yaml apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: letsencrypt-prod-cluster-issuer spec: acme: server: https://acme-v01.api.letsencrypt.org/directory email: mon_email_admin@zwindler.fr privateKeySecretRef: name: letsencrypt-prod http01: {}
On met à jour notre Certificate pour qu’il utilie ce nouveau ClusterIssuer :
cat certmanager/certmanager-blobiblob.yaml apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: blobiblob.zwindler.fr namespace: default spec: secretName: blobiblob.zwindler.fr-tls issuerRef: name: letsencrypt-prod-cluster-issuer kind: ClusterIssuer commonName: blobiblob.zwindler.fr acme: config: - http01: {} domains: - blobiblob.zwindler.fr
On se débarrase du certificat « Fake » qu’on vient de générer :
kubectl delete certificate blobiblob.zwindler.fr certificate "blobiblob.zwindler.fr" deleted
Et enfin on en régénère un nouveau, réel cette fois ci (puis on l’applique dans l’Ingress) :
kubectl apply -f certmanager/certmanager-blobiblob.yaml kubectl apply -f ingress/nginx-ingress.yaml
A partir de là, vous devriez avoir maintenant une application web en HTTPS avec un certificat let’s Encrypt valide et surtout généré automatiquement :)

Sources
- https://letsencrypt.org/docs/staging-environment/
- https://letsencrypt.org/docs/rate-limits/
- https://letsencrypt.org/docs/acme-protocol-updates/
- https://letsencrypt.org/2017/06/14/acme-v2-api.html
Server should be change to
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
Hi,
I just read the annoncements about ACME v2, and it is indeed recommended to switch to v2 even though there is no EOL for v1 (yet)
https://letsencrypt.org/2017/06/14/acme-v2-api.html
Can you confirm that it works using ACME v2 works with cert-manager?
[Production] https://acme-v02.api.letsencrypt.org/directory
[Staging] https://acme-staging-v02.api.letsencrypt.org/directory
Bonjour :)
Tes articles sur kubernetes viennent de me permettre de réaliser en une nuit ce que j’ai tenté pendant une semaine… Alors merci :D
Par contre, je n’arrive pas à avoir le certif… Mon site est une app flask et dans les logs je peux voir
qu’une requete a été faite sur « /.well-known/acme-challenge/blablablatoken » et a retourné 404… Comment remédier à ce problème ?
Merci d’avance :D
Ce qui m’étonne, c’est que la requête acme-challenge soit visible dans tes logs flask. Normalement tout est géré par l’ingress, c’est tout l’avantage justement.
L’ingress ne devrait rediriger que le 443 vers tes applis container, et le 80 doit être ouvert côté Ingress, mais uniquement parce que let’s encrypt fait ses requêtes acme challenge sur le 80
Oui c’est ce que j’ai lu…
Voila un petit md avec les fichiers que j’ai utilisé https://gist.github.com/phineas0fog/8af8b57c6669c5d41b51fb4d88381935