Introduction
L'Ingress expose les applications vers l'extérieur. Une petite erreur de configuration peut divulguer des en-têtes, accepter des chiffrements faibles, contourner l'authentification ou surexposer des chemins réseau. Ce guide montre comment durcir Kubernetes Ingress avec des étapes pratiques, vérifiables et des procédures de restauration claires. Vous allez :
- Inventorier les versions et points d'exposition pour que les changements soient compatibles et testables.
- Appliquer un chemin de configuration sûr et limité pour TLS, mTLS, listes d'autorisation IP, limitation de débit, secrets, RBAC, NetworkPolicy et contrôles de load balancer.
- Vérifier les protections avec des commandes concrètes et résultats attendus.
- Reconnaître les modes de défaillance et récupérer rapidement.
- Maintenir une liste de contrôle opérationnelle exécutable régulièrement.
Les exemples construits sont étiquetés comme tels. Adaptez noms, espaces de noms et limites à votre environnement.
Inventaire des versions et de l'environnement
Avant tout changement, enregistrez ce que vous durcissez. Les versions incompatibles ou topologies ambiguës causent la plupart des surprises.
Prérequis :
- Vous avez les droits d'administrateur de cluster ou délégués pour créer/modifier Ingress, Secrets, RBAC et NetworkPolicy dans les espaces de noms cibles.
- Vous connaissez le contrôleur Ingress utilisé (par exemple, NGINX Ingress Controller) et son espace de noms.
- Vous contrôlez ou pouvez mettre à jour le DNS pour vos noms d'hôtes.
- Vous avez ou pouvez émettre des certificats TLS valides pour vos domaines.
Exemple d'inventaire construit :
| Élément | Valeur exemple | Importance |
|---|---|---|
| Version Kubernetes | 1.28 (exemple construit) | Détermine la disponibilité des API (ex. ValidatingAdmissionPolicy). |
| Contrôleur Ingress | ingress-nginx 1.9.x (exemple construit) | Définit les annotations et clés ConfigMap supportées. |
| Nom IngressClass | public-nginx (exemple construit) | Liaison explicite évite le routage par défaut accidentel. |
| Zone DNS | example.test (exemple construit) | Contrôle le routage hôte et les SAN TLS. |
| Load balancer | Cloud L4 avec plages sources (exemple construit) | Restriction des IP entrantes au niveau LB. |
| Source TLS | CA interne et CA public (exemple construit) | Choix des racines mTLS et certificats serveur. |
Commandes pour capturer l'état actuel :
kubectl get ns
kubectl get deploy,ds,svc,cm -n ingress-nginx
kubectl get ingressclass
kubectl get ingress -A -o wide
kubectl version --short
Chemin de configuration sûr
Commencez par une cible étroite à haute valeur que vous pouvez tester et restaurer rapidement (par exemple, un nom d'hôte). Puis étendez.
1) Lier à un IngressClass explicite
Assurez-vous que chaque ressource Ingress cible un contrôleur connu. Cela empêche des défauts inconnus de capter le trafic.
Exemple d'IngressClass construit :
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: public-nginx
spec:
controller: k8s.io/ingress-nginx
2) Imposer TLS et défauts sécurisés
Utilisez un secret TLS Kubernetes, redirigez HTTP vers HTTPS et activez le transport strict (HSTS). L'exemple suivant montre des annotations sécurisées pour NGINX Ingress Controller.
Créer un secret TLS (exemple construit) :
kubectl create secret tls web-tls \
--namespace app \
--key server.key \
--cert server.crt
Appliquer un Ingress avec annotations sécurisées (exemple construit) :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
namespace: app
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3"
nginx.ingress.kubernetes.io/hsts: "true"
nginx.ingress.kubernetes.io/hsts-max-age: "63072000"
nginx.ingress.kubernetes.io/hsts-include-subdomains: "true"
nginx.ingress.kubernetes.io/hsts-preload: "true"
nginx.ingress.kubernetes.io/server-tokens: "false"
nginx.ingress.kubernetes.io/proxy-body-size: "1m"
# Optionnel : affiner les chiffrements si ensemble client contrôlé
# nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"
labels:
security-tier: hardened
spec:
ingressClassName: public-nginx
tls:
- hosts:
- app.example.test
secretName: web-tls
rules:
- host: app.example.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
Les défauts au niveau contrôleur peuvent être fixés dans le ConfigMap du contrôleur (exemple construit) :
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
server-tokens: "false"
ssl-protocols: "TLSv1.2 TLSv1.3"
# Garder les chiffrements concis ; éviter suites obsolètes
# ssl-ciphers: "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"
3) Ajouter l'authentification par certificat client (mTLS) quand approprié
Pour portails d'administration ou API internes, exigez les certificats clients pour atteindre l'application. Stockez votre bundle CA dans un secret. Exemple (construit) :
kubectl -n app create secret generic client-ca --from-file=ca.crt=ca.pem
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: admin
namespace: app
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/auth-tls-secret: "app/client-ca"
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1"
# Optionnel : transmettre détails cert client à l'amont
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
nginx.ingress.kubernetes.io/whitelist-source-range: "203.0.113.0/24,198.51.100.0/24"
labels:
security-tier: hardened
spec:
ingressClassName: public-nginx
tls:
- hosts: ["admin.example.test" ]
secretName: web-tls
rules:
- host: admin.example.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-svc
port:
number: 8443
4) Ajouter listes d'autorisation IP et limitation de débit
Contraindre les sources de trafic et limiter les abus.
Exemple d'annotations construites :
metadata:
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "203.0.113.0/24,198.51.100.0/24"
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-burst: "20"
Configurer le Service du contrôleur Ingress pour préserver les IP clients afin que la limitation et les listes évaluent les vraies adresses :
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
type: LoadBalancer
externalTrafficPolicy: Local
loadBalancerSourceRanges:
- 0.0.0.0/0 # resserrer pour endpoints admin, ex. CIDR d'entreprise
5) Sécuriser les Secrets et limiter les permissions
- Gardez les secrets TLS dans le même espace de noms que l'Ingress qui les consomme.
- Restreignez qui peut créer ou modifier Ingress et Secrets.
Exemple RBAC limitant les changements Ingress à un groupe spécifique dans l'espace de noms app :
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ingress-maintainer
namespace: app
rules:
- apiGroups: ["networking.k8s.io" ]
resources: ["ingresses" ]
verbs: ["get","list","watch","create","update","patch" ]
- apiGroups: ["" ]
resources: ["secrets" ]
resourceNames: ["web-tls", "client-ca" ]
verbs: ["get","list","watch","update","patch" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ingress-maintainer-binding
namespace: app
subjects:
- kind: Group
name: app-team # identité exemple construite
roleRef:
kind: Role
name: ingress-maintainer
apiGroup: rbac.authorization.k8s.io
Le contrôle d'admission peut imposer des standards cluster-wide. Exemple construit exigeant TLS et IngressClass spécifique :
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: enforce-ingress-tls-and-class
spec:
matchConstraints:
resourceRules:
- apiGroups: ["networking.k8s.io" ]
apiVersions: ["v1" ]
operations: ["CREATE","UPDATE" ]
resources: ["ingresses" ]
validations:
- expression: "has(object.spec.tls) && size(object.spec.tls) > 0"
message: "Ingress must configure TLS."
- expression: "object.spec.ingressClassName == 'public-nginx'"
message: "Ingress must target approved ingressClassName."
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: enforce-ingress-tls-and-class-binding
spec:
policyName: enforce-ingress-tls-and-class
validationActions: ["Deny" ]
6) Contraindre les chemins réseau
Assurez-vous que le contrôleur atteint seulement les backends nécessaires, et que les pairs n'atteignent pas le contrôleur inopinément.
Exemple NetworkPolicy permettant seulement au contrôleur d'ingress d'appeler votre service :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-only-ingress
namespace: app
spec:
podSelector:
matchLabels:
app: web
policyTypes: ["Ingress" ]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 80
Vérification et diagnostics
Utilisez les vérifications suivantes après chaque changement. Vérifiez sur un seul nom d'hôte d'abord.
Référence rapide des commandes de vérification et signaux attendus (résultats construits) :
| Vérification | Commande | Attendu |
|---|---|---|
| Ingress admis | kubectl describe ingress -n app web | Affiche IngressClass public-nginx ; règles et tls peuplés. |
| Adresse allouée | kubectl get ingress -n app web -o wide | ADDRESS peuplé avec IP/hostname LB. |
| HTTP redirige vers HTTPS | curl -I http://app.example.test | 301/308 avec Location: https://app.example.test/ |
| Version TLS minimale | openssl s_client -connect app.example.test:443 -tls1_0 | Échec handshake ; vérifier TLSv1.2 réussit. |
| HSTS présent | curl -I https://app.example.test | En-tête Strict-Transport-Security présent. |
| Application mTLS | curl -I https://admin.example.test | 400/401/403 sans cert client ; 200 avec cert client. |
| Limitation de débit | for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" https://app.example.test; done | Quelques 429 sous RPS serré. |
Étapes détaillées :
- Admission et liaison de classe
kubectl describe ingress -n app web
Attendu : Annotations affichées, IngressClassName: public-nginx, Événements sans messages Deny du ValidatingAdmissionPolicy.
- Redirection TLS et en-têtes
curl -I http://app.example.test
curl -I https://app.example.test
Attendu : HTTP retourne 301/308 vers https. HTTPS retourne 200 et inclut Strict-Transport-Security. Pas de fuite Server: nginx version quand server-tokens est false.
- Plancher de protocole (TLSv1.2+)
# Attendu échec avec protocoles obsolètes
openssl s_client -connect app.example.test:443 -tls1_0 </dev/null
# Attendu succès avec TLSv1.2
openssl s_client -connect app.example.test:443 -tls1_2 </dev/null | grep -i "protocol\|cipher"
Attendu : TLS 1.0 échoue à négocier ; TLS 1.2 réussit et montre un chiffrement moderne.
- Application mTLS
Sans certificat client :
curl -I https://admin.example.test
Attendu : 400/401/403 selon configuration.
Avec certificat client (fichiers exemple construits) :
curl -I https://admin.example.test \
--cert client.crt --key client.key
Attendu : 200 et en-têtes de réponse amont.
- Limitation de débit et listes d'autorisation
Depuis un réseau autorisé, envoyez une rafale :
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" https://app.example.test; done | sort | uniq -c
Attendu : Vous verrez 200 et possiblement 429 selon valeurs RPS et burst.
Depuis un réseau bloqué (ou en resserrant temporairement whitelist), attendez 403.
- Journaux du contrôleur et des requêtes
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller | tail -n 50
Cherchez messages de handshake TLS, avis de limitation de débit, ou échecs cert client. Erreurs répétées suggèrent une erreur de configuration.
- Vérifications NetworkPolicy
Depuis un autre espace de noms, tentez d'atteindre le pod service. Si vous utilisez un pod de test temporaire, il devrait échouer quand NetworkPolicy est en place. Exemple construit :
kubectl run -n default tmp --image=alpine --restart=Never --rm -it -- sh -c "apk add --no-cache curl >/dev/null && curl -Is http://web.app.svc.cluster.local:80 || true"
Attendu : Timeout ou refus de connexion quand la politique bloque le trafic.
Modes de défaillance et restauration
Ce qui casse souvent et comment récupérer vite.
- Problèmes de secret TLS
- Symptôme : échecs TLS style 526/525 au bord, ou journaux contrôleur montrent aucun certificat valide.
- Vérification : Le type de secret doit être kubernetes.io/tls et correspondre à Ingress tls.secretName.
- Correction :
kubectl -n app get secret web-tls -o yaml | grep type
kubectl -n app delete secret web-tls
kubectl -n app create secret tls web-tls --key server.key --cert server.crt
kubectl -n app rollout restart deploy/ingress-nginx-controller # si nécessaire
- HTTP ne redirige pas vers HTTPS
- Symptôme : HTTP 200 sur port 80.
- Vérification : Annotation ssl-redirect à "true" au niveau Ingress ou contrôleur.
- Correction : Ajoutez l'annotation et réappliquez. Vérifiez avec curl -I.
- TLS 1.0/1.1 encore acceptés
- Symptôme : openssl -tls1_0 réussit.
- Vérification : ssl-protocols dans annotations/ConfigMap.
- Correction : Mettez ssl-protocols à "TLSv1.2 TLSv1.3\) ; redémarrez contrôleur si requis.
- mTLS bloque utilisateurs valides
- Symptôme : Tout le monde reçoit 400/401/403.
- Vérifications : Nom secret bundle CA et clé fichier, verify-depth, et si clients envoient un cert.
- Restauration rapide : Retirez annotations auth-tls-* de l'Ingress et réappliquez. Gardez manifeste de sauvegarde pour restauration rapide.
- Limitation de débit agressive
- Symptôme : Beaucoup de 429 pendant charge normale.
- Correction : Augmentez limit-rps et limit-burst ou retirez temporairement. Vérifiez avec boucle test rafale.
- Liste d'autorisation IP trop restrictive
- Symptôme : 403 pour utilisateurs attendus.
- Correction : Ajoutez plages CIDR et réappliquez. Préférez tester changements whitelist sur hôte staging avant production.
- NetworkPolicy bloque backends
- Symptôme : 502/504 du contrôleur ; journaux montrent échecs connexion amont.
- Vérifications : Sélecteurs namespace et pod labels dans politique, numéros ports cibles.
- Correction : Ajustez sélecteurs ou ports ; retestez connectivité depuis namespace contrôleur.
- Plages sources load balancer bloquent vérifications santé
- Symptôme : LB ne signale jamais sain ; Ingress ADDRESS reste en attente ou trafic trou noir.
- Correction : Incluez plages IP sondes santé du fournisseur dans loadBalancerSourceRanges. Confirmez avec docs fournisseur. Test rapide : élargissez temporairement à 0.0.0.0/0 pour confirmer diagnostic, puis resserrez.
Conseils de restauration :
- Exportez toujours le manifeste actuel avant changements :
kubectl -n app get ingress web -o yaml > web-backup-$(date +%Y%m%d%H%M%S).yaml
- Si un changement casse le trafic, réappliquez immédiatement la sauvegarde :
kubectl apply -f web-backup-20240101123456.yaml
- Pour changements cluster-wide (ConfigMap, Service), mettez à jour en staging d'abord. Si production impactée, revenez à version précédente ConfigMap/Service et redémarrez déploiement contrôleur pour reprendre réglages restaurés.
Vérification et restauration expiration certificat (exemple construit) :
# Décoder et vérifier date Not After
kubectl -n app get secret web-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -enddate
# Renouveler en remplaçant secret, puis reverifier
kubectl -n app create secret tls web-tls --key new.key --cert new.crt --dry-run=client -o yaml | kubectl apply -f -
Liste de contrôle opérationnelle
Utilisez cette liste lors revues régulières et avant/après changements.
- Inventaire
- Confirmez versions Kubernetes, contrôleur, et IngressClass.
- Listez tous noms d'hôtes exposés Internet et leurs espaces de noms.
- TLS et en-têtes
- Tous hôtes publics imposent HTTPS avec HSTS ; TLSv1.2+ seulement.
- Secrets sont type kubernetes.io/tls et non partagés entre espaces de noms non liés.
- Pas de fuite version serveur (server-tokens false).
- Contrôle d'accès
- mTLS requis pour endpoints admin ou internes.
- Listes d'autorisation IP présentes pour services restreints.
- Limites de débit ajustées au trafic normal avec marge.
- Permissions et politiques
- RBAC accorde seulement équipes spécifiques droit de modifier Ingress et secrets liés.
- ValidatingAdmissionPolicy impose TLS et IngressClass approuvée.
- NetworkPolicies permettent seulement au contrôleur d'atteindre services backend.
- Load balancer
- externalTrafficPolicy=Local quand IP clients nécessaires.
- loadBalancerSourceRanges restreint IP entrantes quand faisable.
- Vérification
- Contrôles curl et openssl passent pour chaque hôte.
- Journaux contrôleur sans erreurs TLS ou amont répétées.
- Préparation restauration
- Manifestes exportés avant changements ; sauvegardes connues bonnes existent.
- Étapes restauration documentées et testées sur hôte staging.
Conclusion
Vous avez durci Kubernetes Ingress en liant explicitement à un IngressClass, imposant TLS et HSTS, ajoutant mTLS quand nécessaire, contraignant le trafic avec listes d'autorisation IP et limitation de débit, verrouillant secrets et RBAC, appliquant NetworkPolicy, et resserrant l'exposition du load balancer. Vous avez vérifié chaque contrôle avec des vérifications observables et préparé procédures de restauration pour récupération rapide. Ensuite, étendez d'un nom d'hôte durci à tous services Internet-facing, en gardant changements étroits, testables et réversibles. En itérant avec un plan clair et des vérifications mesurables, vous réduisez la retouche et gardez vos points d'entrée sécurisés.