diff --git a/CHANGELOG.md b/CHANGELOG.md index d2051ec..63d3042 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # CHANGELOG — Set-OPS +## 2026-10-12 (139) — Le courriel de la forge du site est chiffré de bout en bout, et le relais vérifie qui il joint + +**95 preuves.** `make verifier` passe ; `test_relais_du_site.py` vérifie aussi les réglages TLS. +Déployé au site après simulation. + +Directive de Daniel : **toutes les données en transit sont chiffrées, à l'interne comme à +l'externe.** Premier flux traité : celui qu'on venait d'ouvrir. + +- **La forge exige STARTTLS** (`PROTOCOL = smtp+starttls`) et vérifie le certificat du relais. + `smtp` seul remettait en clair ; Forgejo le signalait au démarrage, et ne le signale plus. +- **Le relais du site exige le TLS** en entrée (`encrypt`) et **vérifie** le serveur qu'il joint + en sortie (`verify`, autorités publiques du système). Il n'écrit qu'à quelques destinataires + connus : un MX au certificat invalide fait refuser le message plutôt que de le livrer. + `smtp_tls_CAfile` et non `CApath` : le démon d'envoi tourne en chroot ; le fichier est lu avant + d'y entrer, le dossier serait une copie que Debian refait au redémarrage. +- **Chaque session TLS s'écrit au journal** (`smtpd_tls_loglevel`, `smtp_tls_loglevel`) : ce qui se + dit chiffré se mesure. +- Le serveur de courriel **complet** d'un locataire reste en `may` : un MX public qui exige le + TLS refuse le courrier des serveurs qui ne l'offrent pas (RFC 3207). À trancher à l'Étape B. + +Mesuré sur un envoi réel (« mot de passe oublié », 02:21) : forge → relais en TLS 1.3, relais → +`mx.chezlepro.ca` en TLS 1.3, remis (250). Avant `verify`, la sortie était « Untrusted » ; +`posttls-finger` en `verify` rend « Verified » pour `mx.chezlepro.ca` et Gmail. + +Reste du chantier : le registre déclare 52 flux `clair` et 6 `starttls`, dont des étiquettes +antérieures au passage des métriques et des journaux en HTTPS. Chaque flux sera mesuré avant +d'être corrigé ; le plus gros écart réel attendu est edge → applications. + ## 2026-10-12 (138) — La forge du site remet ses courriels **95 preuves.** `make verifier` passe ; `test_relais_du_site.py` est nouveau, et échoue sur diff --git a/roles/serveur_forgejo/templates/app.ini.j2 b/roles/serveur_forgejo/templates/app.ini.j2 index ac0d49c..9395401 100644 --- a/roles/serveur_forgejo/templates/app.ini.j2 +++ b/roles/serveur_forgejo/templates/app.ini.j2 @@ -69,7 +69,10 @@ ACCOUNT_LINKING = auto [mailer] ENABLED = {{ 'true' if serveur_forgejo_smtp else 'false' }} -PROTOCOL = smtp +{# STARTTLS EXIGE, CERTIFICAT VERIFIE (2026-10-12). `smtp` seul remettait en clair : + Forgejo le signalait (« insecure SMTP protocol to non-local address »). Le relais + porte un certificat de l'autorite de l'ecosysteme, que la machine reconnait. #} +PROTOCOL = smtp+starttls SMTP_ADDR = {{ serveur_forgejo_smtp }} SMTP_PORT = 25 FROM = forge@{{ domaine_interne }} diff --git a/roles/serveur_postfix/defaults/main.yml b/roles/serveur_postfix/defaults/main.yml index bc45287..6a12327 100644 --- a/roles/serveur_postfix/defaults/main.yml +++ b/roles/serveur_postfix/defaults/main.yml @@ -59,6 +59,26 @@ serveur_postfix_tls_source_cert: "/etc/step/certs/{{ ansible_fqdn | default(ansi serveur_postfix_tls_source_cle: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.key" serveur_postfix_tls_dir: "/etc/postfix/tls" +# TOUT CE QUI TRANSITE EST CHIFFRE, A L'INTERNE COMME A L'EXTERNE (directive du 2026-10-12). +# +# Un RELAIS ne parle qu'a des emetteurs de son ecosysteme (la forge) et a des MX distants : +# il peut EXIGER le TLS des deux cotes. `may` laissait une forge sans STARTTLS remettre en +# clair, sans que rien ne le dise. Le serveur de courriel COMPLET d'un locataire reste en +# `may` pour l'instant : un MX public qui exige le TLS refuse le courrier des serveurs qui ne +# l'offrent pas (RFC 3207) — c'est une decision de l'Etape B, pas un reglage par defaut. +serveur_postfix_tls_entrant: "{{ 'encrypt' if serveur_postfix_mode == 'relais' else 'may' }}" +# EN SORTIE, LE RELAIS VERIFIE AUSSI QUI IL JOINT (2026-10-12) : `encrypt` chiffrait vers un +# serveur dont il ne verifiait pas l'identite (« Untrusted » au journal). Il n'ecrit qu'a +# quelques destinataires connus ; un MX au certificat invalide fait refuser le message au lieu +# de le livrer, et c'est voulu. Mesure avant : mx.chezlepro.ca et Gmail « Verified ». +serveur_postfix_tls_sortant: "{{ 'verify' if serveur_postfix_mode == 'relais' else 'may' }}" +# Les autorites PUBLIQUES du systeme : celles des MX de l'Internet, pas celle de l'ecosysteme. +# UN FICHIER, PAS UN DOSSIER : `smtp` tourne en chroot ; `CAfile` est lu AVANT d'y entrer, +# `CApath` dedans — sur une copie que Debian refait au redemarrage, et qui vieillit entre deux. +serveur_postfix_tls_ca_publiques: "/etc/ssl/certs/ca-certificates.crt" +# Une ligne par session TLS au journal : ce qui se dit chiffre doit pouvoir se MESURER. +serveur_postfix_tls_journal: 1 + # Zero-confiance : verifier le TLS de la livraison LMTP vers Dovecot (cert step-ca). serveur_postfix_lmtp_tls_verify: false serveur_postfix_lmtp_tls_cafile: "/etc/step/certs/root_ca.crt" diff --git a/roles/serveur_postfix/templates/main.cf.j2 b/roles/serveur_postfix/templates/main.cf.j2 index 78531ef..5eee101 100644 --- a/roles/serveur_postfix/templates/main.cf.j2 +++ b/roles/serveur_postfix/templates/main.cf.j2 @@ -39,9 +39,14 @@ lmtp_host_lookup = native # --- TLS (certificat step_ca) --- smtpd_tls_cert_file = {{ serveur_postfix_tls_dir }}/cert.pem smtpd_tls_key_file = {{ serveur_postfix_tls_dir }}/key.pem -smtpd_tls_security_level = may +smtpd_tls_security_level = {{ serveur_postfix_tls_entrant }} smtpd_tls_protocols = >=TLSv1.2, !SSLv2, !SSLv3 -smtp_tls_security_level = may +smtpd_tls_mandatory_protocols = >=TLSv1.2 +smtpd_tls_loglevel = {{ serveur_postfix_tls_journal }} +smtp_tls_security_level = {{ serveur_postfix_tls_sortant }} +smtp_tls_CAfile = {{ serveur_postfix_tls_ca_publiques }} +smtp_tls_mandatory_protocols = >=TLSv1.2 +smtp_tls_loglevel = {{ serveur_postfix_tls_journal }} {% if serveur_postfix_lmtp_tls_verify | default(false) %} # Zero-confiance : livraison LMTP vers Dovecot chiffree ET verifiee (cert step-ca). # `wrappermode` = TLS des la connexion : c'est ce que sait offrir un `inet_listener` diff --git a/scripts/tests/test_relais_du_site.py b/scripts/tests/test_relais_du_site.py index 99ed935..518ffb7 100644 --- a/scripts/tests/test_relais_du_site.py +++ b/scripts/tests/test_relais_du_site.py @@ -29,6 +29,25 @@ def main() -> int: if "infra-mail-01" in bloc or "groups['serveur_postfix']" not in bloc: echecs.append("serveur_forgejo_smtp ne derive pas du groupe `serveur_postfix`") + # 1b. Tout ce qui transite est chiffre (directive du 2026-10-12) : la forge exige STARTTLS, + # le relais exige le TLS des deux cotes et le journalise. + ini = (RACINE / "roles/serveur_forgejo/templates/app.ini.j2").read_text(encoding="utf-8") + if "PROTOCOL = smtp+starttls" not in ini: + echecs.append("la forge n'exige pas STARTTLS (`PROTOCOL = smtp+starttls`)") + for cle, niveau in (("serveur_postfix_tls_entrant", "encrypt"), + ("serveur_postfix_tls_sortant", "verify")): + ligne = next((l for l in (RACINE / "roles/serveur_postfix/defaults/main.yml") + .read_text(encoding="utf-8").splitlines() if l.startswith(cle + ":")), "") + if f"'{niveau}' if serveur_postfix_mode == 'relais'" not in ligne: + echecs.append(f"{cle} : un relais n'exige pas `{niveau}`") + gabarit = (RACINE / "roles/serveur_postfix/templates/main.cf.j2").read_text(encoding="utf-8") + for attendu in ("smtpd_tls_security_level = {{ serveur_postfix_tls_entrant }}", + "smtp_tls_security_level = {{ serveur_postfix_tls_sortant }}", + "smtpd_tls_loglevel = {{ serveur_postfix_tls_journal }}", + "smtp_tls_CAfile = {{ serveur_postfix_tls_ca_publiques }}"): + if attendu not in gabarit: + echecs.append(f"main.cf.j2 ne porte pas `{attendu}`") + # 2. Le relais admet les emetteurs declares, et eux seuls (pur). serveurs = {"forge": {"ip": "10.0.33.11"}, "edge": {"ip": "10.0.37.11"}, "mon": {"ip": "10.0.36.11"}, "vieille": {"ip": "10.0.33.99", "etat": "retire"}}