Aller au contenu

Lab : cloisonner et exposer Signalements

Prérequis

Testé avec kubectl 1.36.0 kubernetes 1.36 , vérifié le 5 octobre 2026

Ce lab rassemble le cours Kubernetes : réseau et exposition. Vous partez du déploiement de Signalements du lab du cours précédent, sur un cluster kind dont le réseau est confié à Cilium, puis vous l'exposez en HTTPS et vous cloisonnez son namespace. Un script vérifie le résultat en tentant réellement des connexions autorisées et interdites. Il valide le niveau 200 de la compétence « Kubernetes : réseau et exposition ».

Comptez une demi-journée.

Ce qu'il faut

  • Un cluster kind formation créé sans plugin réseau par défaut, avec Cilium installé comme à la leçon 2. Le plugin par défaut de kind (kindnet) applique lui aussi les NetworkPolicies depuis ses versions récentes, mais le lab suit le cours, et Cilium permet d'observer les refus avec Hubble.
  • Les CRD de Gateway API et Traefik comme implémentation (leçon 6), cert-manager (leçon 7).
  • Le déploiement de Signalements du lab précédent : namespace signalements, Deployment et Service signalements (port 80), base PostgreSQL de formation.

Sur un cluster local, Let's Encrypt ne peut pas valider votre domaine : le certificat est émis par une autorité de certification locale gérée par cert-manager (un ClusterIssuer de type selfSigned qui signe une autorité, puis un Issuer de type ca). Le mécanisme côté Gateway est identique à celui de la production.

Les exigences

ExigenceLeçon
La Gateway signalements a un écouteur HTTPS dont le certificat est un Secret produit par cert-manager ; la Gateway est Programmed6, 7
Le Certificate correspondant est Ready7
Une HTTPRoute envoie signalements.example.com vers le Service signalements, et elle est Accepted avec des références résolues6
L'écouteur HTTP redirige vers HTTPS (filtre RequestRedirect)6
Le namespace signalements a une politique de refus par défaut, en entrée comme en sortie8
Les pods du namespace peuvent résoudre des noms (DNS vers CoreDNS autorisé)5, 8
Seule la passerelle peut joindre l'application sur son port ; seule l'application peut joindre la base sur 54328
Un pod d'un autre namespace ne joint ni l'application ni la base8

Vérifier

Enregistrez le script sous verifier-lab.sh et lancez-le avec le kubeconfig du laboratoire. Pour les tests de connexion, il crée des pods éphémères et les supprime ; il ne modifie rien d'autre.

#!/usr/bin/env bash
# Vérifie les propriétés attendues du lab « cloisonner et exposer Signalements ».
# Usage : KUBECONFIG=<kubeconfig du laboratoire> bash verifier-lab.sh   (exige kubectl et jq)
# Chaque contrôle affiche OK ou ÉCHEC ; le code de sortie est le nombre d'échecs.
set -uo pipefail
NS=signalements
echecs=0

controle() {   # controle "libellé" commande...
  local libelle=$1; shift
  if "$@" > /dev/null 2>&1; then
    echo "OK     $libelle"
  else
    echo "ÉCHEC  $libelle"
    echecs=$((echecs + 1))
  fi
}

condition() {   # condition objet type : vrai si .status.conditions contient type=True
  kubectl -n "$NS" get "$1" -o json | jq -e --arg t "$2" '[.status.conditions[]? | select(.type == $t and .status == "True")] | length > 0'
}

gw=$(kubectl -n "$NS" get gateway signalements -o json 2>/dev/null)
controle "la Gateway signalements existe" test -n "$gw"
controle "la Gateway est Programmed" condition gateway/signalements Programmed
controle "la Gateway a un écouteur HTTPS avec un certificat" \
  bash -c 'jq -e "[.spec.listeners[] | select(.protocol == \"HTTPS\" and ((.tls.certificateRefs // []) | length > 0))] | length > 0" <<< "$1"' _ "$gw"
