Configurer la livraison directe des e-mails entrants pour les sites auto-hébergés avec Mail-Receiver

Comment désactiver exactement la prise en charge de DMARC ?

J’ai ajouté INCLUDE_DMARC: false à la section env de mail-receiver.yml, mais cela ne semble pas fonctionner. Cela semble effectivement empêcher les démons opendkim et opendmarc de s’exécuter (ce qui entraîne un avertissement dans les journaux), mais la vérification SPF est toujours effectuée.

Modifié pour ajouter :
Je pense avoir réussi à désactiver les vérifications SPF en ajoutant également la ligne POSTCONF_ suivante à la section env :

env:
  ...
  INCLUDE_DMARC: false
  POSTCONF_smtpd_recipient_restrictions: check_policy_service unix:private/policy
  ...

J’ai obtenu cela en consultant le commit qui a introduit les vérifications DMARC, et en voyant ce qui devrait se passer lorsque INCLUDE_DMARC est faux.

Je ne sais quasiment rien sur la façon dont les images docker sont construites, mais j’ai l’impression que le drapeau INCLUDE_DMARC est quelque chose qui est censé être défini par quelqu’un d’autre, quelque part ailleurs, à un autre moment — pas quelque chose qui peut être fait dans mail-receiver.yml.

2 « J'aime »

J’ai constaté la nécessité d’ouvrir le port 443 sur ufw — j’ai sinon obtenu API Request Preparation Failed dans les logs. Je pense qu’il est préférable de le mentionner car les instructions d’installation standard mentionnent l’activation de ufw.

Le port 25 est mentionné dans mail-receiver.yml et semble contourner ufw.

1 « J'aime »

Le dépôt GitHub doit-il être dans le fil de discussion ?

3 « J'aime »

Utilisateurs de mail-receiver, veuillez consulter Remove smtp_should_reject & discourse-smtp-fast-rejection

Nous allons supprimer entièrement le rejet rapide car la fonctionnalité d’origine était défectueuse et causait des problèmes aux utilisateurs, en particulier ce genre de chose :

et cela affecte également les e-mails transférés car le test de pré-livraison vérifiait l’envelope-from et l’envelope-to, alors que Discourse n’utilise que les valeurs dans les en-têtes.

1 « J'aime »

Je viens de soumettre cette PR pour supprimer les guillemets inutiles autour de la valeur de DISCOURSE_BASE_URL dans le fichier d’exemple mail-receiver.yml. Les guillemets brisaient ma configuration. Supprimer les guillemets permet de terminer ce document avec succès.

Pouvez-vous expliquer comment ? La présence/absence de guillemets autour de cette valeur ne donne aucune différence :

[2] pry(main)=> YAML::load("env:\n  DISCOURSE_BASE_URL: 'https://discourse.example.com'")
=> {"env"=>{"DISCOURSE_BASE_URL"=>"https://discourse.example.com"}}

[3] pry(main)=> YAML::load("env:\n  DISCOURSE_BASE_URL: https://discourse.example.com")
=> {"env"=>{"DISCOURSE_BASE_URL"=>"https://discourse.example.com"}}

Lorsque je suivais les journaux de ce conteneur et que je lui envoyais des messages, je voyais un tas d’erreurs mentionnant quelque chose comme discourse.example.com ne fait pas partie des enregistrements MX ou quelque chose de ce genre. J’ai supprimé les guillemets, reconstruit le conteneur et cela a commencé à fonctionner :person_shrugging:

La séquence des événements pourrait également jouer un rôle :

  1. J’ai configuré et lancé le conteneur de réception de courrier (mail-receiver)
  2. Quelques jours plus tard, j’ai configuré les enregistrements DNS MX
  3. J’ai validé que les enregistrements MX étaient correctement définis, puis j’ai commencé à tester. Cela ne fonctionnait pas
  4. Suppression des guillemets, reconstruction du conteneur, cela a commencé à fonctionner

