Traefik : Intégration ACME
Traefik dispose nativement d'une intégration à ACME et Let's Encrypt, sans passer par l'habituel cert-manager.
La documentation officielle décrit comment l'implémenter, avec un DNS CloudFlare comme exemple :
Le contexte est simple ici :
- Domaine déjà déclaré chez GoDaddy
- LoadBalancer nginx frontal en passthru, donc SSL termination sur le contrôleur Ingress
- Cluster RKE2 (mais pareil pour K8S)
- Une application déjà exposée via Ingress : ArgoCD, à qui l'on doit délivrer un certificat Let's Encrypt valide.
Etape 1 : Activer l'intégration Let's Encrypt x Traefik
Activer de Let's Encrypt
Nous ne documenterons pas le déploiement de Traefik ici, mais on partira du principe qu'il est déployé depuis un Helm Chart.
Par défaut Traefik n'a pas de CertResolver définit, le Helm Chart a les values suivantes :
# -- Certificates resolvers configuration
certResolvers: {}
# letsencrypt:
# # for challenge options cf. https://doc.traefik.io/traefik/https/acme/
# email: email@example.com
# dnsChallenge:
# # also add the provider's required configuration under env
# # or expand then from secrets/configmaps with envfrom
# # cf. https://doc.traefik.io/traefik/https/acme/#providers
# provider: digitalocean
# # add futher options for the dns challenge as needed
# # cf. https://doc.traefik.io/traefik/https/acme/#dnschallenge
# delayBeforeCheck: 30
# resolvers:
# - 1.1.1.1
# - 8.8.8.8
# tlsChallenge: true
# httpChallenge:
# entryPoint: "web"
# # It has to match the path with a persistent volume
# storage: /data/acme.json
Il y a trois types de challenges :
- La partie dnsChallenge est utilisée lorsque les services ne sont pas publiquement disponible, il est ainsi d'interroger un provider avec une clé d'API pour vérifier l'appartenance du domaine et récupérer un certificat.
- La partie tlsChallenge permet d'utiliser HTTPS plutôt que HTTP pour la validation du certificat. Le service doit être donc accessible sur le port 443 depuis internet.
- La partie httpChallenge, standard, utilise de l'HTTP. Le service doit être accessible sur le port 80 depuis internet.
Pour activer l'intégration avec Let's Encrypt, on ajuste les valeurs suivantes :
certResolvers:
letsencrypt:
email: votremail@example.com
storage: /data/acme.jsonStockage des certificats
La section storage permet de définir où les certificats seront stockés, en le pointant de préférence vers un volume persistent pour éviter de perdre les certificats (et devoir les redemander) en cas de crash du pod.
Pensez donc à activer la persistence des données dans la section suivante, enabled doit être fixé à true.
persistence:
accessMode: ReadWriteOnce
annotations: {}
enabled: true
name: data
path: /data
size: 128MiIl semble y avoir un bug lié au podSecurityContext. Si l'on déploie avec la persistence d'activer, on obtient une erreur sur le resolver letsencrypt.
Sur GitHub, on trouve un paquet de solution qui ne fonctionnent pas. Les dernières mentionnent deux choses qui fonctionnent :
- Modifier le paramètre fsGroup
- Exécuter l'initContainer avec l'ID de l'utilisateur fixé dans podSecurityContext, qui est le 65532.
Aucune des solutions n'a fonctionné chez moi, en revanche, ce qui a fonctionné c'est de définir le fsGroup de podSecurityContext à 65532 et de modifier l'initContainer pour l'exécuter et donner les droits au compte du conteneur, 65532.
deployment:
initContainers:
# The "volume-permissions" init container is required if you run into permission issues.
# Related issue: https://github.com/traefik/traefik-helm-chart/issues/396
- name: volume-permissions
image: busybox:latest
command: ["sh", "-c", "touch /data/acme.json; chmod -v 600 /data/acme.json; chown -R 65532:65532 /data"]
securityContext:
runAsNonRoot: false
runAsGroup: 0
runAsUser: 0
volumeMounts:
- name: data
mountPath: /data
podSecurityContext:
fsGroupChangePolicy: OnRootMismatch
runAsGroup: 65532
runAsNonRoot: true
runAsUser: 65532
fsGroup: 65532Mettez à jour votre Helm Chart, en cas de soucis avec l'initContainer, le Pod restera en pending mais vous pourrez vérifiez ses logs.
Si l'initContainer tourne, les logs de traefik indiqueront si le certresolver de Let's Encrypt est ignoré, uniquement en cas d'erreur.
helm -n traefik upgrade traefik traefik/traefik -f values.traefik.yamlIl est possible de vérifier le contenu du fichier /data/acme.json directement avec un shell.
Let's Encrypt limite le nombre de requête pour un domaine. Si traefik plante une dizaine de fois par exemple, suite à un bug ou une erreur humaine, vous pourriez bien vous retrouver sans certificats.
Etape 2: Création de la route Ingress
Traefik a ses propres CRDs, différentes d'un Ingress standard, on définira donc une IngressRoute dans l'API de traefik
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: argocd-server
namespace: argocd
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`argocd.prod.zenops.fr`)
priority: 10
services:
- name: argocd-server
port: 80
- kind: Rule
match: Host(`argocd.prod.zenops.fr`) && Headers(`Content-Type`, `application/grpc`)
priority: 11
services:
- name: argocd-server
port: 80
scheme: h2c
tls:
certResolver: letsencrypt
secretName: argocd-server-tls
Déployer l'Ingress, le certificat devrait être automatiquement être demandé par Traefik, qui l'ajoutera dans son fichier /data/acme.json .