feat(stacks): migrate mail-relay
This commit is contained in:
96
stacks/mail-relay/README.md
Normal file
96
stacks/mail-relay/README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# mail-relay — relais SMTP interne
|
||||
|
||||
Postfix en mode *satellite* : il n'accepte du courrier que depuis le réseau Docker
|
||||
`smtp` et le réexpédie au relais du fournisseur, authentifié avec un compte d'envoi.
|
||||
|
||||
Pas de FQDN, pas de routeur Traefik, pas de port publié, pas de volume : le dossier
|
||||
porte donc le nom du service et non un domaine, comme `stacks/traefik/`.
|
||||
|
||||
## Contrat avec les autres stacks — à ne pas casser
|
||||
|
||||
| Élément | Valeur | Pourquoi c'est figé |
|
||||
|---|---|---|
|
||||
| Réseau Docker | `smtp` | déclaré `external: true` par tous les consommateurs |
|
||||
| Nom d'hôte | `postfix` | valeur de `SMTP_HOST` chez les consommateurs |
|
||||
| Port | `25` | sans TLS ni authentification, réseau interne uniquement |
|
||||
|
||||
Le nom d'hôte `postfix` vient du **nom de service** Compose, pas du nom de projet :
|
||||
renommer le dossier ne le change pas. Renommer le *service*, en revanche, casserait
|
||||
tous les consommateurs en silence — les mails cesseraient de partir sans qu'aucun
|
||||
conteneur ne tombe.
|
||||
|
||||
Consommateurs : `vault.opytex.org`, `authentik`, `matrix_poneyworld`.
|
||||
|
||||
## Le réseau `smtp` est externe
|
||||
|
||||
Il n'est pas créé par cette stack, pour qu'un `docker compose down` ici ne tente pas
|
||||
de supprimer un réseau auquel d'autres stacks sont attachées. À créer une seule fois,
|
||||
s'il n'existe pas déjà :
|
||||
|
||||
```bash
|
||||
docker network ls --filter name=^smtp$ --format '{{.Name}}'
|
||||
docker network create smtp # seulement si la commande ci-dessus ne renvoie rien
|
||||
```
|
||||
|
||||
## Déploiement
|
||||
|
||||
```bash
|
||||
cd ~/infra/stacks/mail-relay
|
||||
sops exec-env secrets.env.yaml 'docker compose up -d'
|
||||
```
|
||||
|
||||
Après une modification d'un secret, `up -d` répond « up to date » et garde l'ancienne
|
||||
valeur. Il faut forcer :
|
||||
|
||||
```bash
|
||||
sops exec-env secrets.env.yaml 'docker compose up -d --force-recreate'
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
`env_file` porte le relais amont et l'identité annoncée (`SMTP_SERVER`, `SMTP_PORT`,
|
||||
`SERVER_HOSTNAME`, `OVERWRITE_FROM`) ; `secrets.env.yaml` porte les identifiants du
|
||||
compte d'envoi (`SMTP_USERNAME`, `SMTP_PASSWORD`), édités avec `sops edit`.
|
||||
|
||||
Les noms sont ceux que l'image lit directement. Pour voir ce que le conteneur a
|
||||
réellement reçu :
|
||||
|
||||
```bash
|
||||
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' postfix \
|
||||
| grep -E 'SMTP|SERVER|OVERWRITE'
|
||||
```
|
||||
|
||||
Un `SMTP_USERNAME` ou `SMTP_PASSWORD` vide signale un `sops exec-env` oublié.
|
||||
|
||||
## Vérifier que le relais fonctionne
|
||||
|
||||
```bash
|
||||
# le nom d'hôte résout depuis un consommateur
|
||||
docker exec vaultwarden getent hosts postfix
|
||||
|
||||
# envoi de bout en bout
|
||||
docker exec -i postfix sh -c 'sendmail -t' <<'EOF'
|
||||
To: destinataire@example.org
|
||||
Subject: test relais
|
||||
|
||||
test
|
||||
EOF
|
||||
|
||||
# file d'attente et journal
|
||||
docker exec postfix mailq
|
||||
docker logs --tail 50 postfix
|
||||
```
|
||||
|
||||
Une file d'attente qui ne se vide pas, ou un `status=deferred` dans le journal, pointe
|
||||
l'authentification auprès du relais amont.
|
||||
|
||||
## Points d'attention
|
||||
|
||||
- **La file d'attente n'est pas persistée.** `/var/spool/postfix` vit dans la couche
|
||||
d'écriture du conteneur : un `docker compose up --force-recreate` perd les messages
|
||||
non encore remis. Acceptable pour des notifications, à revoir si le relais transporte
|
||||
un jour du courrier qui compte.
|
||||
- **Aucune sauvegarde.** Pas de volume, rien à restaurer : la stack se reconstruit
|
||||
entièrement depuis ce dossier plus les secrets.
|
||||
- Le relais fait confiance à tout ce qui est joint au réseau `smtp`. Ne pas y attacher
|
||||
un service exposé qui n'a pas besoin d'envoyer du courrier.
|
||||
22
stacks/mail-relay/docker-compose.yml
Normal file
22
stacks/mail-relay/docker-compose.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
postfix:
|
||||
image: juanluisbaptiste/postfix:1.9.0
|
||||
container_name: postfix
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
env_file:
|
||||
- env_file
|
||||
environment:
|
||||
SMTP_USERNAME: ${SMTP_USERNAME:?}
|
||||
SMTP_PASSWORD: ${SMTP_PASSWORD:?}
|
||||
networks:
|
||||
- smtp
|
||||
|
||||
networks:
|
||||
# Créé hors stack (`docker network create smtp`) et déclaré `external` par
|
||||
# tous les consommateurs. Le garder externe ici aussi évite qu'un
|
||||
# `docker compose down` sur ce relais tente de supprimer un réseau auquel
|
||||
# d'autres stacks sont attachées.
|
||||
smtp:
|
||||
external: true
|
||||
21
stacks/mail-relay/env_file
Normal file
21
stacks/mail-relay/env_file
Normal file
@@ -0,0 +1,21 @@
|
||||
# =============================================================================
|
||||
# MAIL-RELAY (postfix) — configuration NON sensible
|
||||
# =============================================================================
|
||||
# Relais SMTP interne de l'infra. Aucun port publié, aucun routeur Traefik :
|
||||
# il n'est joignable que sur le réseau Docker `smtp`, sous le nom d'hôte
|
||||
# `postfix`, port 25, sans authentification ni TLS.
|
||||
#
|
||||
# Les noms de variables ci-dessous sont ceux attendus par l'image
|
||||
# juanluisbaptiste/postfix, consommés directement.
|
||||
#
|
||||
# SERVER_HOSTNAME : nom annoncé dans le HELO/EHLO, doit correspondre au SPF.
|
||||
# OVERWRITE_FROM : réécrit l'expéditeur de tous les messages relayés.
|
||||
#
|
||||
# Les identifiants du compte d'envoi (SMTP_USERNAME, SMTP_PASSWORD) sont dans
|
||||
# secrets.env.yaml et injectés par `sops exec-env`.
|
||||
# =============================================================================
|
||||
|
||||
SMTP_SERVER=smtp.mailfence.com
|
||||
SMTP_PORT=465
|
||||
SERVER_HOSTNAME=opytex.org
|
||||
OVERWRITE_FROM=noreply@opytex.org
|
||||
26
stacks/mail-relay/secrets.env.yaml
Normal file
26
stacks/mail-relay/secrets.env.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
SMTP_USERNAME: ENC[AES256_GCM,data:+7Kp5g9BMDo=,iv:HwSc6EfVkaipNbEKsdSYKKqu2zmKVdfGdwxmF3DeBcU=,tag:HPdOovDuvWh9+5OUep61HQ==,type:str]
|
||||
SMTP_PASSWORD: ENC[AES256_GCM,data:nkxxgMlpwjoauzF9,iv:aMB9u7i5zVOoZaCCndiV+h43oxT2FiN+uTgo6fU/6/c=,tag:HE6V9Xtly6vcnWIM4fAtIQ==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBueXNlTFdaRWxHUjBvU0Nv
|
||||
MFRJQXZobURqdWRJK0dLSmp2WEx5NDMyWFhnCnp5a1ZIN2pSbHQ2aDBVdjV1UUVu
|
||||
MWNqQjloQXV4QUZoVVZDeUJvMUkvQ00KLS0tIDdSeVA2b1FqVWhHeDdrTDV5bXZ4
|
||||
T3ZIcVlGdUp1cXBETWxTRTZuVzVUdkEKFiEf3srsmNQND/dAftYWZWhzBZEVJDVN
|
||||
C7sWjY+VoaUu/kLmkpSkvlU1AkMyiHuIGbFJcZVWkTo7/+nfDqd60Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age18m2zl3pwgw97djshzcf4wmhrqxksl4erqse6edkpxpkg5rcz0ujs8u4c7s
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXTXM5S0M5cE1mZXJqdWlC
|
||||
ckx4QVE3YkplUkdUcjBDVHJMZ1lCUHJ0N0Q4CkZyUG9PclNQS1FTbTJySG1WQkRG
|
||||
Vm1RdE0wN1BDc0YwdUtmYzBneGZHWVEKLS0tIDg5NzczYkR5YkpldUFtNHRUekxk
|
||||
eUxHY2NTM09kRXQ1L3pkd3MwTlhTU0UKlNWwpdlqqwo7r3+gUZN4GJ3Qarr0DkFS
|
||||
XUJApSBn8vYCG02BRGDBkKlm6KdqspHl36fgsB2pmtZm+oTIhG+1OQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1fh70nny5hzz8a8g9077kgfca0lhlh3wsjf4fjrv4klx5ucdtpdmsazuqdj
|
||||
lastmodified: "2026-08-17T04:51:05Z"
|
||||
mac: ENC[AES256_GCM,data:Eoha31ZU3Rh4+BynONCTvz8/2PWcJ2bb8FQN1DlADnay2RcNKTa2rKkdipINLmVsMDV6BlWj+4RgQP2aVEL7XJmMVPjNKlun6tMClsT69NvJIYvlw9HbfEpXdw8U8SsNNATB2cyQ9Imayu+Zb2IWa/4GnzUUfe3PQai3rih3Yt8=,iv:0Ejixjh4A0Op/0+UpQdcsXktwwtXQoweYrPEqfVQwB0=,tag:ln4E7z6LJX2QV8rGTXIW/A==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.3
|
||||
Reference in New Issue
Block a user