Je ne suis donc pas sûr si la résolution était liée à la suppression des guillemets, ou à la reconstruction du conteneur après la création des enregistrements MX.

Dans le pire des cas, la PR rend le fichier yml cohérent :slight_smile:

1 « J'aime »

Il semble qu’il y ait une supposition que le destinataire du courrier sera toujours le même domaine que le forum de base. Lorsque ce n’est pas le cas, comment configurons-nous TLS ?

Par exemple :
forum => forum.domain.tld
mail-receiver => mail.domain.tld

Dans mail-receiver.yml, le TLS pointe vers les certificats du forum de base. Y a-t-il un moyen pour que le destinataire du courrier obtienne ses propres certificats ?

Je n’ai pas la réponse directe, bien que je soupçonne que cela nécessiterait des options supplémentaires dans le fichier yml pour effectuer des modifications dans le conteneur lors de la construction.

Plus d’informations à ce sujet suivront, mais je me demande quelle est votre raison de vouloir l’exécuter sur un domaine différent. Le récepteur de courrier est fortement adapté et, sans modifications, fonctionne exclusivement pour recevoir des e-mails pour une instance Discourse jumelée, il est donc généralement raisonnable de le faire fonctionner sur le même domaine que cette instance.


Si vous examinez certains des modèles à inclure dans votre fichier yml Discourse, dont certains seront déjà utilisés, vous devriez pouvoir obtenir des indices sur la manière d’exécuter des commandes et de modifier des fichiers via le fichier yml (lors de la construction du conteneur).

web.onion.template.yml contient des exemples sur la façon de remplacer des chaînes de caractères dans les fichiers et web.letsencrypt.ssl.template.yml est celui qui ajoute Let’s Encrypt au conteneur Discourse principal.

Je ne sais pas dans quelle mesure cela dépend des éléments de l’image de base, il pourrait donc potentiellement être plus simple de faire en sorte que le conteneur Discourse principal obtienne un deuxième certificat, puis de simplement modifier les chemins de certificat/clé dans mail-receiver.yml pour qu’ils correspondent.

Faites attention à ce type de changements si vous adoptez cette approche, en vous assurant de savoir exactement quel effet le changement aura. Un changement erroné dans les éléments Let’s Encrypt pourrait entraîner un échec silencieux du renouvellement des certificats, par exemple, ce que vous pourriez ne remarquer qu’environ 3 mois plus tard lorsque les visiteurs commenceront à obtenir des erreurs de certificat expiré.

Cas d’utilisation CloudFlare

Ces instructions s’adressent aux forums Discourse auto-hébergés qui utilisent le Proxy Cloudflare.

Lorsque vous utilisez le Proxy Cloudflare, cela empêche tout trafic SMTP (Port 25) d’atteindre votre serveur. Cela vous oblige à configurer un sous-domaine différent pour que le récepteur de courrier fonctionne.

Par exemple, si votre domaine est forums.domain.tld, vous devrez créer un nouveau sous-domaine, tel que mail.domain.tld.

Avec Cloudflare, vous avez les étapes supplémentaires ci-dessous.

  1. Créez l’enregistrement A pour le nouveau sous-domaine. Il utilisera la même adresse IP que votre forums.domain.tld.
  2. Créez l’enregistrement MX pour le nouveau sous-domaine, comme indiqué dans les instructions principales.

Suivez les ensembles d’instructions principaux avec cette modification mineure. Cela fonctionnera très bien avec la sécurité TLS désactivée.

Si vous souhaitez activer la sécurité TLS, cela nécessitera un travail supplémentaire.

Aperçu de la configuration TLS

Ces instructions installeront Certbot et un plugin Certbot pour CloudFlare. Les commandes obtiendront des certificats Let’s Encrypt en mode autonome via le processus de certification DNS. Une fois les certificats disponibles, ils sont copiés dans la zone partagée du récepteur de courrier pour que le conteneur puisse les utiliser. Nous devons utiliser le modèle DNS, car Discourse utilise déjà le port 80.

