ADR 010 — Ownership de l'exposition : plateforme vs application (2026-05-30)
Contexte
Issue #60. Jusqu'ici, l'exposition publique (GatewayClass, Gateway, HTTPRoute,
Certificate) vivait dans l'overlay Kustomize k8s/overlays/gateway-api/. Or le
pipeline applicatif ne déploie que kubectl apply -k k8s/base/. Conséquence : ces
ressources n'étaient jamais déployées par la CI. Elles étaient appliquées à la
main (depuis #28), donc chaque nouveau cluster éphémère naissait sans exposition,
et api.devopsyouss.com restait down jusqu'à intervention manuelle.
Faire pointer le deploy CI (least-privilege, edit scopé au ns fastapi) sur
l'overlay gateway-api n'est pas viable :
- GatewayClass est cluster-scoped → le user CI ne peut pas la créer (même
classe qu'INC-045 sur le Namespace).
- Gateway, HTTPRoute (gateway.networking.k8s.io) et Certificate
(cert-manager.io) sont des CRDs non couvertes par le ClusterRole edit → 403
(même classe qu'INC-047/048).
Lui donner ces droits impliquerait un ClusterRoleBinding cluster-wide sur une
GatewayClass, ce qui casse le least-privilege.
Décision 1 — Le découpage suit le modèle role-oriented de Gateway API
Gateway API sépare explicitement les responsabilités infra et application. On adopte ce découpage :
| Ressource | Propriétaire | Posée par |
|---|---|---|
| GatewayClass (cluster-scoped) | Cluster operator (infra) | bootstrap Ansible |
| Gateway (ELB, listeners, TLS) | Cluster operator (infra) | bootstrap Ansible |
| Certificate (lié au listener) | Cluster operator (infra) | bootstrap Ansible |
| HTTPRoute (routage app) | Développeur d'app | deploy CI (k8s/base) |
Le Gateway est un point d'entrée stable, provisionné une fois par cycle de cluster.
Le HTTPRoute évolue au rythme des releases (canary via backendRefs pondérés,
nouveaux paths, header matching pour blueprints) et doit donc rester pilotable par
la CI sans re-bootstrap.
Prolongement direct d'INC-045 : les ressources cluster-scoped et de plateforme sont ownées par le bootstrap cluster-admin, pas par le deploy least-privilege.
Décision 2 — Le HTTPRoute conserve son propre binding RBAC explicite
Puisque le HTTPRoute repart dans k8s/base/ (déployé par la CI), le user CI doit
pouvoir agir sur les httproutes. Comme pour les CRDs ESO (INC-048), ce CRD n'est
pas couvert par edit : le bootstrap pose un Role + RoleBinding
fastapi-deploy-httproute (ns fastapi) bindant directement l'ARN du user CI.
On réapplique le pattern validé en #59 : binding RBAC explicite sur l'ARN, jamais d'agrégation (sans effet sur les users mappés via AWS Access Policy).
Décision 3 — Manifests versionnés, appliqués par le bootstrap via src:
Les manifests d'exposition restent des fichiers YAML (pas de YAML inline dans le
playbook) :
- k8s/platform/gateway.yaml (GatewayClass + Gateway)
- k8s/platform/certificate.yaml
Le bootstrap les applique via kubernetes.core.k8s avec src:. L'overlay
k8s/overlays/gateway-api/ est supprimé (les manifests d'exposition partent dans
k8s/platform/, le HTTPRoute dans k8s/base/).
Décision 4 — Ordre de teardown pour éviter l'ELB orphelin
Le teardown supprime le Gateway avant l'uninstall d'Envoy : c'est le contrôleur
Envoy, encore vivant, qui supprime l'ELB associé. Une attente de libération
ELB + ENIs précède la suite du teardown. Sans cet ordre, l'ELB orphelin bloque
terraform destroy (DependencyViolation sur les subnets, cf INC-016).
Conséquences
api.devopsyouss.comest exposé automatiquement à chaque montée d'infra, sans intervention manuelle (hors mise à jour du CNAME, suivie par le futur item ExternalDNS).- Le HTTPRoute est exclu de l'overlay
local-kind($patch: delete), kind n'ayant pas les CRDs Gateway API. - Point ouvert (quota TLS) : le Certificate utilise
letsencrypt-prod. Le cluster étant éphémère, le secretfastapi-tlsmeurt à chaqueaws-stop, donc cert-manager ré-émet à chaque montée → quota Let's Encrypt prod (5 certificats identiques/semaine). À mitiger plus tard (persisterfastapi-tls, ou valider le flux enletsencrypt-stagingavant de basculer prod).
Date : 2026-05-30 Sprint : 3 Issue : #60