amont : declarer par quelle adresse patient 0 joint sa forge parente

Le tenant a le droit de sortir sur l'internet et pas d'entrer dans le LAN
192.168.x de son hebergeur. L'adresse publique de la forge amont repond en
~7 ms (retour en epingle local) et sert bien la version 8.0.3, distincte de la
16.0.2 locale. C'est celle-la qu'on epingle, parce qu'on a prouve qu'elle livre.

A suivre : les hotes resolvent par Quad9 et n'interrogent pas infra-dns-01.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-23 10:09:09 -04:00
parent 1b4bfd8b4b
commit 8c4a39b8b1
5 changed files with 236 additions and 0 deletions

46
flux-genere/forge-01.nft Normal file
View file

@ -0,0 +1,46 @@
#!/usr/sbin/nft -f
# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote 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
ct state established,related accept
ct state invalid drop
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
ip saddr { 10.17.0.0/24 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 192.168.255.2/32 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } tcp dport 22 accept # serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur est filtré à l'OPNsense).
icmp type destination-unreachable icmp code frag-needed accept # serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire nos paquets — les transferts se figent.
ip saddr { 10.29.16.11 } tcp dport 3000 accept # serveur_forgejo: Interface web + Git HTTP servis via l'edge (TLS terminé à l'edge).
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
iifname "docker0" accept
oifname "docker0" accept
}
chain output { type filter hook output priority 0; policy accept; }
}

View file

@ -0,0 +1,47 @@
#!/usr/sbin/nft -f
# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote infra-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
ct state established,related accept
ct state invalid drop
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
ip saddr { 10.17.0.0/24 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 192.168.255.2/32 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } tcp dport 22 accept # serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur est filtré à l'OPNsense).
icmp type destination-unreachable icmp code frag-needed accept # serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire nos paquets — les transferts se figent.
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } udp dport 53 accept # serveur_powerdns: Résolution DNS interne (zone souveraine). DoT/DoH = feuille de route (chiffrement DNS).
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } tcp dport 53 accept # serveur_powerdns: Résolution DNS interne en TCP (réponses volumineuses, AXFR restreint par allow_axfr_ips).
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
iifname "docker0" accept
oifname "docker0" accept
}
chain output { type filter hook output priority 0; policy accept; }
}

View file

@ -0,0 +1,49 @@
#!/usr/sbin/nft -f
# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote infra-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
ct state established,related accept
ct state invalid drop
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
ip saddr { 10.17.0.0/24 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 192.168.255.2/32 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } tcp dport 22 accept # serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur est filtré à l'OPNsense).
icmp type destination-unreachable icmp code frag-needed accept # serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire nos paquets — les transferts se figent.
tcp dport 80 accept # serveur_nginx: HTTP entrant — redirection permanente vers HTTPS.
tcp dport 443 accept # serveur_nginx: HTTPS entrant — services exposés (terminaison TLS à l'edge).
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } tcp dport 443 accept # serveur_nginx: HTTPS depuis le tenant : les FQDN publiés vivent à l'edge (découverte OIDC, appels inter-services par nom).
ip saddr { 10.17.0.0/24, 192.168.255.2/32 } tcp dport 443 accept # serveur_nginx: HTTPS depuis le reseau d'administration : l'exploitant administre les services par leur interface web, servie par l'edge.
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
iifname "docker0" accept
oifname "docker0" accept
}
chain output { type filter hook output priority 0; policy accept; }
}

View file