Défi DNS

Au lieu de prouver la propriété du domaine via HTTP, certbot le prouve en créant un enregistrement TXT dans votre DNS. Puisque votre DNS est Cloudflare, cela peut être entièrement automatisé avec un jeton d’API Cloudflare — pas de port 80, pas d’arrêt du serveur web nécessaire.

Comment ça marche

Certbot → Crée l'enregistrement TXT _acme-challenge.mail.lotuselan.net dans Cloudflare
Let's Encrypt → Consulte cet enregistrement TXT → Valide → Émet le certificat
Certbot → Supprime l'enregistrement TXT

Tout cela se fait sur votre serveur de base, pas à l’intérieur du conteneur discourse.

Configuration

1 — Installez cerbot et le plugin certbot Cloudflare :

bash

apt install certbot python3-certbot-dns-cloudflare -y

2 — Créez un jeton d’API Cloudflare :

  1. Allez dans Cloudflare → Mon Profil → Jetons API → Créer un jeton
  2. Utilisez le modèle « Modifier le DNS de la zone »
  3. Autorisations : Zone → DNS → Modifier
  4. Ressources de zone : Inclure → Zone spécifique → lotuselan.net
  5. Restrictions IP : Configurez uniquement pour autoriser l’adresse IP de votre serveur
  6. Copiez le jeton

3 — Enregistrez le jeton dans un fichier d’informations d’identification :

bash

mkdir -p /etc/letsencrypt/cloudflare
nano /etc/letsencrypt/cloudflare/credentials.ini

Collez :

dns_cloudflare_api_token = VOTRE_JETON_API_CLOUDFLARE

Verrouillez le fichier :

bash

chmod 600 /etc/letsencrypt/cloudflare/credentials.ini

4 — Demandez le certificat :

Mettez à jour la commande suivante avec votre email d’administrateur et votre nom de domaine.

bash

certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
  --non-interactive \
  --agree-tos \
  --email votre_adresse_email@domain.tld \
  -d mail.domain.tld

Dans vos résultats, il devrait y avoir une déclaration indiquant :

Certbot a configuré une tâche planifiée pour renouveler automatiquement ce certificat en arrière-plan.

Certbot configurera une cron pour vérifier l’expiration du certificat deux fois par jour. Il renouvellera les certificats lorsqu’ils auront moins de 30 jours avant d’expirer. Vous pouvez le vérifier en :

# Vérifier si le minuteur systemd est actif (la plupart des systèmes Ubuntu modernes)
systemctl status certbot.timer

# Ou vérifier si une tâche cron a été ajoutée
cat /etc/cron.d/certbot

Vous avez maintenant les certificats TLS sur votre serveur pour le nouveau nom de domaine du récepteur de courrier. Ils ne sont pas à un endroit où ils peuvent être utilisés.

5 — Configurez un script de déploiement pour déplacer les fichiers
Puisque certbot se renouvelle automatiquement, vous n’avez besoin que de votre script pour gérer les parties spécifiques à Discourse — copier les certificats renouvelés et reconstruire le récepteur de courrier. Vous pouvez simplifier considérablement le script en utilisant le hook de déploiement intégré de certbot, qui s’exécute automatiquement après un renouvellement réussi.

Créez un fichier de hook de déploiement :

bash

nano /etc/letsencrypt/renewal-hooks/deploy/mail-receiver-deploy.sh
chmod +x /etc/letsencrypt/renewal-hooks/deploy/mail-receiver-deploy.sh

Collez ceci :

bash

#!/bin/bash
DOMAIN="mail.domain.tld"
DISCOURSE_DIR="/var/discourse"
CERT_SRC="/etc/letsencrypt/live/${DOMAIN}"
CERT_DEST_1="${DISCOURSE_DIR}/shared/mail-receiver/letsencrypt/${DOMAIN}"
CERT_DEST_2="${DISCOURSE_DIR}/shared/mail-receiver/letsencrypt/${DOMAIN}_ecc"
ADMIN_EMAIL="adresse email admin"
LOG_FILE="/var/log/mail-cert-renewal.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "=== Hook de déploiement Certbot déclenché pour ${DOMAIN} ==="