controle "un Certificate cert-manager est Ready" \
  bash -c 'kubectl -n signalements get certificates -o json | jq -e "[.items[] | select(any(.status.conditions[]?; .type == \"Ready\" and .status == \"True\"))] | length > 0"'

route=$(kubectl -n "$NS" get httproutes -o json | jq -c '[.items[] | select((.spec.hostnames // []) | index("signalements.example.com"))][0] // empty')
controle "une HTTPRoute sert signalements.example.com" test -n "$route"
controle "la HTTPRoute est Accepted et ses références résolues" \
  bash -c 'jq -e "[.status.parents[]?.conditions[]? | select(.status == \"True\") | .type] as \$c | (\$c | index(\"Accepted\")) != null and (\$c | index(\"ResolvedRefs\")) != null" <<< "$1"' _ "$route"
controle "l'écouteur HTTP redirige vers HTTPS" \
  bash -c 'kubectl -n signalements get httproutes -o json | jq -e "[.items[].spec.rules[]?.filters[]? | select(.type == \"RequestRedirect\" and .requestRedirect.scheme == \"https\")] | length > 0"'

politiques=$(kubectl -n "$NS" get networkpolicies -o json)
controle "une politique de refus par défaut couvre tous les pods, en entrée et en sortie" \
  bash -c 'jq -e "[.items[] | select((.spec.podSelector | length) == 0 or (.spec.podSelector.matchLabels // {} | length) == 0 and (.spec.podSelector.matchExpressions // [] | length) == 0) | select((.spec.policyTypes | index(\"Ingress\")) != null and (.spec.policyTypes | index(\"Egress\")) != null) | select((.spec.ingress // []) == [] and (.spec.egress // []) == [])] | length > 0" <<< "$1"' _ "$politiques"
controle "une politique autorise le DNS en sortie (port 53)" \
  bash -c 'jq -e "[.items[].spec.egress[]?.ports[]? | select(.port == 53 or .port == \"dns\" or .port == \"dns-tcp\")] | length > 0" <<< "$1"' _ "$politiques"

controle "un pod sans rôle du namespace ne joint pas l'application" \
  bash -c '! kubectl -n signalements run essai-$RANDOM --rm -i --restart=Never --quiet --image=curlimages/curl:8.16.0 --command -- curl -s -o /dev/null --max-time 4 http://signalements/sante'
controle "un pod d'un autre namespace ne joint pas l'application" \
  bash -c '! kubectl -n default run essai-$RANDOM --rm -i --restart=Never --quiet --image=curlimages/curl:8.16.0 --command -- curl -s -o /dev/null --max-time 4 http://signalements.signalements.svc.cluster.local/sante'
controle "l'application elle-même résout et joint la base (GET /sante, qui teste la base)" \
  bash -c 'kubectl -n signalements exec deploy/signalements -- python3 -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen(\"http://127.0.0.1:8000/sante\", timeout=4).status == 200 else 1)"'

echo
echo "$echecs échec(s)"
exit "$echecs"

Quelques contrôles demandent une explication :

  • Les essais de connexion sont l'arbitre : une NetworkPolicy bien écrite mais que le plugin réseau n'applique pas passe les contrôles de forme et échoue ici. Un essai qui doit échouer réussit si la connexion est refusée ou expire (code de curl non nul).
  • Le dernier contrôle passe par kubectl exec dans un pod de l'application : la sonde /sante interroge la base, donc une réponse 200 prouve que le DNS et le chemin application vers base sont ouverts. Il suppose que l'image de l'application contient Python, ce qui est le cas de Signalements.
  • La politique de refus par défaut se reconnaît à un sélecteur de pods vide, aux deux types Ingress et Egress, et à l'absence de règle.

Démonter

Supprimez les NetworkPolicies, la HTTPRoute, la Gateway et les objets cert-manager du namespace, ou le cluster entier avec kind delete cluster --name formation.

Plan du cours