serveur_postfix (Postfix 3.x) : MTA edge-mta — cartes LDAP + LMTP + TLS step_ca

Réception :25, validation des boîtes par carte LDAP (attribut mail),
remise LMTP réseau vers le mail-store Dovecot (virtual_transport lmtp),
TLS via step_ca (pont de cert), aucune boîte locale. main.cf +
ldap-mailboxes.cf, validé par postfix check. Bind LDAP: vault_openldap_admin.

Validé statiquement (ansible-lint, syntax, rendu main.cf) ; déploiement à suivre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-07-02 15:34:24 -04:00
parent a4b25018e6
commit 9480e585a5
11 changed files with 284 additions and 0 deletions

View file

@ -19,6 +19,12 @@
mis à jour. Réévaluer Stalwart ~2028.
### Ajouté
- **Rôle `serveur_postfix` (Postfix 3.x) — MTA du nœud edge-mta.** Réception `:25`, cartes
**LDAP** (validation des boîtes via l'attribut `mail`), remise **LMTP réseau** vers le nœud
mail-store Dovecot (`virtual_transport = lmtp:inet:[…]:24`), TLS via **step_ca** (pont de
cert), aucune boîte locale. Config `main.cf` + carte `ldap-mailboxes.cf`, validée par
`postfix check`. Secret de bind : `vault_openldap_admin`. Nécessite `serveur_postfix_mailstore_hote`
(FQDN du mail-store). Validé statiquement ; déploiement réel à suivre.
- **Rôle `serveur_dovecot` (Dovecot 2.4) — déployé et prouvé.** IMAP `:993`/`:143` +
LMTP, **auth/annuaire LDAP** (vers `serveur_openldap`, filtre `mail`), stockage Maildir
(user système `vmail`), **TLS via step_ca** (pont de cert + resync au renouvellement),

View file

@ -0,0 +1,15 @@
---
- name: Appliquer le groupe serveur_postfix
hosts: serveur_postfix
become: true
gather_facts: true
pre_tasks:
- name: Vérifier que la cible est Debian
ansible.builtin.assert:
that:
- ansible_facts.distribution == "Debian"
fail_msg: "Ce playbook est prévu pour Debian."
roles:
- serveur_postfix

View file

@ -0,0 +1,31 @@
---
# Postfix 3.x (Debian 13) — MTA du nœud edge-mta : réception :25, cartes LDAP (domaines,
# boîtes, alias), remise LMTP réseau vers le nœud mail-store (Dovecot). TLS via step_ca.
# NE STOCKE AUCUNE BOÎTE. Cf. docs/courriel-conception.md + docs/identite-sso.md.
serveur_postfix_paquets:
- postfix
- postfix-ldap
serveur_postfix_service: "postfix"
serveur_postfix_domaine: "{{ domaine_interne }}"
serveur_postfix_nom_hote: "{{ ansible_fqdn | default(ansible_hostname) }}"
# Réseaux internes de confiance (relais autorisé). À adapter au réseau de l'instance.
serveur_postfix_reseaux_confiance: "127.0.0.0/8 [::1]/128"
# --- Nœud mail-store (Dovecot) : remise LMTP réseau ---
# OBLIGATOIRE : le FQDN/nom du nœud qui héberge les boîtes (Dovecot LMTP).
serveur_postfix_mailstore_hote: ""
serveur_postfix_lmtp_port: 24
# --- Annuaire LDAP (validation domaines/boîtes/alias) ---
serveur_postfix_ldap_serveur: "ldaps://id-ldap-01.{{ domaine_interne }}:636"
serveur_postfix_ldap_base: "ou=people,dc={{ domaine_interne.split('.') | join(',dc=') }}"
serveur_postfix_ldap_bind_dn: "cn=admin,dc={{ domaine_interne.split('.') | join(',dc=') }}"
serveur_postfix_ldap_bind_password: "{{ vault_openldap_admin | default('') }}"
# --- TLS via le certificat d'hôte step_ca (client_pki) ---
serveur_postfix_tls_actif: true
serveur_postfix_tls_source_cert: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.crt"
serveur_postfix_tls_source_cle: "/etc/step/certs/{{ ansible_fqdn | default(ansible_hostname) }}.key"
serveur_postfix_tls_dir: "/etc/postfix/tls"

View file

@ -0,0 +1,6 @@
---
- name: Recharger postfix
ansible.builtin.systemd:
name: "{{ serveur_postfix_service }}"
state: reloaded
when: not ansible_check_mode

View file

@ -0,0 +1,8 @@
---
# Empreinte ressources du logiciel — dimensionnement VM derive (scripts/instancier.py).
# Sommee au socle SE + marge pour estimer coeurs/RAM/disque de la VM hote.
# Override possible par hote dans le plan (serveurs.yml: coeurs/memoire/disque).
setops_empreinte:
coeurs: 1
memoire_mo: 512
disque_go: 3

View file

@ -0,0 +1,137 @@
---
- name: Exiger le mot de passe de bind LDAP (Vault)
ansible.builtin.assert:
that:
- serveur_postfix_ldap_bind_password | length > 0
fail_msg: "serveur_postfix_ldap_bind_password requis (via Ansible Vault, ex. vault_openldap_admin)."
- name: Exiger le nœud mail-store (remise LMTP)
ansible.builtin.assert:
that:
- serveur_postfix_mailstore_hote | length > 0
fail_msg: "serveur_postfix_mailstore_hote requis (FQDN du nœud Dovecot qui héberge les boîtes)."
- name: Installer debconf-utils (requis par le module debconf)
ansible.builtin.apt:
name: debconf-utils
state: present
update_cache: true
cache_valid_time: 3600
- name: Préconfigurer Postfix (debconf, installation non interactive)
ansible.builtin.debconf:
name: postfix
question: "{{ item.question }}"
vtype: "{{ item.vtype }}"
value: "{{ item.value }}"
loop:
- { question: "postfix/main_mailer_type", vtype: "select", value: "Internet Site" }
- { question: "postfix/mailname", vtype: "string", value: "{{ serveur_postfix_domaine }}" }
loop_control:
label: "{{ item.question }}"
- name: Installer Postfix
ansible.builtin.apt:
name: "{{ serveur_postfix_paquets }}"
state: present
update_cache: true
# --- Pont de certificat step_ca (déposé par client_pki) ---
- name: TLS — créer le répertoire des certificats
ansible.builtin.file:
path: "{{ serveur_postfix_tls_dir }}"
state: directory
owner: root
group: root
mode: "0755"
when: serveur_postfix_tls_actif | bool
- name: TLS — déployer le script de synchronisation du certificat
ansible.builtin.template:
src: setops-postfix-cert-sync.sh.j2
dest: /usr/local/sbin/setops-postfix-cert-sync
owner: root
group: root
mode: "0755"
when: serveur_postfix_tls_actif | bool
- name: TLS — déployer les unités de synchronisation (service + path)
ansible.builtin.template:
src: "{{ item.s }}"
dest: "{{ item.d }}"
owner: root
group: root
mode: "0644"
loop:
- { s: "setops-postfix-cert-sync.service.j2", d: "/etc/systemd/system/setops-postfix-cert-sync.service" }
- { s: "setops-postfix-cert-sync.path.j2", d: "/etc/systemd/system/setops-postfix-cert-sync.path" }
loop_control:
label: "{{ item.d | basename }}"
when: serveur_postfix_tls_actif | bool
- name: TLS — synchroniser le certificat maintenant
ansible.builtin.command: /usr/local/sbin/setops-postfix-cert-sync
changed_when: false
when:
- serveur_postfix_tls_actif | bool
- not ansible_check_mode
- name: TLS — activer la surveillance du certificat (renouvellement)
ansible.builtin.systemd:
name: setops-postfix-cert-sync.path
enabled: true
state: started
daemon_reload: true
when:
- serveur_postfix_tls_actif | bool
- not ansible_check_mode
- name: TLS — vérifier que le certificat est en place
ansible.builtin.stat:
path: "{{ serveur_postfix_tls_dir }}/cert.pem"
register: serveur_postfix_cert
when: serveur_postfix_tls_actif | bool
- name: TLS — exiger le certificat (intégration client_pki requise sur ce nœud)
ansible.builtin.assert:
that:
- serveur_postfix_cert.stat.exists | default(false)
fail_msg: >-
Aucun certificat step_ca ({{ serveur_postfix_tls_source_cert }}). Le nœud Postfix doit
porter l'intégration client_pki. Déploie-la, puis relance.
when:
- serveur_postfix_tls_actif | bool
- not ansible_check_mode
# --- Configuration ---
- name: Déployer la carte LDAP des boîtes
ansible.builtin.template:
src: ldap-mailboxes.cf.j2
dest: /etc/postfix/ldap-mailboxes.cf
owner: root
group: postfix
mode: "0640"
no_log: true # contient le mot de passe de bind LDAP
notify: Recharger postfix
- name: Déployer main.cf
ansible.builtin.template:
src: main.cf.j2
dest: /etc/postfix/main.cf
owner: root
group: root
mode: "0644"
notify: Recharger postfix
- name: Valider la configuration Postfix
ansible.builtin.command: postfix check
changed_when: false
when: not ansible_check_mode
- name: Activer et démarrer Postfix
when: not ansible_check_mode
ansible.builtin.systemd:
name: "{{ serveur_postfix_service }}"
enabled: true
state: started
daemon_reload: true

View file

@ -0,0 +1,10 @@
# Géré par Set-OPS (rôle serveur_postfix). Ne pas éditer à la main.
# Carte LDAP : valide qu'une adresse correspond à une boîte (attribut mail).
server_host = {{ serveur_postfix_ldap_serveur }}
search_base = {{ serveur_postfix_ldap_base }}
query_filter = (mail=%s)
result_attribute = mail
bind = yes
bind_dn = {{ serveur_postfix_ldap_bind_dn }}
bind_pw = {{ serveur_postfix_ldap_bind_password }}
version = 3

View file

@ -0,0 +1,34 @@
# Géré par Set-OPS (rôle serveur_postfix). Ne pas éditer à la main.
compatibility_level = 3.6
myhostname = {{ serveur_postfix_nom_hote }}
mydomain = {{ serveur_postfix_domaine }}
myorigin = $mydomain
inet_interfaces = all
inet_protocols = ipv4
biff = no
append_dot_mydomain = no
readme_directory = no
smtpd_banner = $myhostname ESMTP
mynetworks = {{ serveur_postfix_reseaux_confiance }}
# Aucune remise locale : ce MTA ne fait QUE des boîtes virtuelles (via LDAP + LMTP).
mydestination =
local_recipient_maps =
# --- Boîtes virtuelles : validation LDAP + remise LMTP vers le nœud mail-store (Dovecot) ---
virtual_mailbox_domains = {{ serveur_postfix_domaine }}
virtual_mailbox_maps = ldap:/etc/postfix/ldap-mailboxes.cf
virtual_transport = lmtp:inet:[{{ serveur_postfix_mailstore_hote }}]:{{ serveur_postfix_lmtp_port }}
{% if serveur_postfix_tls_actif %}
# --- 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_protocols = >=TLSv1.2, !SSLv2, !SSLv3
smtp_tls_security_level = may
{% endif %}
# --- Restrictions de base ---
smtpd_helo_required = yes
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination

View file

@ -0,0 +1,10 @@
# Géré par Set-OPS (rôle serveur_postfix). Ne pas éditer à la main.
[Unit]
Description=Set-OPS — surveille le certificat step_ca de Postfix
[Path]
PathChanged={{ serveur_postfix_tls_source_cert }}
Unit=setops-postfix-cert-sync.service
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,8 @@
# Géré par Set-OPS (rôle serveur_postfix). Ne pas éditer à la main.
[Unit]
Description=Set-OPS — synchronise le certificat step_ca vers Postfix
After=postfix.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/setops-postfix-cert-sync

View file

@ -0,0 +1,19 @@
#!/bin/sh
# Géré par Set-OPS (rôle serveur_postfix). Ne pas éditer à la main.
# Synchronise le certificat step_ca (client_pki, root:root 600) vers Postfix, puis recharge.
# Idempotent ; appelé au déploiement ET au renouvellement (unité path). La clé est lisible
# par le groupe postfix (smtpd tourne sous postfix).
set -eu
SRC_CERT="{{ serveur_postfix_tls_source_cert }}"
SRC_KEY="{{ serveur_postfix_tls_source_cle }}"
DST="{{ serveur_postfix_tls_dir }}"
[ -f "$SRC_CERT" ] && [ -f "$SRC_KEY" ] || exit 0
install -o root -g root -m 0644 "$SRC_CERT" "$DST/cert.pem"
install -o root -g postfix -m 0640 "$SRC_KEY" "$DST/key.pem"
if systemctl --quiet is-active postfix 2>/dev/null; then
systemctl reload postfix 2>/dev/null || systemctl restart postfix
fi