DNS-01 challenge: Certificats wildcard TLS sur Kubernetes
Sommaire
Lorsqu'on déploie des applications sur un cluster kubernetes, celles-ci sont souvent exposées à l'extérieur en HTTPS via un ingress controller, généralement en utilisant cert-manager pour gérer les certificats TLS avec Let's Encrypt.
La demande de certificat TLS est généralement faite avec le challenge HTTP-01, qui vérifie que l'on peut accéder à du contenu sur une URL spécifique.
Cependant, d'autres types de challenges existent, notamment le challenge DNS-01 : Let's Encrypt vérifier une entrée DNS spécifiques, pour s'assurer que le domaine sur lequel nous demandons un certificat nous appartien bien. Cela peut être utile dans plusieurs cas :
- Les applications ne sont pas exposées sur Internet (Let's Encrypt ne peut alors pas se connecter sur l'URL)
- Le certificats wildcard, car il n'y a pas d'URL par défaut ou héberger la preuve de certificat
On peut retrouver plus d'informations sur les différents types de challenge ici : https://letsencrypt.org/docs/challenge-types/
Pour demander un certificat de cette manière dans un cluster kubernetes, on aura besoin de mettre à jour automatiquement les entrées DNS associées à l'application chez le cloud provider. Seulement les providers "majeurs" sont nativement supportés, mais on peut étendre ce fonctionnement avec des webhooks.
Dans cet article, nous verrons comment paramétrer un cluster kubernetes, configurer cert-manager pour gérer des challenges DNS-01 avec des certificats Wildcards, et déployer une application utilisatn ces certificats.
Paramétrage et Pré-requis
Pour la démo, on utilise les outils et plateformes suivants :
- Cluster Kubernetes Kapsule de chez Scaleway
- Serveur DNS de chez Scaleway toujours, car on peut le piloter avec les cert-manager webhooks
- Terraform pour facilement tout mettre en palce
- k8s Lens pour facilement voir les ressourcs déployées
L'article ne couvre pas l'installation de Terraform & autres outils kubectl, helm and co.
Provisionnement du cluster
Avec Terraform, on provisionne un cluster Kapsule simple. Il suffit juste d'utilser les ressources scaleway_k8s_cluster
et scaleway_k8s_pool
:
1terraform {
2 required_providers {
3 scaleway = {
4 source = "scaleway/scaleway"
5 version = "2.1.0"
6 }
7 }
8 required_version = ">= 0.13"
9}
10
11provider "scaleway" {
12 zone = "fr-par-1"
13 region = "fr-par"
14}
15
16resource "scaleway_k8s_cluster" "k8s_cluster" {
17 name = var.k8s_cluster_name
18 version = "1.22.2"
19 cni = "cilium"
20}
21
22resource "scaleway_k8s_pool" "k8s_pool" {
23 cluster_id = scaleway_k8s_cluster.k8s_cluster.id
24 name = var.k8s_pool_name
25 node_type = "DEV1-M"
26 size = 2
27 autohealing = true
28}
29
30resource "local_file" "kubeconfig" {
31 content = scaleway_k8s_cluster.k8s_cluster.kubeconfig[0].config_file
32 filename = "${path.module}/${scaleway_k8s_cluster.k8s_cluster.name}-kubeconfig"
33 file_permission = "0600"
34}
Maintenant on peut provisionner le cluster :
1$ terraform init
2$ terraform plan -out k8s_scaleway.out
3$ terraform apply k8s_scaleway.out
Après quelques minutes d'attente, le cluster est prêt et dispo, avec Nginx comme ingress controller, un load-balancer, et cert-manager de configuré. On a également un fichier kubeconfig à disposition pour contacter le cluster !
Paramétrage DNS
On peut maintenant configurer l'entrée DNS pour faire le lien avec l'IP du load-balancer. On pourrait faire cela automatiquement, mais les ressources Terraform n'existent pas encore, c'est prévu pour une prochaine release, voir ici.
Pour ce setup, le domaine utilisé est scw.vrchr.fr, et donc chaque URL se terminant ainsi sera automatiquement dirigée vers l'IP du load-balancer du cluster kubernetes préalablement installé.
Note : le cluster de démo n'existe plus à l'heure actuelle, pas la peine d'essayer de le joindre, cela n'a été fait que le temps de l'article ;)
Certificat wildcard avec DNS-01
Webhook Scaleway
Pour rapppel, pour certains cloud providers, on doit utiliser les webhooks cert-manager afin de piloter les entrées DNS. C'est le cas pour Scaleway, alors installons ce qu'il faut :
1$ git clone https://github.com/scaleway/cert-manager-webhook-scaleway.git
2$ cd cert-manager-webhook-scaleway
3$ helm install scaleway-webhook deploy/scaleway-webhook --set secret.accessKey=changeme --set secret.secretKey=changeme --set certManager.serviceAccountName=jetstack-cert-manager --namespace=cert-manager
Attention au serviceAccountName qui doit être le même que celui créé avec le chart helm cert-manager.
On peut maintenant voir les ressources déployées :
Issuer and Certificates
Maintenant, on va créer un "Certificate Issuer", qui sera responsable des demandes de certificats TLS. Il s'agit d'une Custom Resource avec la propriété "dns01". Dans l'exemple on créé un ClusterIssuer plutôt qu'un Issuer, afin de gérer les demandes de certificats sur l'ensemble du cluster :
1---
2apiVersion: cert-manager.io/v1
3kind: ClusterIssuer
4metadata:
5 name: scaleway-issuer-prod
6spec:
7 acme:
8 email: name@company.com
9 server: https://acme-v02.api.letsencrypt.org/directory
10 privateKeySecretRef:
11 name: scaleway-private-key-secret
12 solvers:
13 - dns01:
14 webhook:
15 groupName: acme.scaleway.com
16 solverName: scaleway
1$ kubectl apply -f cert_issuer.yaml
On peut rajouter un issuer pour les certificats de type staging si besoin.
Maintenant, créons une demande de certificat ! La demande sera faite pour "*.scw.vrchr.fr", qui sera enregistrée dans un secret nommé "wildcard-scw-vrchr-fr-tls” :
1apiVersion: cert-manager.io/v1
2kind: Certificate
3metadata:
4 name: wildcard-scw-vrchr-fr-tls
5 namespace: default
6spec:
7 dnsNames:
8 - "*.scw.vrchr.fr"
9 issuerRef:
10 name: scaleway-issuer-prod
11 kind: ClusterIssuer
12 secretName: wildcard-scw-vrchr-fr-tls
Que se passe-t'il maintenant ?
- Cert-manager détecte une demande de certificat, et génère une demande vers les serveurs Let’s Encrypt
- Cert-manager utilise le webhook pour créer une entrée DNS TXT DNS : _acme-challenge.scw.vrchr.fr
- Les serveurs Let's Encrypt vont vérifier cette entrée DNS, et valident la requpete. Il faut être patient, le temps de la propagation DNS qui peut être longue
- Une fois la demande validée, le certificat est créé dans un secret disponible dans le cluster !
Deploiement d'application
OK ! Maintenant qu'on a un certificat wildcard généré automatiquement, utilisons-le sur des applications.
On créé un déploiement simple, on l'expose via un service, et on créé une règle d'ingress. La partie importante de la configuration est dans l'ingress, qui spécifie quel certificat TLS utilisé. Pas besoin d'annotations particulières pour géré le certificat automatique, car il est déjà créé et géré ailleurs.
1[...]
2spec:
3 tls:
4 - hosts:
5 - "*.scw.vrchr.fr"
6 secretName: wildcard-scw-vrchr-fr-tls
7[...]
Comme on a créé un certificat wildcard, chaque FQDN qui matchera le wildcard sera valide.
Voici l'exemple d'un déploiement complet de l'application jpetazzo/webcolor qui expose différentes couleurs.
Exemple de déploiement & service:
1---
2apiVersion: apps/v1
3kind: Deployment
4metadata:
5 name: green
6spec:
7 selector:
8 matchLabels:
9 app: green
10 replicas: 1
11 template:
12 metadata:
13 labels:
14 app: green
15 spec:
16 containers:
17 - image: jpetazzo/webcolor
18 name: webcolor
19
20---
21apiVersion: v1
22kind: Service
23metadata:
24 name: green
25spec:
26 ports:
27 - port: 8000
28 targetPort: 8000
29 selector:
30 app: green
Ingress:
1---
2apiVersion: networking.k8s.io/v1
3kind: Ingress
4metadata:
5 name: color-ingress
6 annotations:
7 kubernetes.io/ingress.class: nginx
8spec:
9 tls:
10 - hosts:
11 - "*.scw.vrchr.fr"
12 secretName: wildcard-scw-vrchr-fr-tls
13 rules:
14 - host: green.scw.vrchr.fr
15 http:
16 paths:
17 - path: /
18 pathType: Prefix
19 backend:
20 service:
21 name: green
22 port:
23 number: 8000
24 - host: purple.scw.vrchr.fr
25 http:
26 paths:
27 - path: /
28 pathType: Prefix
29 backend:
30 service:
31 name: purple
32 port:
33 number: 8000
34 - host: yellow.scw.vrchr.fr
35 http:
36 paths:
37 - path: /
38 pathType: Prefix
39 backend:
40 service:
41 name: yellow
42 port:
43 number: 8000
1$ kubectl apply -f dep_green.yaml
2$ kubectl apply -f wildcard_tls_ingress.yaml
Maintenant, accédons à https://green.scw.vrchr.fr, qui devrait présenter le certificat wildcard !
Conclusion et remarques
Dans cet article on a pu :
- Déployer un cluster kubernetes sur Scaleway avec Terraform
- Déployer Cert Manager & Nginx via Helm & Terraform
- Déployer les DNS Webhook Scaleway
- Générer des certificats wildcard avec le protocole DNS-01 via les webhooks
- Déployer une application dans un sous-domaine, présentant le certificats wildcard
Maintenant on peut déployer autant d'application dans le sous-domaine que l'on veut, en utilisant toujours le même certificat. Pour faciliter l'exercice, ici tout a été fait dans un même namespace, ne pas hésiter à lire la doc de cert-manager pour partager les certificats entre plusieurs namespaces.
Enfin on retrouvera le code d'exemple de l'articile ici : https://gitlab.com/rverchere/vrchr-k8s-dns-demo
Et n'oublions pas : "It's always DNS" !
Références
Quelques ressources très utiles pour comprendre les concepts utilisés dans l'articule :