Skip to content

ADR 035 — Le NACL par défaut reste large en entrée, le FAIL CIS est accepté (2026-09-05)

Statut

Accepté, écrit le 2026-09-05. Issue #189 (action 14 de l'audit, diff CIS 5.2/5.3), Sprint 7 (Remédiation & Fiabilité).

Cette ADR tranche un arbitrage laissé ouvert par écrit le 2026-09-04, après le premier scan Prowler du compte. Elle ne change aucune ressource : elle acte une configuration existante et nomme ce qui la ferait revoir.

C'est la troisième voie que l'issue #199 avait nommée pour une autre question et qui vaut ici : accepter et documenter est une décision, pas une absence de décision.

Contexte

Ce que le NACL pose aujourd'hui

Un NACL (Network ACL) est le filtre réseau attaché au sous-réseau, en amont des groupes de sécurité qui, eux, sont attachés aux ressources.

terraform/modules/vpc/main.tf configure le NACL par défaut du VPC, en entrée :

# Action Protocole Ports Source
100 deny TCP 22 0.0.0.0/0
110 deny TCP 3389 0.0.0.0/0
200 allow tous tous 0.0.0.0/0
32767 deny tous tous 0.0.0.0/0 (implicite)

En sortie : 100 allow tout, puis le deny implicite.

Les règles sont évaluées par numéro croissant et la première qui correspond gagne. Les deux deny étroits passent donc avant l'allow général : 22 et 3389 sont bien refusés depuis internet.

La règle 200 est large parce qu'un NACL est stateless : il n'a aucune mémoire des connexions. Le trafic de retour d'un appel sortant revient sur un port éphémère tiré au hasard, et doit être autorisé explicitement, sans quoi plus rien ne sort du VPC.

Ce que le scanner rapporte

Scan Prowler du 2026-09-04, VPC monté, région eu-west-3 :

Check fastapi-eks-default-nacl
ec2_networkacl_allow_ingress_tcp_port_22 PASS
ec2_networkacl_allow_ingress_tcp_port_3389 PASS
ec2_networkacl_allow_ingress_any_port FAIL

Le FAIL n'est pas un faux positif. ec2_networkacl_allow_ingress_any_port ne demande pas « le port 22 est-il ouvert » mais « tous les ports sont-ils ouverts ». La réponse est factuellement oui.

Les deux contrôles ne posent pas la même question, et un même objet peut être conforme à l'un et non conforme à l'autre sans contradiction. La configuration n'est pas fausse, elle est étroite : la défense en profondeur voulue par l'action 14 ne couvre que deux ports.

Où se trouvent réellement les ressources

C'est le fait qui manquait à l'arbitrage, et il change la conclusion.

Les nœuds EKS sont dans les subnets privés. terraform/modules/eks/main.tf les place sur private_subnet_ids (lignes 135, 244, 292), et create_nat_gateway vaut true par défaut.

                    internet
                       |
              [ Internet Gateway ]
                       |
   subnets PUBLICS  10.0.1.0/24  10.0.2.0/24
     |  NLB (80, 443)        |  NAT Gateway
     |                       |
     +-----------+-----------+
                 |  (pas de route entrante)
   subnets PRIVÉS  10.0.3.0/24  10.0.4.0/24
        nœuds EKS, ENI du cluster, RDS

Un subnet privé n'a aucune route vers l'Internet Gateway. Un paquet non sollicité venu d'internet ne peut donc pas y arriver, quoi que dise le NACL. La règle 200 y est inatteignable depuis l'extérieur.

Restent les subnets publics. Ils portent exactement deux choses : le NLB d'Envoy Gateway, qui n'écoute que 80 et 443, et la NAT Gateway, qui n'écoute sur aucun port.

Ce que la parade fermerait vraiment

La parade cadrée remplace la règle 200 par quatre règles : 80, 443, les ports éphémères 1024-65535 pour le retour, et le trafic interne au VPC.

Après cette parade, 1024-65535 reste ouvert depuis 0.0.0.0/0, faute de quoi le VPC se coupe du réseau. Le seul gain est donc la fermeture de 0-1023 sauf 80 et 443.

Aujourd'hui Après la parade
Ports ouverts depuis internet tous 80, 443, 1024-65535
Ce qui écoute sur les subnets publics NLB sur 80/443 inchangé
Réduction d'exposition faible voire nulle
Risque oublier une catégorie coupe le VPC

Rien n'écoute sous 1024 sur ces subnets en dehors du NLB, déjà autorisé. RDS (5432) et kubelet (10250) sont au-dessus de 1024, donc la parade ne les couvrirait pas davantage, et ils vivent de toute façon dans les subnets privés.

Décision

Nous acceptons le FAIL de ec2_networkacl_allow_ingress_any_port sur le NACL par défaut du VPC, parce que la restriction proposée n'apporte pas de réduction d'exposition significative dans l'architecture actuelle, tout en augmentant le risque de régression réseau.

Quatre points la précisent.

1. Le FAIL est connu et accepté, il n'est pas ignoré

La différence est tout le sujet. Un FAIL accepté est une ligne d'un scan qu'on sait expliquer, rattachée à une décision datée. Un FAIL ignoré est une ligne que personne n'a lue.

