Dans cet article, je vais parler de la façon d'automatiser la commande et le renouvellement des certificats de Let's Encrypt (et pas seulement) pour Ingress in Kubernetes à l'aide du gestionnaire de certificats complémentaire. Mais je vais commencer par une brève introduction à l'essence du problème.Un peu de programme éducatif
Le protocole HTTP, développé au début des années 90 du siècle dernier, est entré si étroitement dans notre vie quotidienne qu’il est difficile d’imaginer au moins une journée sans l’utiliser. Cependant, en soi, il n'offre même pas un niveau de sécurité minimum lors de l'échange d'informations entre un utilisateur et un serveur Web. Le HTTPS («S» - sécurisé) vient à la rescousse: en utilisant le conditionnement des données transmises en SSL / TLS, ce protocole a fait ses preuves dans la protection des informations contre l'interception et est activement promu par l'industrie.Par exemple, Google a adhéré à 2014Position "HTTPS partout" et diminue même la priorité des sites sans elle dans les résultats de recherche. Cette «propagande» ne contourne pas non plus les consommateurs ordinaires: les navigateurs modernes avertissent leurs utilisateurs de la présence et de l'exactitude des certificats SSL pour les sites visités.

Le coût d'un certificat pour un site personnel commence à partir de dizaines de dollars. L'acheter n'est pas toujours justifié et approprié. Heureusement, depuis fin 2015, une alternative gratuite sous forme de certificats Let's Encrypt (LE) est disponible . Ce projet à but non lucratif a été créé par des passionnés de Mozilla afin de couvrir la plupart des sites Web avec cryptage.L'autorité de certification émet des certificats validés par domaine(le plus simple parmi ceux disponibles sur le marché) avec une période de validité de 90 jours, et depuis de nombreuses années, il est possible de délivrer le certificat dit générique pour plusieurs sous-domaines.Pour obtenir le certificat, le site utilise les algorithmes décrits dans le protocole ACME ( Automated Certificate Management Environment ), créé spécifiquement pour Let's Encrypt. Lors de son utilisation, la confirmation de la propriété du domaine est effectuée par des demandes via le placement d'un code HTTP spécifique (appelé HTTP-01 ) ou l'installation d'enregistrements DNS (DNS-01) - plus d'informations à leur sujet seront données ci-dessous.Responsable Certification
Cert-manager est un projet spécial pour Kubernetes, qui est un ensemble de CustomResourceDefinitions (d'où la limitation de la version minimale prise en charge de K8s - v1.12) pour la configuration CA (autorités de certification) et la commande directe de certificats. L'installation de CRD dans un cluster est triviale et revient à utiliser un seul fichier YAML:kubectl create ns cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.13.0/cert-manager.yaml
( Il y a aussi la possibilité d'installer à l'aide de Helm.)Pour lancer la procédure de commande, les ressources des autorités de certification (CA) doivent être déclarées dans le cluster: Issuer
ou ClusterIssuer
, qui sont utilisées pour signer le CSR (demandes d'émission de certificats). La différence entre la première ressource et la seconde est dans la portée:Issuer
peut être utilisé dans le même espace de noms,ClusterIssuer
est un objet cluster global.
Pratique avec cert-manager
N ° 1. Certificat auto-signé
Commençons par le cas le plus simple - commander un certificat auto-signé. Cette option est assez courante, par exemple, pour les environnements de test dynamiques pour les développeurs ou dans le cas de l'utilisation d'un équilibreur externe qui met fin au trafic SSL.La ressource Issuer
ressemblera à ceci:apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: selfsigned
spec:
selfSigned: {}
Et pour émettre un certificat, il est nécessaire de décrire la ressource Certificate
, où il est indiqué comment l'émettre (voir la section issuerRef
ci - dessous) et où se trouve la clé privée (champ secretName
). Après cela, Ingress devra se référer à cette clé (voir la section tls
c spec
):---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: selfsigned-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: selfsigned
commonName: "yet-another.website"
dnsNames:
- "yet-another.website"
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: app
spec:
tls:
- hosts:
- "yet-another.website"
secretName: tls-secret
rules:
- host: "yet-another.website"
http:
paths:
- path: /
backend:
serviceName: app
servicePort: 8080
Quelques secondes après avoir ajouté ces ressources au cluster, le certificat sera émis. Vous pouvez voir le rapport correspondant dans la sortie de la commande:kubectl -n app describe certificate selfsigned-crt
...
Normal GeneratedKey 5s cert-manager Generated a new private key
Normal Requested 5s cert-manager Created new CertificateRequest resource "selfsigned-crt-4198958557"
Normal Issued 5s cert-manager Certificate issued successfully
Si vous regardez la ressource secrète elle-même, elle contient:- clé privée
tls.key
, - certificat racine
ca.crt
, - Notre certificat auto-signé
tls.crt
.
Le contenu de ces fichiers peut être vu en utilisant l'utilitaire openssl
, par exemple, comme ceci:kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:01:59 2020 GMT
notAfter=May 10 21:01:59 2020 GMT
issuer=O = cert-manager, CN = yet-another.website
Il convient de noter que, dans le cas général, le certificat émis à l'aide de celui- ci ne sera pas approuvé par lesIssuer
clients connectés . La raison est simple: elle n'a pas de CA (voir la note sur le site du projet ) . Pour éviter cela, vous devez spécifier dans le chemin d'accès au fichier secret où il se trouve . Il peut s'agir d'une organisation d'autorité de certification d'entreprise - pour signer les certificats émis pour Ingress avec une clé déjà utilisée pour les besoins d'autres services de serveur / systèmes d'information.Certificate
ca.crt
N ° 2. Cryptons le certificat avec la validation HTTP
Pour émettre des certificats LE, comme mentionné précédemment, deux types de confirmation de propriété de domaine sont disponibles : HTTP-01 et DNS-01.La première approche (HTTP-01) consiste à lancer un petit serveur Web avec un déploiement séparé, qui enverra à Internet via le lien <YOUR_DOMAIN > /. Bien connu / acme-challenge / <TOKEN> certaines données demandées au serveur de certification. Par conséquent, cette méthode implique la disponibilité d'Ingress de l'extérieur sur le port 80 et la résolution de l'enregistrement DNS du domaine en adresses IP publiques.La deuxième option pour vérifier le certificat émis (DNS-01) provient ded'avoir une API pour le serveur DNS hébergeant les enregistrements de domaine. L'émetteur, à l'aide des jetons indiqués, crée des enregistrements TXT sur le domaine, que le serveur ACME reçoit ensuite lors de la confirmation. Parmi les fournisseurs DNS officiellement pris en charge figurent CloudFlare, AWS Route53, Google CloudDNS et d'autres, y compris sa propre implémentation ( acme-dns ).Remarque : Let's Encrypt a des limites assez strictes sur les demandes aux serveurs ACME. Afin de ne pas entrer dans une longue interdiction, il est recommandé d'utiliser le type de certificat letsencrypt-staging pour le débogage (la différence ne concerne que le serveur ACME).Nous décrivons donc les ressources:apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: le-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: letsencrypt
commonName: yet-another.website
dnsNames:
- yet-another.website
Notez que l' adresse du serveur de transfert est spécifiée comme server
y acme
(c Issuer
) . Le remplacer par une bataille sera possible plus tard. En appliquant cette configuration, nous suivons l'intégralité du chemin de commande:- La création a
Certificate
engendré une nouvelle ressource CertificateRequest
:
kubectl -n app describe certificate le-crt
...
Created new CertificateRequest resource "le-crt-1127528680"
- Dans sa description - une marque sur la création
Order
:
kubectl -n app describe certificaterequests le-crt-1127528680
…
Created Order resource app/le-crt-1127528680-1805948596
- Il
Order
décrit avec quels paramètres le test réussit et quel est son état actuel. Cette vérification est effectuée par la ressource Challenge
:
kubectl -n app describe order le-crt-1127528680-1805948596
…
Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
- Enfin, les détails de cette ressource contiennent des informations sur l'état de l'analyse elle-même:
kubectl -n app describe challenges le-crt-1127528680-1805948596-1231544594
...
Reason: Successfully authorized domain
...
Normal Started 2m45s cert-manager Challenge scheduled for processing
Normal Presented 2m45s cert-manager Presented challenge using http-01 challenge mechanism
Normal DomainVerified 2m22s cert-manager Domain "yet-another.website" verified with "http-01" validation
Si toutes les conditions sont remplies (c'est-à-dire que le domaine est accessible de l'extérieur, il n'y a pas d'interdiction du LE ...) - en moins d'une minute, le certificat sera émis. En cas de succès, un describe certificate le-crt
enregistrement apparaîtra dans la sortie Certificate issued successfully
.Vous pouvez maintenant modifier en toute sécurité l'adresse du serveur pour combattre ( https://acme-v02.api.letsencrypt.org/directory
) et réorganiser les vrais certificats déjà signés non Fake LE Intermediate X1
, mais Let's Encrypt Authority X3
.Pour ce faire, vous devez d'abord supprimer la ressource Certificate
: sinon, aucune procédure de commande n'est activée, car le certificat existe déjà et il est pertinent. La suppression d'un secret entraînera son retour immédiat avec un message à describe certificate
: Normal PrivateKeyLost 44s cert-manager Lost private key for CertificateRequest "le-crt-613810377", deleting old resource
Il reste à appliquer le manifeste «combat» à Issuer
celui déjà décrit ci-dessus Certificate
(il n'a pas changé):apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
Après avoir reçu le message Certificate issued successfully
, describe
vérifiez-le:kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:11:48 2020 GMT
notAfter=May 10 21:11:48 2020 GMT
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
Numéro 3. Wildcard LE avec validation DNS
Nous compliquerons la tâche en écrivant un certificat immédiatement à tous les sous-domaines du site et en utilisant cette fois la vérification DNS (via CloudFlare).Tout d'abord, récupérez le jeton dans le panneau de configuration CloudFlare pour travailler via l'API:- Profil → Jetons d'API → Créer un jeton.
- Définissez les autorisations comme suit:
- Autorisations:
- Zone - DNS - Modifier
- Zone - Zone - Lire
- Ressources de zone:
- Inclure - Toutes les zones
- Copiez le jeton reçu après l'enregistrement (par exemple :)
y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
.
Créez un secret dans lequel ce jeton sera stocké et faites-y référence dans l'émetteur:apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token
type: Opaque
stringData:
api-token: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- dns01:
cloudflare:
email: my-cloudflare-acc@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: le-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: letsencrypt
commonName: yet-another.website
dnsNames:
- "yet-another.website"
- "*.yet-another.website"
(N'oubliez pas d'utiliser la mise en scène si vous expérimentez!)Passons en revue le processus de confirmation de la propriété du domaine:kubectl -n app describe challenges.acme.cert-manager.io le-crt-613810377-1285319347-3806582233
...
Status:
Presented: true
Processing: true
Reason: Waiting for dns-01 challenge propagation: DNS record for "yet-another.website" not yet propagated
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 54s cert-manager Challenge scheduled for processing
Normal Presented 53s cert-manager Presented challenge using dns-01 challenge mechanism
Un enregistrement TXT apparaîtra dans le panneau:
... et après un certain temps, le statut passera à:Domain "yet-another.website" verified with "dns-01" validation
Assurez-vous que le certificat est valide pour n'importe quel sous-domaine:kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -text |grep DNS:
DNS:*.yet-another.website, DNS:yet-another.website
La validation par DNS, en règle générale, ne se fait pas rapidement, car la plupart des fournisseurs DNS ont une période de mise à jour des données qui montre combien de temps s'écoule entre le moment où l'enregistrement DNS est modifié et la mise à jour réelle de tous les serveurs DNS du fournisseur. Cependant, la norme ACME fournit également une combinaison de deux options de vérification, qui peuvent être utilisées pour accélérer la réception d'un certificat pour le domaine principal. Dans ce cas, la description Issuer
sera la suivante:apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https:
privateKeySecretRef:
name: letsencrypt
solvers:
- selector:
dnsNames:
- "*.yet-another.website"
dns01:
cloudflare:
email: my-cloudflare-acc@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
- selector:
dnsNames:
- "yet-another.website"
http01:
ingress:
class: nginx
Si vous appliquez cette configuration, deux ressources seront créées Challenge
:kubectl -n app describe orders le-crt-613810377-1285319347
…
Normal Created 3m29s cert-manager Created Challenge resource "le-crt-613810377-1285319347-3996324737" for domain "yet-another.website"
Normal Created 3m29s cert-manager Created Challenge resource "le-crt-613810377-1285319347-1443470517" for domain "yet-another.website"
Numéro 4. Utilisation d'annotations spéciales Ingress
En plus du chemin d'accès direct à la création de certificats dans cert-manager, il est possible d'utiliser un composant appelé ingress-shim et de ne pas créer explicitement de ressources Certificate
. L'idée est qu'à l'aide d'annotations Ingress spéciales, le certificat sera automatiquement commandé en utilisant celui qui y est indiqué Issuer
. Le résultat est à peu près la ressource Ingress suivante:apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- "yet-another.website"
secretName: tls-secret
rules:
- host: "yet-another.website"
http:
paths:
- path: /
backend:
serviceName: app
servicePort: 8080
Pour un fonctionnement correct, seule la présence de l'émetteur suffit ici, c'est-à-dire la création d'une entité de moins.De plus, il existe une annotation obsolète kube-lego - kubernetes.io/tls-acme: "true"
, - qui nécessite une tâche Issuer
par défaut lors de l'installation de cert-manager à l'aide des paramètres de Helm (ou dans les options de lancement du conteneur de gestionnaire).Nous, dans l'entreprise, n'utilisons pas ces options et ne pouvons pas les conseiller en raison de l'opacité des approches utilisées pour commander des certificats SSL (et en même temps de divers problèmes qui surviennent ), mais nous avons tout de même décidé de mentionner dans l'article pour une image plus complète.Au lieu d'une conclusion
Grâce à de simples manipulations avec CRD, nous avons appris à écrire des certificats SSL auto-renouvelables, auto-signés et gratuits à partir du projet Let's Encrypt pour les domaines de sites Web lancés dans le cadre des clusters Ingresss in Kubernetes.L'article fournit des exemples de résolution des problèmes les plus courants dans notre pratique. Cependant, les fonctions cert-manager ne sont pas limitées aux fonctionnalités décrites ci-dessus. Sur le site Web de l'utilitaire, vous pouvez trouver des exemples de travail avec d'autres services - par exemple, un bundle avec Vault ou l' utilisation de centres d'émission externes (émetteurs).PS
Lisez aussi dans notre blog: