frontière : la sortie générale est déclarée, pas subie
Le devis se terminait par `block out log all` avec une seule règle sortante (le relais SMTP). Appliqué tel quel, il coupait la flotte d'Internet : plus d'apt, plus de NTP, plus de récursion DNS. Rien ne le signalait — la ligne la plus lourde de conséquences du devis, posée à la suite des autres. La sortie est déclarée dans le registre, donc dérivée : - `serveur_debian` (socle, 14 hôtes) : 443 et 80 pour les dépôts apt, 123/udp pour l'horloge — une dérive fait échouer step-ca et le SSO des semaines après la cause ; - `client_unbound` : 53 udp et tcp, la récursion depuis la racine qu'implique `client_unbound_transitaires: []`. Le TCP est le repli obligatoire dès qu'une réponse DNSSEC dépasse la taille UDP. Le devis passe de 6 à 11 règles, et sa section 5 énonce le default-deny sortant, le nombre de règles qui l'accompagnent, et où déclarer un besoin oublié — jamais à la main dans le pare-feu. Vérifié : les nftables d'hôte sont inchangés, octet pour octet. Le pair `externe` reste sauté par resoudre_flux. Non déclaré volontairement : le rôle `chrony` n'est référencé par aucun groupe ni playbook — un flux pour lui aurait été une règle morte. Preuves : 24 OK, 0 échec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
9804573f12
commit
4d37570c0c
7 changed files with 93 additions and 9 deletions
27
CHANGELOG.md
27
CHANGELOG.md
|
|
@ -1,5 +1,32 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-08-02 (suite) — la sortie générale est déclarée, pas subie
|
||||
|
||||
Le devis frontière se terminait par `block out log all` avec **une seule** règle sortante
|
||||
(le relais SMTP). Appliqué tel quel, il coupait la flotte d'Internet : plus de `apt`, plus
|
||||
de NTP, plus de récursion DNS. Rien ne le signalait — c'était la ligne la plus lourde de
|
||||
conséquences du devis, posée à la suite des autres.
|
||||
|
||||
La sortie est désormais **déclarée dans le registre**, donc dérivée comme tout le reste :
|
||||
|
||||
- `serveur_debian` (le socle, porté par les 14 hôtes) — `443/tcp` et `80/tcp` pour les
|
||||
dépôts apt, `123/udp` pour l'horloge. Une dérive d'horloge fait échouer la validation des
|
||||
certificats step-ca et le SSO, des semaines après la cause.
|
||||
- `client_unbound` — `53/udp` et `53/tcp` : `client_unbound_transitaires` est vide, donc
|
||||
Unbound interroge lui-même la racine. C'est le choix souverain ; il a un coût réseau qu'il
|
||||
faut déclarer. Le TCP n'est pas optionnel — c'est le repli obligatoire dès qu'une réponse
|
||||
DNSSEC dépasse la taille UDP.
|
||||
|
||||
Le devis passe de 6 à 11 règles, et sa section 5 **énonce** le default-deny sortant, le
|
||||
nombre de règles qui l'accompagnent, et où déclarer un besoin oublié — jamais à la main dans
|
||||
le pare-feu, la règle serait perdue à la génération suivante.
|
||||
|
||||
Vérifié : les nftables d'hôte sont **inchangés**, octet pour octet. Le pair `externe` reste
|
||||
sauté par `resoudre_flux.py` — ces flux relèvent de la bordure, et d'elle seule.
|
||||
|
||||
Non déclaré volontairement : le rôle `chrony` existe mais n'est référencé par aucun groupe,
|
||||
aucun playbook ni le graphe de dépendances. Lui écrire un flux aurait créé une règle morte.
|
||||
|
||||
## 2026-08-02 — les interfaces se nomment par leur identifiant
|
||||
|
||||
`opt1`, `igb1` et `TENANTS` désignent le même port dans OPNsense : l'identifiant interne, le
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. |
|
||||
| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check). |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 30 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 29 rôles, 63 flux, schéma + matrice OK. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 29 rôles, 68 flux, schéma + matrice OK. |
|
||||
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
|
||||
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | playbook: playbooks/proxmox/cloner_vm_debian.yml |
|
||||
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
| P21 | Federation : aucun index en collision | AFF-001 | ✅ OK | Federation coherente : 2 instance(s) federee(s), aucun index en collision. |
|
||||
| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (19 sections). |
|
||||
| P23 | Underlay sans collision avec la plage tenant | AFF-001 | ✅ OK | Underlay conforme : 5 reseau(x), aucune collision avec la plage tenant. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-001 | ✅ OK | CONFORME : frontiere nord/sud, 6 regles, 2 routes, admin=192.168.255.0/24. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-001 | ✅ OK | CONFORME : frontiere nord/sud, 11 regles, 2 routes, admin=192.168.255.0/24. |
|
||||
|
||||
## Couverture des affirmations ✅ du registre
|
||||
|
||||
|
|
|
|||
|
|
@ -231,7 +231,9 @@ ansible-vault edit instance/inventories/principal/group_vars/all/vault.yml
|
|||
`binardat` n'a pas été confrontée au matériel. À vérifier sur un `show running-config`.
|
||||
- **Les ports physiques restent à nommer** — `<PORT-VERS-PROXMOX>`, `<PORT-VERS-FRONTIERE>`
|
||||
et `<PORT-TRUNK>` ; rien dans le modèle ne peut les deviner.
|
||||
- **La sortie générale** (mises à jour apt, ACME) n'est pas déclarée dans le registre : les
|
||||
hôtes sortent aujourd'hui parce que la politique `output` des nftables est `accept` et que
|
||||
l'ACL de switch se termine par `permit ip <tenant> any`. À trancher : la déclarer
|
||||
explicitement, ou l'assumer comme politique par défaut de la bordure.
|
||||
- ~~**La sortie générale** n'est pas déclarée~~ — **réglé le 2026-08-02.** Elle est
|
||||
déclarée dans le registre, donc dérivée comme le reste : `serveur_debian` (le socle, porté
|
||||
par tous les hôtes) déclare 443, 80 et 123/udp — dépôts apt et horloge ; `client_unbound`
|
||||
déclare 53 en UDP et TCP, la récursion depuis la racine que le choix souverain implique.
|
||||
Le `block out` de la section 5 est donc un vrai default-deny **assumé**, et le devis
|
||||
l'énonce désormais au lieu de le poser en silence.
|
||||
|
|
|
|||
|
|
@ -13,10 +13,15 @@
|
|||
| `client_smtp` | egress | 25 | tcp | serveur_postfix | starttls | Relais des notifications locales vers le MTA central (Postfix), STARTTLS. |
|
||||
| `client_unbound` | ingress | 53 | udp | localhost | clair | Résolveur local sur boucle locale (les processus du nœud interrogent 127.0.0.1). |
|
||||
| `client_unbound` | egress | 53 | tcp | serveur_powerdns | clair | Transfert des requêtes de la zone souveraine vers le DNS autoritatif interne (PowerDNS). |
|
||||
| `client_unbound` | egress | 53 | udp | externe | clair | Récursion DNS depuis la racine (UDP d'abord), validée par DNSSEC — la confidentialité du transport n'est pas l'enjeu, l'authenticité l'est. |
|
||||
| `client_unbound` | egress | 53 | tcp | externe | clair | Récursion DNS en TCP : repli obligatoire quand la réponse dépasse la taille UDP (fréquent avec DNSSEC). |
|
||||
| `serveur_backup` | ingress | 22 | tcp | client_backup | ssh | Dépôt restic servi par SSH (utilisateur restreint restic + clé) ; chaque client_backup pousse ses instantanés. |
|
||||
| `serveur_collabora` | ingress | 9980 | tcp | edge | clair | Éditeur servi au navigateur via l'edge (WebSocket WOPI ; TLS terminé à l'edge). |
|
||||
| `serveur_collabora` | ingress | 9980 | tcp | localhost | clair | Vérifications WOPI serveur→Collabora depuis Nextcloud co-localisé. |
|
||||
| `serveur_debian` | ingress | 22 | tcp | flotte, externe | ssh | Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur est filtré à l'OPNsense). |
|
||||
| `serveur_debian` | egress | 80 | tcp | externe | clair | Dépôts apt en clair et redirections HTTP des miroirs (l'intégrité vient de la signature des paquets, pas du transport). |
|
||||
| `serveur_debian` | egress | 123 | udp | externe | n-a | Synchronisation d'horloge (NTP). Une dérive fait échouer la validation des certificats step-ca et l'authentification SSO. |
|
||||
| `serveur_debian` | egress | 443 | tcp | externe | tls-requis | Dépôts apt en HTTPS (Debian, Smallstep, Grafana, Icinga, Forgejo, Nextcloud) — sans quoi aucun correctif de sécurité n'entre. |
|
||||
| `serveur_dovecot` | ingress | 24 | tcp | serveur_postfix | tls-requis | Remise LMTP depuis Postfix (edge-mta -> mailstore), en TLS vérifié (lmtp_tls_security_level=verify). |
|
||||
| `serveur_dovecot` | ingress | 993 | tcp | externe | tls-requis | Accès courriel des utilisateurs (IMAPS). Frontière publique gérée à l'OPNsense. |
|
||||
| `serveur_dovecot` | ingress | 12345 | tcp | serveur_postfix | tls | Authentification SASL déléguée : Postfix valide les identifiants de soumission contre Dovecot. |
|
||||
|
|
@ -72,9 +77,10 @@
|
|||
|
||||
## Synthèse chiffrement
|
||||
|
||||
- **clair** : 23 flux
|
||||
- **clair** : 26 flux
|
||||
- **n-a** : 1 flux
|
||||
- **ssh** : 3 flux
|
||||
- **starttls** : 6 flux
|
||||
- **tls** : 8 flux
|
||||
- **tls-cible** : 2 flux
|
||||
- **tls-requis** : 21 flux
|
||||
- **tls-requis** : 22 flux
|
||||
|
|
|
|||
|
|
@ -13,3 +13,20 @@ flux:
|
|||
pair: serveur_powerdns
|
||||
chiffrement: clair
|
||||
raison: "Transfert des requêtes de la zone souveraine vers le DNS autoritatif interne (PowerDNS)."
|
||||
|
||||
# Récursion sortante : `client_unbound_transitaires` est vide par défaut, donc Unbound
|
||||
# interroge lui-même la racine puis les autoritatifs — c'est le choix souverain, sans
|
||||
# dépendre du résolveur d'un fournisseur. Renseigner des transitaires rendrait ces deux
|
||||
# flux inutiles, mais les laisser déclarés ne coûte rien et évite une panne muette.
|
||||
- sens: egress
|
||||
port: 53
|
||||
protocole: udp
|
||||
pair: externe
|
||||
chiffrement: clair
|
||||
raison: "Récursion DNS depuis la racine (UDP d'abord), validée par DNSSEC — la confidentialité du transport n'est pas l'enjeu, l'authenticité l'est."
|
||||
- sens: egress
|
||||
port: 53
|
||||
protocole: tcp
|
||||
pair: externe
|
||||
chiffrement: clair
|
||||
raison: "Récursion DNS en TCP : repli obligatoire quand la réponse dépasse la taille UDP (fréquent avec DNSSEC)."
|
||||
|
|
|
|||
|
|
@ -8,3 +8,26 @@ flux:
|
|||
pair: [flotte, externe]
|
||||
chiffrement: ssh
|
||||
raison: "Plan de gestion : administration et déploiement Ansible par SSH (inter-nœud ; l'accès depuis l'extérieur est filtré à l'OPNsense)."
|
||||
|
||||
# --- Sortie générale de la flotte ---
|
||||
# Portée par le socle parce qu'elle vaut pour TOUS les hôtes, sans exception.
|
||||
# Non déclarée, la frontière la bloquerait : son `block out` final est un vrai
|
||||
# default-deny, et la flotte perdrait ses mises à jour et son horloge en silence.
|
||||
- sens: egress
|
||||
port: 443
|
||||
protocole: tcp
|
||||
pair: externe
|
||||
chiffrement: tls-requis
|
||||
raison: "Dépôts apt en HTTPS (Debian, Smallstep, Grafana, Icinga, Forgejo, Nextcloud) — sans quoi aucun correctif de sécurité n'entre."
|
||||
- sens: egress
|
||||
port: 80
|
||||
protocole: tcp
|
||||
pair: externe
|
||||
chiffrement: clair
|
||||
raison: "Dépôts apt en clair et redirections HTTP des miroirs (l'intégrité vient de la signature des paquets, pas du transport)."
|
||||
- sens: egress
|
||||
port: 123
|
||||
protocole: udp
|
||||
pair: externe
|
||||
chiffrement: n-a
|
||||
raison: "Synchronisation d'horloge (NTP). Une dérive fait échouer la validation des certificats step-ca et l'authentification SSO."
|
||||
|
|
|
|||
|
|
@ -299,9 +299,18 @@ def rendre(devis: dict) -> str:
|
|||
f"# {rg['role']} ({rg['chiffrement']})"
|
||||
)
|
||||
out.append(f"# {rg['raison']}")
|
||||
sortants = sum(1 for r in devis["regles"] if r["sens"] == "out")
|
||||
out += [
|
||||
"",
|
||||
"## 5. Defaut",
|
||||
"## 5. Defaut — DENY DANS LES DEUX SENS",
|
||||
"# `block out` est un vrai default-deny : ce qui n'est pas declare en section 4",
|
||||
f"# ne sort PAS. {sortants} regle(s) sortante(s) y figurent, toutes derivees du",
|
||||
"# registre des flux (`sens: egress`, `pair: externe`).",
|
||||
"#",
|
||||
"# Un besoin sortant oublie ne se manifeste pas par un refus clair : apt expire,",
|
||||
"# l'horloge derive et les certificats deviennent invalides des semaines plus tard.",
|
||||
"# Le declarer dans `roles/<role>/meta/flux.yml` — ne jamais l'ouvrir ici a la main,",
|
||||
"# la regle serait perdue a la prochaine generation.",
|
||||
"block in log all # tout ce qui n'est pas ci-dessus",
|
||||
"block out log all",
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in a new issue