Dans une architecture Kubernetes, la gestion manuelle des Secret tls est une source majeure de failles de sécurité et de pannes. cert-manager s’impose comme l’opérateur de référence pour automatiser l’émission, la validation et le renouvellement des certificats (via Let’s Encrypt, HashiCorp Vault ou une PKI interne d’entreprise).
1. Les CRD Clés de cert-manager
Avant le déploiement, il est essentiel de comprendre la dynamique entre les ressources personnalisées (CRDs) introduites par cert-manager :
- Issuer / ClusterIssuer : Définit l’autorité de certification (CA) qui signera les certificats. L’
Issuerfonctionne au niveau d’un seul namespace, tandis que leClusterIssuerest disponible à l’échelle de tout le cluster (idéal pour les environnements de production multi-tenant). - Certificate : Une ressource déclarative décrivant le certificat souhaité (noms de domaine, durée de validité, algorithme de clé, secret cible).
- Secret : Le secret Kubernetes natif généré automatiquement par cert-manager pour stocker la clé privée et le certificat au format
tls.crt/tls.key.
2. Étape 1 : Installation de cert-manager via Helm
L’installation recommandée se fait via Helm 3. Elle inclut l’injection automatique des CRDs.
Bash
# 1. Ajouter le dépôt Helm de Jetstack
helm repo add jetstack https://charts.jetstack.io
helm repo update
# 2. Installer cert-manager dans un namespace dédié avec les CRDs
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true
# 3. Vérifier que les pods sont prêts (Status: Running)
kubectl get pods --namespace cert-manager
3. Étape 2 : Configuration du ClusterIssuer
Voici deux cas d’usage industriels : le challenge ACME HTTP-01 (exposé à Internet) et l’intégration avec une PKI interne d’entreprise (Vault).
Cas A : ACME Let’s Encrypt (Production – HTTP-01)
Ce manifeste configure un émetteur global utilisant l’environnement de production de Let’s Encrypt.
YAML
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Adresse email pour les notifications d'expiration d'urgence
email: secops@aminejafjaf.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret Kubernetes qui stockera la clé privée du compte ACME
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: nginx
Cas B : PKI d’Entreprise / HashiCorp Vault (Environnement Bancaire / Réseau Privé)
Pour les infrastructures privées ou sans accès direct à l’Internet public, cert-manager s’interface nativement avec Vault.
YAML
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vault-internal-issuer
spec:
vault:
server: "https://vault.internal.net:8200"
path: "pki_internal/sign/banking-role"
auth:
tokenSecretRef:
name: vault-token
key: token
Appliquez votre configuration :
Bash
kubectl apply -f cluster-issuer.yaml
4. Étape 3 : Automatisation des certificats pour les applications
Il existe deux méthodes pour demander et attacher un certificat : automatique via l’Ingress Controller ou déclarative via le manifeste Certificate.
Méthode 1 : Automatisation directe via l’Ingress (Recommandée pour le Web)
Il suffit d’ajouter une annotation sur votre Ingress natif. cert-manager interceptera la ressource et créera automatiquement le certificat.
YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-banking-ingress
namespace: production
annotations:
# Déclenche cert-manager et spécifie le ClusterIssuer à utiliser
cert-manager.io/cluster-issuer: "letsencrypt-prod"
# Optionnel: force le rechargement sans coupure Nginx
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.aminejafjaf.com
secretName: api-banking-tls-secret # Le secret sera créé par cert-manager
rules:
- host: api.aminejafjaf.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: banking-core-service
port:
number: 8080
Méthode 2 : Déclaration explicite avec le CRD Certificate
Utile pour sécuriser du trafic inter-services (mTLS) ou exporter un certificat pour un Load Balancer externe.
YAML
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-secops-cert
namespace: production
spec:
secretName: internal-secops-tls
duration: 2160h # 90 jours
renewBefore: 360h # Renouvellement automatique 15 jours avant expiration
subject:
organizations:
- Amine Jafjaf SecOps
isCA: false
privateKey:
algorithm: RSA
encoding: PKCS1
size: 4096
dnsNames:
- secops.internal.local
- *.secops.internal.local
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
5. Inspection et Diagnostic (Troubleshooting)
En tant qu’ingénieur SecOps, la validation de la chaîne d’émission et l’audit des événements sont cruciaux.
Bash
# 1. Lister les certificats et vérifier l'état READY (True)
kubectl get certificate -n production
# 2. Inspecter les étapes d'émission d'un certificat
kubectl describe certificate api-banking-tls-secret -n production
# 3. Inspecter les requêtes de certificat sous-jacentes (CertificateRequest)
kubectl get certificaterequest -n production
# 4. Examiner les challenges ACME en cours (en cas de blocage HTTP-01/DNS-01)
kubectl get challenges -n production
6. Bonnes Pratiques SecOps pour la Production
- Privilégier le Challenge DNS-01 pour la sécurité maximale : Le challenge HTTP-01 nécessite d’ouvrir le port 80 sur l’extérieur. Dans des environnements bancaires stricts, préférez le challenge DNS-01 (via Cloudflare, Route53 ou Infoblox) pour émettre des certificats Wildcard (
*.domaine.com) sans exposer vos pods au web public. - Rotation des clés privées : Activez la rotation automatique des clés privées à chaque renouvellement dans cert-manager en ajoutant
rotationPolicy: Alwaysdans la spécification de votreCertificate. - Supervision Prometheus / Grafana : Exposez les métriques natives de cert-manager pour surveiller la métrique
certmanager_certificate_expiration_timestamp_secondset créer des alertes Slack/PagerDuty en cas d’échec de renouvellement à J-7.