Настоящая почта: docker-compose.public.yml (MX, DKIM, Let's Encrypt через lego), сертификат перечитывается без перезапуска
continuous-integration/drone/pr Build is passing
continuous-integration/drone/pr Build is passing
- ReloadingKeyManager: сертификат и ключ из PEM (PKCS#8, RSA, EC/SEC1 как у lego), раз в минуту проверяет файлы и перечитывает их; битый файл оставляет прежний сертификат; поколения по алиасам. - docker-compose.public.yml: порты 25/587/465/993 на публичном адресе ВМ, OUTBOUND_MODE=mx, DKIM, SPF/DMARC, регистрация закрыта, без ловушки; сервис acme (lego v5.5.2, HTTP-01 на :80, EC256, продление за 30 дней, хук кладёт пару в том tls для uid 10001). - README: раздел «Настоящая почта», DNS-записи, перечитка сертификата. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
# Real mail on the internet (v0.2.3 75.6): receives mail for MAIL_DOMAINS and sends it straight to the recipients'
|
||||
# MX servers (port 25), DKIM-signed. From the ready image, like docker-compose.image.yml.
|
||||
#
|
||||
# Needs a VM of its own with a public address (Jambotron 60.8: «Публичный адрес» of the VM, SMTP «да» — port 25
|
||||
# is closed by default) — the ports below are published on that address:
|
||||
# 25 SMTP: mail from other servers (STARTTLS) and from mail programs
|
||||
# 587 submission: mail programs (STARTTLS + login) — the same SMTP listener as 25
|
||||
# 465 SMTPS: mail programs, implicit TLS
|
||||
# 993 IMAPS: mail programs, implicit TLS
|
||||
# 80 only for Let's Encrypt (HTTP-01) — service "acme"
|
||||
# The web client + API (service "mail", port 8080) goes through Jambotron's Caddy under ANOTHER name
|
||||
# (webmail.<domain>): the name MAIL_HOSTNAME points at the VM's public address, not at Caddy.
|
||||
#
|
||||
# DNS before the first start (the certificate is issued over HTTP on MAIL_HOSTNAME):
|
||||
# A mail.<domain> → public address of the VM
|
||||
# PTR public address → mail.<domain> (hoster's panel: Reverse DNS)
|
||||
# MX <domain> → 10 mail.<domain>
|
||||
# TXT <domain> → "v=spf1 ip4:<public address> -all"
|
||||
# TXT _dmarc.<domain> → "v=DMARC1; p=none; rua=mailto:postmaster@<domain>"
|
||||
# TXT mail._domainkey.<domain> → printed in the "mail" log on the first start (DKIM key, kept in the data volume)
|
||||
#
|
||||
# Mailbox admin@<domain>: its password is in the "mail" log on the first start and in /app/data/initial-passwords.txt.
|
||||
# Registration is closed: new mailboxes only by the admin. No catch-all.
|
||||
name: reactive-mail-public
|
||||
|
||||
services:
|
||||
secrets-init:
|
||||
image: alpine:3.20
|
||||
restart: "no"
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
umask 077
|
||||
for name in DB_PASSWORD JWT_SECRET; do
|
||||
[ -s /secrets/$$name ] || head -c 48 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' > /secrets/$$name
|
||||
done
|
||||
chmod 644 /secrets/DB_PASSWORD /secrets/JWT_SECRET
|
||||
volumes:
|
||||
- secrets:/secrets
|
||||
|
||||
# Let's Encrypt for MAIL_HOSTNAME: issued on the first start, renewed 30 days before the end (checked twice a day).
|
||||
# The deploy hook copies the pair to /tls (readable by the "mail" user 10001); the server reads it again within
|
||||
# a minute — no restart
|
||||
acme:
|
||||
image: goacme/lego:v5.5.2
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
entrypoint:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
cat > /usr/local/bin/jb-deploy <<'EOF'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cp "$$LEGO_HOOK_CERT_PATH" /tls/cert.pem.new
|
||||
cp "$$LEGO_HOOK_CERT_KEY_PATH" /tls/key.pem.new
|
||||
chown 10001 /tls/cert.pem.new /tls/key.pem.new
|
||||
chmod 600 /tls/key.pem.new
|
||||
chmod 644 /tls/cert.pem.new
|
||||
mv /tls/key.pem.new /tls/key.pem
|
||||
mv /tls/cert.pem.new /tls/cert.pem
|
||||
echo "certificate for $$LEGO_HOOK_CERT_DOMAINS copied to /tls"
|
||||
EOF
|
||||
chmod 755 /usr/local/bin/jb-deploy
|
||||
while true; do
|
||||
/lego run --accept-tos --email "$$ACME_EMAIL" --domains "$$MAIL_HOSTNAME" --key-type EC256 \
|
||||
--http --http.address :80 --path /var/lib/lego --renew-days 30 --no-random-sleep \
|
||||
--deploy-hook /usr/local/bin/jb-deploy || echo "lego failed — next try in 1 hour"
|
||||
[ -s /tls/cert.pem ] && sleep 43200 || sleep 3600
|
||||
done
|
||||
environment:
|
||||
MAIL_HOSTNAME: ${MAIL_HOSTNAME:?MAIL_HOSTNAME, e.g. mail.jambocheck.com}
|
||||
ACME_EMAIL: ${ACME_EMAIL:-postmaster@${MAIL_DOMAINS}}
|
||||
LEGO_LOG_FORMAT: text
|
||||
volumes:
|
||||
- lego:/var/lib/lego
|
||||
- tls:/tls
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "test -s /tls/cert.pem && test -s /tls/key.pem"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
start_period: 10m
|
||||
retries: 3
|
||||
mem_limit: 128m
|
||||
|
||||
db:
|
||||
image: postgres:17
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
secrets-init:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
POSTGRES_DB: mailserver
|
||||
POSTGRES_USER: mail
|
||||
POSTGRES_PASSWORD_FILE: /run/mail-secrets/DB_PASSWORD
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- secrets:/run/mail-secrets:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mail -d mailserver"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
mem_limit: 256m
|
||||
|
||||
mail:
|
||||
image: gitea.jambotron.com/jambotron_gang/reactive-mail-server:${MAIL_IMAGE_TAG:-latest}
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
acme:
|
||||
condition: service_healthy # waits for the first certificate
|
||||
ports:
|
||||
- "25:3025"
|
||||
- "587:3025"
|
||||
- "465:3465"
|
||||
- "993:3993"
|
||||
expose:
|
||||
- "8080" # web client + API → webmail.<domain> in Jambotron
|
||||
environment:
|
||||
DB_HOST: db
|
||||
MAILSERVER_SECRETS_DIR: /run/mail-secrets
|
||||
MAIL_DOMAINS: ${MAIL_DOMAINS:?MAIL_DOMAINS, e.g. jambocheck.com}
|
||||
MAIL_HOSTNAME: ${MAIL_HOSTNAME:?MAIL_HOSTNAME, e.g. mail.jambocheck.com}
|
||||
MAIL_TLS_CERT: /tls/cert.pem
|
||||
MAIL_TLS_KEY: /tls/key.pem
|
||||
SMTPS_PORT: "3465"
|
||||
IMAPS_PORT: "3993"
|
||||
DKIM_DOMAIN: ${MAIL_DOMAINS}
|
||||
DKIM_SELECTOR: mail
|
||||
DKIM_KEYS_DIR: /app/data/dkim-keys
|
||||
MAIL_CATCH_ALL: ""
|
||||
MAIL_REGISTRATION: "false"
|
||||
OUTBOUND_MODE: mx
|
||||
SMTP_AUTH_WITHOUT_TLS: "false"
|
||||
IMAP_AUTH_WITHOUT_TLS: "false"
|
||||
SPF_ENABLED: "true"
|
||||
DMARC_ENABLED: "true"
|
||||
volumes:
|
||||
- data:/app/data
|
||||
- secrets:/run/mail-secrets:ro
|
||||
- tls:/tls:ro
|
||||
mem_limit: 512m
|
||||
|
||||
volumes:
|
||||
secrets:
|
||||
pgdata:
|
||||
data:
|
||||
lego:
|
||||
tls:
|
||||
Reference in New Issue
Block a user