# Copier les certificats (utiliser -L pour résoudre les liens symboliques)
for DEST in "$CERT_DEST_1" "$CERT_DEST_2"; do
    mkdir -p "$DEST"
    cp -L "${CERT_SRC}/fullchain.pem" "${DEST}/fullchain.pem"
    cp -L "${CERT_SRC}/privkey.pem"   "${DEST}/privkey.pem"
    cp -L "${CERT_SRC}/cert.pem"      "${DEST}/cert.pem"
    cp -L "${CERT_SRC}/chain.pem"     "${DEST}/chain.pem"
    chmod 644 "${DEST}/fullchain.pem" "${DEST}/cert.pem" "${DEST}/chain.pem"
    chmod 600 "${DEST}/privkey.pem"
    log "Certificats copiés vers ${DEST}"
done

# Reconstruire le récepteur de courrier
cd "$DISCOURSE_DIR" || { echo "Impossible de cd vers ${DISCOURSE_DIR}" | mail -s "[ÉCHEC] Échec du hook de déploiement de certificat de courrier" "$ADMIN_EMAIL"; exit 1; }
log "Reconstruction du récepteur de courrier..."
if ./launcher rebuild mail-receiver >> "$LOG_FILE" 2>&1; then
    log "mail-receiver reconstruit avec succès"
else
    log "ERREUR : la reconstruction a échoué"
    echo "La reconstruction de mail-receiver a échoué après le renouvellement du certificat. Consultez ${LOG_FILE}" | \
        mail -s "[ÉCHEC] Échec du hook de déploiement de certificat de courrier" "$ADMIN_EMAIL"
    exit 1
fi

log "=== Hook de déploiement terminé avec succès ==="

Aucune tâche cron manuelle n’est nécessaire — certbot orchestre l’ensemble du processus. Le hook de déploiement ne se déclenche que lorsqu’un renouvellement se produit réellement, de sorte que votre récepteur de courrier n’aura pas de reconstructions inutiles les jours où certbot vérifie mais ne renouvelle pas.

Pour tester le hook de renouvellement, exécutez ce qui suit :

bash

bash /etc/letsencrypt/renewal-hooks/deploy/mail-receiver-deploy.sh

Si tout a été correctement configuré, cela va
→ copier les certificats dans les répertoires Discourse
→ reconstruire le récepteur de courrier
→ tout journaliser

6 — Configurez TLS dans mail-receiver.yml

Le principal problème ici semble être que l’enregistrement A pour forum.domain.tld est masqué par le proxy, par opposition au désir explicite d’avoir le serveur de messagerie sur un domaine séparé.

Lors de la négociation de TLS, le nom commun du certificat est comparé au nom d’hôte de l’enregistrement MX, c’est-à-dire le nom d’hôte auquel le client (qui pourrait être un autre serveur de messagerie) essaie de se connecter, plutôt qu’à l’enregistrement A auquel il fait référence. Cela signifie que vous pouvez créer votre enregistrement A mail.domain.tld défini sur DNS Mode Only, puis créer un enregistrement MX pour forum.domain.tld faisant référence à mail.domain.tld et aucune autre étape spéciale n’est nécessaire dans cet arrangement.

Oui, vous pouvez utiliser le mode DNS uniquement pour l’enregistrement A de votre forum principal. L’utilisation de cette approche signifie que vous perdez les capacités de proxy global inversé de CloudFlare. (Ce n’était pas une option pour mon installation Discourse.)

C’est pourquoi la première ligne définissant cette solution est destinée aux sites utilisant le proxy CloudFlare.

Je faisais référence à l’enregistrement A de mail.domain.tld défini sur Mode DNS uniquement plutôt qu’à l’enregistrement A de forum.domain.tld, mais j’ai réalisé que j’interprétais mal la manière dont les clients SMTP authentifient les certificats TLS.

