Set-OPS-Public/playbooks/maintenance/devis-courriel.yml
Daniel Allaire aac2d35e06 devis courriel et identite : ils nomment le compte du service qu ils verifient
Depuis le 13 septembre, resoudre_annuaire exige un compte par consommateur ;
ces deux devis retombaient sur inconnu et echouaient avant toute connexion.
courriel-plan et identite-plan : CONFORME.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 15:54:01 -04:00

133 lines
5.3 KiB
YAML

---
# 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
# LE COMPTE DU SERVICE QU'ON VERIFIE (2026-09-28). Depuis le 2026-09-13, chaque
# consommateur de l'annuaire nomme SON compte ; ce devis n'en nommait aucun et
# retombait sur `inconnu` — la cible echouait sur son assertion, avant toute
# connexion. Il voit l'annuaire comme Postfix le voit : avec le compte de Postfix.
vars:
resoudre_annuaire_bind_cn: "postfix"
resoudre_annuaire_secret: "vault_ldap_bind_postfix"
- 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