diff --git a/flux-genere/site-backup-01.connectivite.json b/flux-genere/site-backup-01.connectivite.json new file mode 100644 index 0000000..c3dc110 --- /dev/null +++ b/flux-genere/site-backup-01.connectivite.json @@ -0,0 +1,190 @@ +{ + "hote": "site-backup-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_backup", + "sources": [ + "10.31.32.11", + "10.31.33.11", + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_backup_site", + "sources": [ + "10.17.0.0/16", + "10.23.0.0/16" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + } + ] +} diff --git a/flux-genere/site-backup-01.nft b/flux-genere/site-backup-01.nft new file mode 100644 index 0000000..3b76bc9 --- /dev/null +++ b/flux-genere/site-backup-01.nft @@ -0,0 +1,86 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-backup-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.32.11, 10.31.33.11, 10.31.36.11 } tcp dport 22 accept comment "serveur_backup: Dépôt restic servi par SSH (utilisateur restreint restic + clé) ; chaque client_backup pousse ses instant..." + ip saddr { 10.17.0.0/16, 10.23.0.0/16 } tcp dport 22 accept comment "serveur_backup_site: Les écosystèmes de ce site déposent leur état ici tant qu'ils n'ont pas leur propre dépôt. SFTP p..." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-cache-01.connectivite.json b/flux-genere/site-cache-01.connectivite.json new file mode 100644 index 0000000..75af182 --- /dev/null +++ b/flux-genere/site-cache-01.connectivite.json @@ -0,0 +1,204 @@ +{ + "hote": "site-cache-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "tcp", + "port": "3142", + "role": "serveur_cache_site", + "sources": [ + "10.17.0.0/16", + "10.23.0.0/16" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + }, + { + "proto": "tcp", + "port": "443", + "role": "serveur_cache_site" + }, + { + "proto": "tcp", + "port": "80", + "role": "serveur_artefacts" + }, + { + "proto": "tcp", + "port": "80", + "role": "serveur_cache_site" + } + ] +} diff --git a/flux-genere/site-cache-01.nft b/flux-genere/site-cache-01.nft new file mode 100644 index 0000000..d44d218 --- /dev/null +++ b/flux-genere/site-cache-01.nft @@ -0,0 +1,86 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-cache-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 3142 accept comment "serveur_artefacts: Toute la flotte prend ses paquets ici. En clair, et c'est correct : l'intégrité d'un dépôt apt vient..." + ip saddr { 10.17.0.0/16, 10.23.0.0/16 } tcp dport 3142 accept comment "serveur_cache_site: Servir les caches des écosystèmes voisins. Debian n'est ainsi téléchargé qu'une fois pour tout le s..." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-dns-01.connectivite.json b/flux-genere/site-dns-01.connectivite.json new file mode 100644 index 0000000..c586c88 --- /dev/null +++ b/flux-genere/site-dns-01.connectivite.json @@ -0,0 +1,229 @@ +{ + "hote": "site-dns-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "udp", + "port": "53", + "role": "serveur_resolveur", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "udp", + "port": "53", + "role": "serveur_resolveur_site", + "sources": [ + "10.17.0.0/16", + "10.23.0.0/16" + ] + }, + { + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur_site", + "sources": [ + "10.17.0.0/16", + "10.23.0.0/16" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + }, + { + "proto": "tcp", + "port": "53", + "role": "serveur_powerdns" + }, + { + "proto": "udp", + "port": "53", + "role": "serveur_powerdns" + }, + { + "proto": "udp", + "port": "53", + "role": "serveur_resolveur" + } + ] +} diff --git a/flux-genere/site-dns-01.nft b/flux-genere/site-dns-01.nft new file mode 100644 index 0000000..f79d0ef --- /dev/null +++ b/flux-genere/site-dns-01.nft @@ -0,0 +1,88 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-dns-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } udp dport 53 accept comment "serveur_resolveur: Toute la flotte du tenant résout ici — et nulle part ailleurs." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 53 accept comment "serveur_resolveur: Réponses longues et bascule TCP, obligatoires en DNS." + ip saddr { 10.17.0.0/16, 10.23.0.0/16 } udp dport 53 accept comment "serveur_resolveur_site: Les écosystèmes de ce site résolvent ici tant qu'ils n'ont pas leur propre résolveur. `client_re..." + ip saddr { 10.17.0.0/16, 10.23.0.0/16 } tcp dport 53 accept comment "serveur_resolveur_site: Réponses longues et bascule TCP, obligatoires en DNS." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-dnspub-01.connectivite.json b/flux-genere/site-dnspub-01.connectivite.json new file mode 100644 index 0000000..95743bf --- /dev/null +++ b/flux-genere/site-dnspub-01.connectivite.json @@ -0,0 +1,205 @@ +{ + "hote": "site-dnspub-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "udp", + "port": "1053", + "role": "serveur_dns_public", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "tcp", + "port": "1053", + "role": "serveur_dns_public", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "udp", + "port": "53", + "role": "serveur_dns_public", + "sources": [ + "10.17.0.0/16", + "10.23.0.0/16" + ] + }, + { + "proto": "tcp", + "port": "53", + "role": "serveur_dns_public", + "sources": [ + "10.17.0.0/16", + "10.23.0.0/16" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + } + ] +} diff --git a/flux-genere/site-dnspub-01.nft b/flux-genere/site-dnspub-01.nft new file mode 100644 index 0000000..68a5651 --- /dev/null +++ b/flux-genere/site-dnspub-01.nft @@ -0,0 +1,88 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-dnspub-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + udp dport 1053 accept comment "serveur_dns_public: Les resolveurs de l'Internet interrogent les zones publiques des locataires du site. Les reponses sont s..." + tcp dport 1053 accept comment "serveur_dns_public: Repli TCP : reponses tronquees par dnsdist (ANY, debit) et reponses signees trop grosses pour l'UDP." + ip saddr { 10.17.0.0/16, 10.23.0.0/16 } udp dport 53 accept comment "serveur_dns_public: NOTIFY des primaires des locataires du site : une zone publique a change. Le contenu d'une zone publique..." + ip saddr { 10.17.0.0/16, 10.23.0.0/16 } tcp dport 53 accept comment "serveur_dns_public: Repli TCP des notifications des primaires des locataires." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-edge-01.connectivite.json b/flux-genere/site-edge-01.connectivite.json new file mode 100644 index 0000000..8ab99ff --- /dev/null +++ b/flux-genere/site-edge-01.connectivite.json @@ -0,0 +1,233 @@ +{ + "hote": "site-edge-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3000", + "role": "serveur_grafana" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "8080", + "role": "serveur_icingaweb2" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "8090", + "role": "serveur_ops" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "80", + "role": "serveur_nginx", + "sources": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ] + }, + { + "proto": "tcp", + "port": "443", + "role": "serveur_nginx", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "tcp", + "port": "443", + "role": "serveur_nginx", + "sources": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + } + ] +} diff --git a/flux-genere/site-edge-01.nft b/flux-genere/site-edge-01.nft new file mode 100644 index 0000000..87bc8f6 --- /dev/null +++ b/flux-genere/site-edge-01.nft @@ -0,0 +1,87 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-edge-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + ip saddr { 10.31.0.0/24, 10.31.31.0/24, 10.31.31.1/32, 10.31.32.1/32, 10.31.33.1/32, 10.31.34.1/32, 10.31.35.1/32, 10.31.36.1/32, 10.31.37.1/32 } tcp dport 80 accept comment "serveur_nginx: HTTP depuis le reseau d'administration — redirection permanente vers HTTPS." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 443 accept comment "serveur_nginx: HTTPS depuis le tenant : les FQDN publiés vivent à l'edge (découverte OIDC, appels inter-services par nom)." + ip saddr { 10.31.0.0/24, 10.31.31.0/24, 10.31.31.1/32, 10.31.32.1/32, 10.31.33.1/32, 10.31.34.1/32, 10.31.35.1/32, 10.31.36.1/32, 10.31.37.1/32 } tcp dport 443 accept comment "serveur_nginx: HTTPS depuis le reseau d'administration : l'exploitant administre les services par leur interface web, servie..." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-forge-01.connectivite.json b/flux-genere/site-forge-01.connectivite.json new file mode 100644 index 0000000..c6c20b9 --- /dev/null +++ b/flux-genere/site-forge-01.connectivite.json @@ -0,0 +1,214 @@ +{ + "hote": "site-forge-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_backup" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5432", + "role": "serveur_postgresql" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "443", + "role": "serveur_forge_site", + "sources": [ + "10.17.0.0/16", + "10.23.0.0/16" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_forge_site", + "sources": [ + "10.17.19.41", + "10.23.19.41" + ] + }, + { + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo", + "sources": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.31.11", + "10.31.32.1/32", + "10.31.32.11", + "10.31.33.1/32", + "10.31.33.11", + "10.31.33.21", + "10.31.34.1/32", + "10.31.34.11", + "10.31.35.1/32", + "10.31.35.11", + "10.31.36.1/32", + "10.31.36.11", + "10.31.37.1/32", + "10.31.37.11", + "10.31.37.21" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + } + ] +} diff --git a/flux-genere/site-forge-01.nft b/flux-genere/site-forge-01.nft new file mode 100644 index 0000000..bf3cbec --- /dev/null +++ b/flux-genere/site-forge-01.nft @@ -0,0 +1,87 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-forge-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + ip saddr { 10.17.0.0/16, 10.23.0.0/16 } tcp dport 443 accept comment "serveur_forge_site: Servir le génome aux écosystèmes de ce site : c'est de cette forge qu'ils clonent leur moteur, leurs..." + ip saddr { 10.17.19.41, 10.23.19.41 } tcp dport 22 accept comment "serveur_forge_site: Chaque runner de locataire lit son propre depot du genome, prive, avec sa cle de deploiement en lecture..." + ip saddr { 10.31.0.0/24, 10.31.31.0/24, 10.31.31.1/32, 10.31.31.11, 10.31.32.1/32, 10.31.32.11, 10.31.33.1/32, 10.31.33.11, 10.31.33.21, 10.31.34.1/32, 10.31.34.11, 10.31.35.1/32, 10.31.35.11, 10.31.36.1/32, 10.31.36.11, 10.31.37.1/32, 10.31.37.11, 10.31.37.21 } tcp dport 443 accept comment "serveur_forgejo: Sans edge devant elle — la forge du SITE — elle sert son propre TLS sur le port du schéma (443), avec..." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-mon-01.connectivite.json b/flux-genere/site-mon-01.connectivite.json new file mode 100644 index 0000000..8f13c49 --- /dev/null +++ b/flux-genere/site-mon-01.connectivite.json @@ -0,0 +1,423 @@ +{ + "hote": "site-mon-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_backup" + }, + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "9100", + "role": "client_metrique" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "3000", + "role": "serveur_grafana", + "sources": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32", + "10.31.37.11" + ] + }, + { + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga", + "sources": [ + "10.31.32.11", + "10.31.33.11", + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga", + "sources": [ + "10.31.35.11" + ] + }, + { + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "tcp", + "port": "8080", + "role": "serveur_icingaweb2", + "sources": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32", + "10.31.37.11" + ] + }, + { + "proto": "tcp", + "port": "3100", + "role": "serveur_loki", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "tcp", + "port": "3100", + "role": "serveur_loki", + "sources": [ + "10.0.4.1", + "10.0.4.11", + "10.31.0.1", + "10.31.0.11", + "10.31.31.1", + "10.31.32.1", + "10.31.33.1", + "10.31.34.1", + "10.31.35.1", + "10.31.36.1", + "10.31.37.1", + "10.31.50.11", + "10.31.60.2" + ] + }, + { + "proto": "tcp", + "port": "1514", + "role": "serveur_loki", + "sources": [ + "10.0.4.1", + "10.0.4.11", + "10.31.0.1", + "10.31.0.11", + "10.31.31.1", + "10.31.32.1", + "10.31.33.1", + "10.31.34.1", + "10.31.35.1", + "10.31.36.1", + "10.31.37.1", + "10.31.50.11", + "10.31.60.2" + ] + }, + { + "proto": "tcp", + "port": "25", + "role": "serveur_postfix", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "tcp", + "port": "5432", + "role": "serveur_postgresql", + "sources": [ + "10.31.33.11", + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "9187", + "role": "serveur_postgresql", + "sources": [ + "10.31.36.11" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + }, + { + "proto": "tcp", + "port": "25", + "role": "serveur_postfix" + } + ] +} diff --git a/flux-genere/site-mon-01.nft b/flux-genere/site-mon-01.nft new file mode 100644 index 0000000..2f1677b --- /dev/null +++ b/flux-genere/site-mon-01.nft @@ -0,0 +1,95 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-mon-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + ip saddr { 10.31.0.0/24, 10.31.31.0/24, 10.31.31.1/32, 10.31.32.1/32, 10.31.33.1/32, 10.31.34.1/32, 10.31.35.1/32, 10.31.36.1/32, 10.31.37.1/32, 10.31.37.11 } tcp dport 3000 accept comment "serveur_grafana: Interface web : servie via l'edge la ou il y en a un, joignable depuis le plan d'administration partout —..." + ip saddr { 10.31.32.11, 10.31.33.11, 10.31.36.11 } tcp dport 5665 accept comment "serveur_icinga: Rapport passif de chaque detenteur d'etat sur SON depot distant : le depot du site heberge du chiffre et ne..." + ip saddr { 10.31.35.11 } tcp dport 5665 accept comment "serveur_icinga: Le depot de sauvegarde depose ses resultats passifs (portee : process-check-result sur « sauvegarde: * »)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 5665 accept comment "serveur_icinga: Rapport passif de sante de chaque noeud (unites systemd en echec)." + ip saddr { 10.31.0.0/24, 10.31.31.0/24, 10.31.31.1/32, 10.31.32.1/32, 10.31.33.1/32, 10.31.34.1/32, 10.31.35.1/32, 10.31.36.1/32, 10.31.37.1/32, 10.31.37.11 } tcp dport 8080 accept comment "serveur_icingaweb2: Interface web servie via l'edge (TLS terminé à l'edge ; SSO possible via oauth2-proxy), et joignable d..." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 3100 accept comment "serveur_loki: Réception des journaux poussés par Alloy (client_journal) en HTTPS (http_tls_config, cert step-ca)." + ip saddr { 10.0.4.1, 10.0.4.11, 10.31.0.1, 10.31.0.11, 10.31.31.1, 10.31.32.1, 10.31.33.1, 10.31.34.1, 10.31.35.1, 10.31.36.1, 10.31.37.1, 10.31.50.11, 10.31.60.2 } tcp dport 3100 accept comment "serveur_loki: Journaux des hyperviseurs. La machine qui PORTE les VM ecrivait ses journaux nulle part ailleurs que sur son p..." + ip saddr { 10.0.4.1, 10.0.4.11, 10.31.0.1, 10.31.0.11, 10.31.31.1, 10.31.32.1, 10.31.33.1, 10.31.34.1, 10.31.35.1, 10.31.36.1, 10.31.37.1, 10.31.50.11, 10.31.60.2 } tcp dport 1514 accept comment "serveur_loki: Journaux de la frontiere, en TLS mutuel. Elle n'accueille aucun agent et n'emet que du syslog ; sans ce flux,..." + tcp dport 25 accept comment "serveur_postfix: SMTP entrant : courrier externe (MX) et notifications internes (client_smtp)." + ip saddr { 10.31.33.11, 10.31.36.11 } tcp dport 5432 accept comment "serveur_postgresql: Connexions applicatives à PostgreSQL (verify-full ; pg_hba hostssl)." + ip saddr { 10.31.36.11 } tcp dport 9187 accept comment "serveur_postgresql: Metriques PostgreSQL lues par l'observatoire, en HTTPS verifie. Series, pas verdicts : ce qui derive len..." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-ops-01.connectivite.json b/flux-genere/site-ops-01.connectivite.json new file mode 100644 index 0000000..8a97a65 --- /dev/null +++ b/flux-genere/site-ops-01.connectivite.json @@ -0,0 +1,193 @@ +{ + "hote": "site-ops-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-pki-01", + "ip": "10.31.32.11", + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "8090", + "role": "serveur_ops", + "sources": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32", + "10.31.37.11" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + }, + { + "proto": "tcp", + "port": "443", + "role": "serveur_ops_site" + } + ] +} diff --git a/flux-genere/site-ops-01.nft b/flux-genere/site-ops-01.nft new file mode 100644 index 0000000..03449e7 --- /dev/null +++ b/flux-genere/site-ops-01.nft @@ -0,0 +1,85 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-ops-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + ip saddr { 10.31.0.0/24, 10.31.31.0/24, 10.31.31.1/32, 10.31.32.1/32, 10.31.33.1/32, 10.31.34.1/32, 10.31.35.1/32, 10.31.36.1/32, 10.31.37.1/32, 10.31.37.11 } tcp dport 8090 accept comment "serveur_ops: Console d'exploitation servie par l'edge (TLS terminé à l'edge), et joignable depuis le plan d'administration..." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/flux-genere/site-pki-01.connectivite.json b/flux-genere/site-pki-01.connectivite.json new file mode 100644 index 0000000..af42baf --- /dev/null +++ b/flux-genere/site-pki-01.connectivite.json @@ -0,0 +1,180 @@ +{ + "hote": "site-pki-01", + "genere_par": "scripts/resoudre_flux.py (make flux) — ne pas editer", + "sorties": [ + { + "vers": "site-backup-01", + "ip": "10.31.35.11", + "proto": "tcp", + "port": "22", + "role": "serveur_backup" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-cache-01", + "ip": "10.31.33.21", + "proto": "tcp", + "port": "3142", + "role": "serveur_artefacts" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-dns-01", + "ip": "10.31.34.11", + "proto": "tcp", + "port": "53", + "role": "serveur_resolveur" + }, + { + "vers": "site-dnspub-01", + "ip": "10.31.37.21", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-edge-01", + "ip": "10.31.37.11", + "proto": "tcp", + "port": "443", + "role": "serveur_nginx" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-forge-01", + "ip": "10.31.33.11", + "proto": "tcp", + "port": "443", + "role": "serveur_forgejo" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "3100", + "role": "serveur_loki" + }, + { + "vers": "site-mon-01", + "ip": "10.31.36.11", + "proto": "tcp", + "port": "5665", + "role": "serveur_icinga" + }, + { + "vers": "site-ops-01", + "ip": "10.31.31.11", + "proto": "tcp", + "port": "22", + "role": "serveur_debian" + } + ], + "entrees": [ + { + "proto": "tcp", + "port": "9100", + "role": "client_metrique", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "22", + "role": "serveur_debian", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + }, + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian", + "sources": [ + "0.0.0.0/0" + ] + }, + { + "proto": "icmp", + "port": "echo-request", + "role": "serveur_debian", + "sources": [ + "10.31.36.11" + ] + }, + { + "proto": "tcp", + "port": "8443", + "role": "serveur_step_ca", + "sources": [ + "10.31.31.11", + "10.31.32.11", + "10.31.33.11", + "10.31.33.21", + "10.31.34.11", + "10.31.35.11", + "10.31.36.11", + "10.31.37.11", + "10.31.37.21" + ] + } + ], + "admin_ssh": [ + "10.31.0.0/24", + "10.31.31.0/24", + "10.31.31.1/32", + "10.31.32.1/32", + "10.31.33.1/32", + "10.31.34.1/32", + "10.31.35.1/32", + "10.31.36.1/32", + "10.31.37.1/32" + ], + "sorties_externes": [ + { + "proto": "icmp", + "port": "frag-needed", + "role": "serveur_debian" + } + ] +} diff --git a/flux-genere/site-pki-01.nft b/flux-genere/site-pki-01.nft new file mode 100644 index 0000000..4bfc910 --- /dev/null +++ b/flux-genere/site-pki-01.nft @@ -0,0 +1,85 @@ +#!/usr/sbin/nft -f +# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote site-pki-01. +# Deploye par le role `nftables_baseline` en tant que /etc/nftables.conf. + +# Remplace UNIQUEMENT les tables setops (pas de `flush ruleset` : préserve les +# tables etrangeres — DNAT/forward de conteneurs, par exemple). +# +# `setops_filter` est le gabarit de REPLI, pose quand aucun ruleset resolu +# n'existe encore. Les deux fichiers sont des ALTERNATIVES, jamais des couches — +# mais `nft -f` AJOUTE sans purger, et le repli survivait donc a la bascule. +# Deux chaines `input` sur le meme hook, toutes deux en `policy drop` : le +# paquet traverse les DEUX, et seule l'intersection de leurs `accept` passait. +# Mesure du 2026-08-10 sur Technolibre : l'AC etait debout, sa regle 8443 posee +# et acceptante, l'ICMP passait en 0,15 ms — et le TCP expirait, arrete par le +# `policy drop` du repli. Chaque fichier retire donc desormais la table de +# l'autre, et la bascule converge dans les deux sens. +table inet setops_flux {} +delete table inet setops_flux +table inet setops_filter {} +delete table inet setops_filter + +table inet setops_flux { + chain input { + type filter hook input priority 0; policy drop; + + iif "lo" accept comment "socle: communications locales" + ct state established,related accept comment "socle: connexions etablies et associees" + ct state invalid drop comment "socle: paquets invalides" + ip protocol icmp accept comment "socle: diagnostic et erreurs reseau IPv4" + ip6 nexthdr icmpv6 accept comment "socle: diagnostic et erreurs reseau IPv6" + + ip saddr { 10.31.0.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.0/24 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.31.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.32.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.33.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.34.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.35.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.37.1/32 } tcp dport 22 accept comment "administration: Garde anti-lockout, intrant nftables_admin_ssh" + ip saddr { 10.31.36.11 } tcp dport 9100 accept comment "client_metrique: Scrape des métriques par Prometheus (node_exporter en HTTPS)." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 22 accept comment "serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur..." + icmp type destination-unreachable icmp code frag-needed accept comment "serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire no..." + ip saddr { 10.31.36.11 } icmp type echo-request accept comment "serveur_debian: La supervision verifie que ce noeud repond (hostalive). Sans lui, elle le tient pour mort et supprime ses no..." + ip saddr { 10.31.31.11, 10.31.32.11, 10.31.33.11, 10.31.33.21, 10.31.34.11, 10.31.35.11, 10.31.36.11, 10.31.37.11, 10.31.37.21 } tcp dport 8443 accept comment "serveur_step_ca: ACME + API step-ca : chaque nœud (client_pki) émet/renouvelle ses certificats et récupère la racine." + + # L'INTERNE PARLE, LA BORDURE SE TAIT (2026-08-28). + # + # Ce qui n'est pas accepte plus haut est REFUSE A VOIX HAUTE, et ce n'est pas + # le refus qui informe : c'est ce qu'il fait au TIMEOUT. Sous `drop`, un + # silence voulait dire trois choses incompatibles — aucune machine, aucune + # route, ou une politique. Deux erreurs de diagnostic en une journee le + # 2026-08-28, dont une qui a fait appliquer un pare-feu pour reparer une + # ABSENCE. Quand la politique parle, le silence ne veut plus dire qu'une + # chose : il n'y a personne, ou il n'y a pas de chemin. + # + # `admin-prohibited` ET NON `tcp reset` : un RST est indiscernable d'un port + # ferme sans service. Ce message-ci dit qu'une POLITIQUE a refuse — c'est la + # seule forme qui distingue « on ne veut pas de toi » de « il n'y a rien ». + # + # POURQUOI C'EST PRUDENT ICI ET PAS A LA FRONTIERE : la bordure reste en + # `block`, donc rien d'indeclare ne franchit le perimetre. Ce reject ne + # repond jamais a l'Internet — il ne parle qu'a des pairs qui ont deja + # l'adresse de cette machine dans leur `/etc/hosts` genere. L'obscurite + # etait donc deja nulle entre nous : le `drop` coutait du diagnostic sans + # rien acheter. Mesure du 2026-08-27 a la frontiere, qui explique l'autre + # moitie du choix : 982 000 entrees par jour, dont 82 %% un balayage + # Internet contre le port VNC. Repondre a chacune serait un vecteur + # d'amplification, source usurpee comprise. + reject with icmpx type admin-prohibited comment "socle: refus des entrees non autorisees" + } + + # LA CHAINE `forward` RESTE MUETTE, et ce n'est pas un oubli : elle porte le + # trafic qui TRAVERSE cet hote, pas celui qui lui est destine. Y repondre + # ferait parler cette machine AU NOM d'une destination qui n'est pas elle — + # un refus qu'on attribuerait au mauvais endroit, c'est-a-dire exactement le + # defaut qu'on corrige dans `input`. + chain forward { + type filter hook forward priority 0; policy drop; + ct state established,related accept comment "transit: connexions etablies et associees" + iifname "docker0" accept comment "transit: trafic depuis le pont docker0" + oifname "docker0" accept comment "transit: trafic vers le pont docker0" + } + chain output { type filter hook output priority 0; policy accept; } +} diff --git a/plan/10-intrants.yml b/plan/10-intrants.yml index 257df73..5a2538d 100644 --- a/plan/10-intrants.yml +++ b/plan/10-intrants.yml @@ -22,8 +22,12 @@ integrations_exemptes: {} # La cle publique du runner du site — GENEREE au premier deploiement de `site-ops-01`, puis # recopiee ici (et dans `cles_admin`). Vide au depart : c'est normal. runner_cle_publique: "" -# `forge_ssh_cle_hote` : la cle d'hote SSH de la forge, relevee apres le premier deploiement -# de `site-forge-01` — comme au site de Chezlepro. Absente tant que la forge n'existe pas. +# LA CLE D'HOTE SSH DE LA FORGE, epinglee par le runner avant de lire un depot prive. Relevee +# A LA SOURCE le 2026-10-13, apres le premier deploiement de `site-forge-01` — jamais recopiee +# de memoire, et identique a ce que la forge presente sur le reseau : +# +# ssh ansible@10.31.33.11 cat /etc/ssh/ssh_host_ed25519_key.pub +forge_ssh_cle_hote: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMQ5lQPeCw1enlogZUk8AZax4mk2wMMa/C5Wd9ioXq9S" # LE GABARIT. Sur un noeud unique il n'y a pas d'autre machine pour le porter : ne jamais le # personnaliser, et ne jamais convertir une VM i440fx en q35.