@ -0,0 +1,46 @@
#!/usr/sbin/nft -f
# GENERE par scripts/resoudre_flux.py depuis les meta/flux.yml — hote infra-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
ct state established,related accept
ct state invalid drop
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
ip saddr { 10.17.0.0/24 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 192.168.255.2/32 } tcp dport 22 accept # administration (garde anti-lockout, intrant nftables_admin_ssh)
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } tcp dport 22 accept # serveur_debian: Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur est filtré à l'OPNsense).
icmp type destination-unreachable icmp code frag-needed accept # serveur_debian: ICMP « fragmentation nécessaire » entrant : sans lui, un distant ne peut pas nous demander de réduire nos paquets — les transferts se figent.
ip saddr { 10.29.16.11, 10.29.19.11, 10.29.19.21, 10.29.21.11 } tcp dport 8443 accept # serveur_step_ca: ACME + API step-ca : chaque nœud (client_pki) émet/renouvelle ses certificats et récupère la racine.
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
iifname "docker0" accept
oifname "docker0" accept
}
chain output { type filter hook output priority 0; policy accept; }
}

View file

@ -0,0 +1,48 @@
---
# L'AMONT DE LA LIGNEE — comment patient 0 atteint la forge dont il descend.
#
# La forge amont (Alliance Boreale) porte les cinq depots du genome. La forge de
# patient 0 les MIROITE toutes les huit heures, pour que la copie cesse d'etre figee au
# jour du poussage. Ce fichier declare par quelle adresse elle l'atteint.
#
# CE QUI A ETE MESURE LE 2026-08-23, depuis forge-01, dans l'overlay de patient 0 :
#
# ping 100 octets vers 192.168.14.66 -> BLOQUE
# ping 100 octets vers 192.168.12.201 -> BLOQUE
# ping / https vers l'internet -> passe, toutes tailles
#
# Le tenant de patient 0 a le droit de SORTIR sur l'internet et PAS d'entrer dans le
# LAN 192.168.x de l'hebergeur. C'est le default-deny entre les deux mondes, et il est
# correct : un ecosysteme enfant n'a rien a faire dans le reseau physique de son hote.
#
# L'ADRESSE PUBLIQUE, ELLE, REPOND — ET C'EST BIEN LA BONNE FORGE :
#
# curl https://69.70.26.51/api/v1/version -> {"version":"8.0.3+gitea-1.22.0"}
# la forge locale de patient 0 -> {"version":"16.0.2+gitea-1.22.0"}
# 8 essais sur 8 : HTTP 200 en ~6,7 ms
#
# Sept millisecondes vers une adresse publique : le trafic ne sort pas vraiment, il
# revient en epingle au bord du reseau. C'est stable et c'est le chemin qui marche.
#
# POURQUOI EPINGLER PLUTOT QUE LAISSER LE DNS FAIRE. Le DNS public ne rend aujourd'hui
# qu'un seul enregistrement A (69.70.26.51), et les hotes de patient 0 interrogent Quad9
# — la resolution est donc juste par construction. Mais le jour ou un enregistrement a
# horizon partage rendrait l'adresse INTERNE, le miroir s'arreterait SANS BRUIT : la
# poignee TCP vers 192.168.14.66 « reussit » (mesure du meme jour), seule la livraison
# manque. Une copie du genome qui vieillit en silence est exactement le defaut que ce
# depot combat. On epingle donc l'adresse dont on a prouve qu'elle livre.
#
# SI CETTE ADRESSE CHANGE, les miroirs s'arretent — mais bruyamment, et tout le reste de
# l'acces a la forge amont s'arrete en meme temps. C'est un echec visible, pas un silence.
#
# Le plancher /etc/hosts est REGENERE integralement a chaque passage du role
# `hosts_statiques` : une entree posee a la main y serait effacee. Elle doit vivre ici.
hosts_statiques_externes:
- ip: "69.70.26.51"
noms:
- "forge.alliance-boreale.ca"
pourquoi: "forge amont du genome — l'adresse interne est bloquee par la frontiere"
# A SUIVRE, constate en passant le 2026-08-23 : les hotes de patient 0 resolvent par
# 9.9.9.9 (Quad9) et n'interrogent PAS leur propre serveur `infra-dns-01`. L'ecosysteme
# fait donc tourner un resolveur que personne n'utilise. A traiter separement.