--- # Devis du courriel — RELEVE seul. Ne modifie rien (D-23). # # La chaine est longue — Postfix -> LDAP -> LMTP -> Dovecot -> IMAP — et chaque maillon # peut casser sans que le suivant s'en apercoive. On interroge donc chaque maillon la # ou il dit la verite : `postmap -q` pour la resolution LDAP de Postfix, `doveadm user` # pour celle de Dovecot (c'est precisement la ou la livraison avait bloque), et une vraie # conversation SMTP pour ce que le serveur accepte. # # make courriel-plan - name: Devis du courriel — Postfix hosts: serveur_postfix become: true gather_facts: false tasks: - name: Résoudre la connexion à l'annuaire ansible.builtin.include_role: name: resoudre_annuaire - name: Choisir un compte réel comme sonde community.general.ldap_search: dn: "{{ resoudre_annuaire_users_dn }}" scope: onelevel filter: "(objectClass=inetOrgPerson)" attrs: ["uid"] server_uri: "{{ resoudre_annuaire_uri }}" bind_dn: "{{ resoudre_annuaire_bind_dn }}" bind_pw: "{{ resoudre_annuaire_bind_password }}" register: devis_courriel_comptes changed_when: false no_log: true delegate_to: "{{ groups['serveur_openldap'] | first }}" - name: Retenir l'adresse sonde ansible.builtin.set_fact: devis_courriel_sonde: "{{ (devis_courriel_comptes.results | first).uid }}@{{ domaine_interne }}" - name: Relever Postfix (résolution LDAP, portée du relais, dialecte SMTP) ansible.builtin.shell: executable: /bin/bash cmd: | set -uo pipefail echo "SONDE={{ devis_courriel_sonde }}" echo "TROUVE=$(postmap -q '{{ devis_courriel_sonde }}' \ ldap:/etc/postfix/ldap-mailboxes.cf 2>/dev/null)" # Une adresse qui n'existe pas DOIT rester sans reponse : sinon la boite est # un fourre-tout et n'importe quel nom serait accepte. echo "FANTOME=$(postmap -q 'nexistepas-{{ 999 | random }}@{{ domaine_interne }}' \ ldap:/etc/postfix/ldap-mailboxes.cf 2>/dev/null)" echo "MYNETWORKS=$(postconf -h mynetworks)" # La file d'attente est le seul endroit qui dit si le courrier BOUGE. Sans ce # releve, le devis a annonce CONFORME pendant que toute livraison interne # etait differee sur un echec TLS — resolution correcte, dialectes corrects, # et rien qui arrive. Constate le 2026-08-08. echo "DIFFERES=$(mailq 2>/dev/null | grep -cE '^[0-9A-F]{8,}' || echo 0)" echo "RAISONS<<" mailq 2>/dev/null | grep -E '^\(' | head -3 echo ">>" for P in 25 587; do echo "PORT=$P" exec 3<>/dev/tcp/127.0.0.1/$P || continue read -r -t 5 _ <&3 printf 'EHLO devis.local\r\n' >&3 while read -r -t 5 L <&3; do echo " $L" [ "${L:3:1}" = " " ] && break done printf 'QUIT\r\n' >&3 exec 3<&- 3>&- done register: devis_courriel_postfix changed_when: false - name: Déposer le relevé Postfix ansible.builtin.copy: dest: "{{ playbook_dir }}/../../instance/devis-courriel.json.postfix" mode: "0600" content: >- {{ {'hote': inventory_hostname, 'role': 'postfix', 'supernet': setops_supernet | default(''), 'brut': devis_courriel_postfix.stdout} | to_nice_json }} delegate_to: localhost become: false - name: Devis du courriel — Dovecot hosts: serveur_dovecot become: true gather_facts: false tasks: - name: Relever Dovecot (résolution userdb et dialecte IMAP) ansible.builtin.shell: executable: /bin/bash cmd: | set -uo pipefail SONDE="{{ hostvars[groups['serveur_postfix'] | first]['devis_courriel_sonde'] }}" echo "SONDE=$SONDE" # `doveadm user` est le maillon exact ou la livraison avait bloque : c'est lui # qui dit si Dovecot sait ou ecrire pour cette adresse. echo "USERDB<<" doveadm user "$SONDE" 2>&1 | head -12 echo ">>" echo "PORT=143" exec 3<>/dev/tcp/127.0.0.1/143 || exit 0 read -r -t 5 BANNIERE <&3 echo " $BANNIERE" printf 'a CAPABILITY\r\n' >&3 while read -r -t 5 L <&3; do echo " $L" case "$L" in a\ *) break ;; esac done printf 'b LOGOUT\r\n' >&3 register: devis_courriel_dovecot changed_when: false - name: Déposer le relevé Dovecot ansible.builtin.copy: dest: "{{ playbook_dir }}/../../instance/devis-courriel.json.dovecot" mode: "0600" content: >- {{ {'hote': inventory_hostname, 'role': 'dovecot', 'brut': devis_courriel_dovecot.stdout} | to_nice_json }} delegate_to: localhost become: false