Tout scan CIS futur du compte fera ressortir ce contrôle. La réponse est cette ADR, pas une nouvelle enquête.

2. Le gain est jugé faible dans l'architecture actuelle, pas dans l'absolu

L'affirmation n'est pas « ce contrôle ne sert à rien ». Elle est : compte tenu de ce qui est déployé aujourd'hui dans les subnets publics, la réduction de surface serait faible voire nulle.

C'est une affirmation datée, qui dépend de faits vérifiables. Elle tombe si les faits changent, d'où le point 4.

3. Le contrôle réseau se lit sur trois niveaux, et le NACL n'est pas le principal

Niveau État Ce qu'il assure
Groupes de sécurité des ressources restrictifs le contrôle fin, stateful, par ressource
Routage des subnets privés pas de route entrante l'inaccessibilité depuis internet
NACL, unique, sur tous les subnets large en entrée, sauf 22 et 3389 une barrière grossière, en défense en profondeur

Il n'existe qu'un seul NACL. terraform/modules/vpc/main.tf l'associe aux deux familles de subnets en un seul concat : les subnets privés portent donc exactement le même NACL large que les publics. Ce qui les protège, c'est le routage, pas une règle.

Ce point est écrit explicitement pour qu'aucune relecture ne suppose un NACL privé restrictif qui n'existe pas.

4. La décision est réévaluée si les flux publics changent

Elle repose sur un inventaire, donc elle doit être revue quand l'inventaire bouge. Quatre déclencheurs, à traiter comme une checklist de revue :

  1. une ressource exposée est ajoutée dans un subnet public ;
  2. un listener du NLB est ajouté ou modifié ;
  3. un service se met à écouter sur un autre port dans un subnet public ;
  4. la topologie réseau change, en particulier si des nœuds passent en public.

Dans ces cas, la parade de l'alternative B ci-dessous redevient candidate, et cette ADR doit être remplacée plutôt que citée.

Conséquences

Ce qui ne change pas. Aucune ressource n'est modifiée, aucun apply n'est requis. Le NACL reste tel qu'il est déployé et vérifié le 2026-09-04.

Ce qui change. L'action 14 de l'audit est traitée : elle est livrée pour 22 et 3389, et son extension à any_port est explicitement écartée, avec sa justification. La case NACL de #189 se ferme sur cette décision.

Ce qu'il faut accepter. Un scan CIS du compte continuera de sortir un FAIL sur ce contrôle. C'est le prix assumé de ne pas faire de compliance theater : transformer un FAIL en PASS sans réduire l'exposition réelle, en échange d'une configuration plus longue et plus fragile.

Ce qui reste ouvert, hors périmètre. Le NACL du VPC par défaut de la région échoue sur les trois contrôles. Aucune ressource du projet n'y tourne, mais il vit dans le même compte et ressortira dans tout scan futur. À traiter dans une issue séparée.

Un contrôle qui n'existe que pendant une fenêtre d'infra montée n'existe pas. Le scan du 2026-09-04 a été joué à la main et n'aurait pas pu l'être après le teardown. L'absence de job CIS dans les configs CI est un manque distinct, également à traiter à part.

Alternatives écartées

A. Fermer any_port en énumérant les flux

Remplacer la règle 200 par 80, 443, 1024-65535 et le trafic interne au VPC.

Écartée pour la raison qui fait tout l'objet de cette ADR : elle laisse 1024-65535 ouvert de toute façon, donc ne ferme que 0-1023 hors 80 et 443, où rien n'écoute. Elle achète un PASS au prix d'un risque de coupure réseau.

Oublier les ports éphémères est l'erreur classique du durcissement NACL : elle ne se voit pas au plan, elle se voit quand plus rien ne sort du VPC.

B. Séparer le NACL public du NACL privé

Créer un second NACL, dédié aux subnets privés, autorisant le CIDR 10.0.0.0/16 et les ports éphémères, et refusant le reste.

Écartée pour l'instant, et nommée ici pour ne pas être réinventée. Trois raisons :

  • le retour du trafic sortant traverse la NAT Gateway avec l'adresse publique distante en source, donc le NACL privé devrait quand même ouvrir 1024-65535 depuis 0.0.0.0/0 : le gain retombe au même niveau ;
  • le FAIL Prowler resterait sur le NACL public, qui est celui que le contrôle regarde ;
  • elle ajoute une ressource à maintenir et une fenêtre d'association de subnets, là où le NACL par défaut n'en a aucune.

Elle redevient candidate si l'un des quatre déclencheurs du point 4 se produit.

C. Ne rien écrire et laisser le FAIL sans réponse

Écartée : c'est la seule option qui rendrait le constat invisible. Un FAIL sans décision associée se represente à chaque scan et se re-instruit à chaque fois.

Références

  • Issue #189 — durcissement Terraform, flow logs, NACL, chiffrement EBS, HTTPS sur le state (action 14, diff CIS 5.2/5.3)
  • ADR 031 — garde-fou de teardown : même forme de décision, un contrôle qu'on choisit de ne pas rendre bloquant, par écrit
  • terraform/modules/vpc/main.tf — la ressource aws_default_network_acl.main et son commentaire, qui pointe cette ADR
  • docs/infra-eks-summary.md, section « Durcissement Terraform » — les quatre réglages de l'action 14 tels que livrés par !341