courriel du site : chiffre de bout en bout, et le relais verifie qui il joint
La forge exige STARTTLS et verifie le certificat du relais. Le relais du site exige le TLS en entree, verifie le serveur joint en sortie (autorites publiques, lues avant le chroot), et journalise chaque session TLS. Mesure sur un envoi reel : TLS 1.3 sur les deux troncons, remis. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
73fd941780
commit
203f7c5436
5 changed files with 78 additions and 3 deletions
28
CHANGELOG.md
28
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
|
||||
|
|
|
|||
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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"}}
|
||||
|
|
|
|||
Loading…
Reference in a new issue