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:
parent
a4b25018e6
commit
9480e585a5
11 changed files with 284 additions and 0 deletions
|
|
@ -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),
|
||||
|
|
|
|||
15
playbooks/groupes/serveur_postfix.yml
Normal file
15
playbooks/groupes/serveur_postfix.yml
Normal 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
|
||||
31
roles/serveur_postfix/defaults/main.yml
Normal file
31
roles/serveur_postfix/defaults/main.yml
Normal 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"
|
||||
6
roles/serveur_postfix/handlers/main.yml
Normal file
6
roles/serveur_postfix/handlers/main.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Recharger postfix
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ serveur_postfix_service }}"
|
||||
state: reloaded
|
||||
when: not ansible_check_mode
|
||||
8
roles/serveur_postfix/meta/empreinte.yml
Normal file
8
roles/serveur_postfix/meta/empreinte.yml
Normal 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
|
||||
137
roles/serveur_postfix/tasks/main.yml
Normal file
137
roles/serveur_postfix/tasks/main.yml
Normal 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
|
||||
10
roles/serveur_postfix/templates/ldap-mailboxes.cf.j2
Normal file
10
roles/serveur_postfix/templates/ldap-mailboxes.cf.j2
Normal 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
|
||||
34
roles/serveur_postfix/templates/main.cf.j2
Normal file
34
roles/serveur_postfix/templates/main.cf.j2
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in a new issue