Le comportement que j’observais était un artefact de la méthode opportuniste par défaut qui ne valide pas le nom d’hôte, donc mon affirmation selon laquelle il valide le nom d’hôte de l’enregistrement MX plutôt que la cible de l’enregistrement MX était incorrecte. Cela fonctionnerait dans la plupart des cas, mais pas si DANE ou MTA-STS sont utilisés pour appliquer l’authentification d’identité TLS.

Avoir l’enregistrement A proxied (proxifié) et l’enregistrement MX sur DNS Only (DNS uniquement) ne fonctionne pas. La documentation de CloudFlare indique que tout domaine dont l’enregistrement A est proxifié voit tout le trafic SMTP bloqué.

Je valide ceci avec plusieurs séries de tests. Dès que vous dé-proxifiez l’enregistrement A, les données SMTP circulent. Activez le Proxy, les données SMTP ne circulent jamais. (Les tests ont été effectués en utilisant TELNET sur le port 25.)

Donc, si vous voulez :

  • Que votre forum Discourse utilise les services de proxy de CloudFlare
  • Que votre récepteur de courrier SMTP accepte le courrier

Vous devez avoir des domaines différents pour votre courrier entrant.

Si vous voulez TLS pour votre récepteur de courrier SMTP :

  • Vous devez configurer un LetsEncrypt via la vérification DNS

Les instructions semblent intimidantes, mais il a fallu plus de temps pour rédiger les instructions que pour réellement implémenter la solution.

Ce n’est pas ce que je voulais dire, plus précisément ce que je suggérais était trois enregistrements DNS :
A : forum.domaine.tld → adresse IP de l’hôte (proxy activé)
A : mail.domaine.tld → adresse IP de l’hôte (Mode DNS Seul)
MX : forum.domaine.tldmail.domaine.tld

Cependant, comme mentionné, j’ai réalisé plus tard que cela ne fonctionnerait qu’en mode TLS opportuniste par défaut, cela ne fonctionnera pas si vous (quelqu’un) voulez également activer DANE ou MTA-STS pour appliquer l’authentification d’identité (assurer que le bon serveur est contacté plutôt que de seulement chiffrer le trafic).

Elles semblent très bonnes, faciles à suivre et font tout en dehors du conteneur, il n’y a donc aucun risque que cela casse potentiellement avec les mises à jour de Discourse. J’apprécie particulièrement l’utilisation d’un hook de renouvellement certbot que je ne connaissais pas auparavant.

1 « J'aime »

Notez que cela expose l’adresse IP de votre forum et permet aux gens de contourner les mécanismes de protection de Cloudflare tels que la protection DDoS et le WAF. Il est préférable d’exécuter le récepteur de courrier sur un serveur séparé.

Mon intention initiale était d’exécuter le récepteur de courrier sur un serveur différent pour cette raison. Chaque fois que j’essayais d’exécuter l’application de lancement pour démarrer le récepteur de courrier, elle voulait installer le système Discourse complet. Existe-t-il un moyen simple de lancer et d’exécuter uniquement le récepteur de courrier sur un serveur Docker autonome ?

Je ne connais pas vraiment l’application lanceur car nous gérons notre propre infrastructure, mais mon idée serait de ne pas utiliser l’application lanceur et de la démarrer comme n’importe quel autre conteneur. Les variables d’environnement dont vous avez besoin se trouvent dans le readme.

@Simon_Manning & @RGJ - La présentation de Cloudflare a été mise à jour pour fournir les 3 options principales et leurs compromis. Nous espérons que cela répond aux diverses questions que vous avez soulevées concernant les premières options présentées.

@kelv Vous pourriez envisager d’ajouter une note en bas de page dans la description principale de la présentation de Cloudflare. Cela fera gagner du temps aux personnes concernées. Configure direct-delivery incoming email for self-hosted sites with Mail-Receiver - #541 by LotusJeff

2 « J'aime »