fix: update mailserver configuration to sync Traefik certificates and adjust SSL settings
This commit is contained in:
Executable
+65
@@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ACME_JSON_PATH="${1:-/var/data/config/traefik/acme.json}"
|
||||||
|
MAIL_DOMAIN="${2:-mail.furyhawk.lol}"
|
||||||
|
TARGET_DIR="/var/data/docker-mailserver/ssl"
|
||||||
|
|
||||||
|
if [ ! -f "$ACME_JSON_PATH" ]; then
|
||||||
|
echo "ACME file not found: $ACME_JSON_PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$TARGET_DIR"
|
||||||
|
|
||||||
|
python3 - "$ACME_JSON_PATH" "$MAIL_DOMAIN" "$TARGET_DIR" <<'PY'
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
acme_path, mail_domain, target_dir = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
|
|
||||||
|
with open(acme_path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
cert_entries = data.get("letsencrypt", {}).get("Certificates", [])
|
||||||
|
if not cert_entries:
|
||||||
|
raise SystemExit("No certificates found in acme.json")
|
||||||
|
|
||||||
|
selected = None
|
||||||
|
for entry in cert_entries:
|
||||||
|
domain = entry.get("domain", {})
|
||||||
|
main = domain.get("main", "")
|
||||||
|
sans = domain.get("sans", []) or []
|
||||||
|
if main == mail_domain or mail_domain in sans:
|
||||||
|
selected = entry
|
||||||
|
break
|
||||||
|
|
||||||
|
if selected is None:
|
||||||
|
raise SystemExit(f"No certificate found for domain: {mail_domain}")
|
||||||
|
|
||||||
|
cert_b64 = selected.get("certificate")
|
||||||
|
key_b64 = selected.get("key")
|
||||||
|
if not cert_b64 or not key_b64:
|
||||||
|
raise SystemExit("Selected certificate entry is missing certificate/key data")
|
||||||
|
|
||||||
|
cert_pem = base64.b64decode(cert_b64)
|
||||||
|
key_pem = base64.b64decode(key_b64)
|
||||||
|
|
||||||
|
cert_path = os.path.join(target_dir, "fullchain.pem")
|
||||||
|
key_path = os.path.join(target_dir, "privkey.pem")
|
||||||
|
|
||||||
|
with open(cert_path, "wb") as f:
|
||||||
|
f.write(cert_pem)
|
||||||
|
with open(key_path, "wb") as f:
|
||||||
|
f.write(key_pem)
|
||||||
|
|
||||||
|
os.chmod(cert_path, 0o600)
|
||||||
|
os.chmod(key_path, 0o600)
|
||||||
|
|
||||||
|
print(f"Wrote {cert_path}")
|
||||||
|
print(f"Wrote {key_path}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
echo "Certificate sync complete for $MAIL_DOMAIN"
|
||||||
@@ -9,7 +9,7 @@ services:
|
|||||||
- mail_state:/var/mail-state
|
- mail_state:/var/mail-state
|
||||||
- mail_logs:/var/log/mail
|
- mail_logs:/var/log/mail
|
||||||
- mail_config:/tmp/docker-mailserver
|
- mail_config:/tmp/docker-mailserver
|
||||||
- /var/data/config/acme.json:/etc/letsencrypt/acme.json:ro
|
- /var/data/docker-mailserver/ssl:/tmp/docker-mailserver/ssl:ro
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
- traefik-public
|
- traefik-public
|
||||||
|
|||||||
+27
-23
@@ -1,19 +1,30 @@
|
|||||||
# Self-hosted Mail Server (Docker Swarm)
|
# Self-hosted Mail Server (Docker Swarm)
|
||||||
|
|
||||||
This stack uses Docker Mailserver and is deployed with:
|
This stack uses Docker Mailserver and can reuse the certificate issued by Traefik.
|
||||||
|
|
||||||
make deploy-mailserver
|
|
||||||
|
|
||||||
## 1) Configure mail domain
|
## 1) Configure mail domain
|
||||||
|
|
||||||
Edit swarm/mailserver/docker-mailserver.env:
|
Edit [swarm/mailserver/docker-mailserver.env](swarm/mailserver/docker-mailserver.env):
|
||||||
|
|
||||||
- OVERRIDE_HOSTNAME: mail host FQDN (example: mail.example.com)
|
- OVERRIDE_HOSTNAME: mail host FQDN (example: mail.example.com)
|
||||||
- OVERRIDE_DOMAINNAME: root mail domain (example: example.com)
|
- OVERRIDE_DOMAINNAME: root mail domain (example: example.com)
|
||||||
- POSTMASTER_ADDRESS: postmaster mailbox
|
- POSTMASTER_ADDRESS: postmaster mailbox
|
||||||
- SSL_DOMAIN: certificate domain for mail TLS
|
|
||||||
|
|
||||||
## 2) DNS records (required)
|
## 2) Sync Traefik cert for mailserver
|
||||||
|
|
||||||
|
Traefik stores certificates in `/var/data/config/traefik/acme.json`.
|
||||||
|
Mailserver needs cert files (`fullchain.pem` and `privkey.pem`).
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
`./scripts/mailserver-sync-traefik-cert.sh /var/data/config/traefik/acme.json mail.example.com`
|
||||||
|
|
||||||
|
This writes:
|
||||||
|
|
||||||
|
- `/var/data/docker-mailserver/ssl/fullchain.pem`
|
||||||
|
- `/var/data/docker-mailserver/ssl/privkey.pem`
|
||||||
|
|
||||||
|
## 3) DNS records (required)
|
||||||
|
|
||||||
Create these DNS records for your mail domain:
|
Create these DNS records for your mail domain:
|
||||||
|
|
||||||
@@ -23,7 +34,7 @@ Create these DNS records for your mail domain:
|
|||||||
- DKIM TXT: mail._domainkey.example.com -> generated DKIM key
|
- DKIM TXT: mail._domainkey.example.com -> generated DKIM key
|
||||||
- DMARC TXT: _dmarc.example.com -> v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com
|
- DMARC TXT: _dmarc.example.com -> v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com
|
||||||
|
|
||||||
## 3) Open ports
|
## 4) Open ports
|
||||||
|
|
||||||
Ensure inbound TCP is open to your Traefik manager node for:
|
Ensure inbound TCP is open to your Traefik manager node for:
|
||||||
|
|
||||||
@@ -32,27 +43,20 @@ Ensure inbound TCP is open to your Traefik manager node for:
|
|||||||
- 993 (IMAPS)
|
- 993 (IMAPS)
|
||||||
- 995 (POP3S)
|
- 995 (POP3S)
|
||||||
|
|
||||||
Mail ports are exposed by the Traefik service in [swarm/core.yml](swarm/core.yml), then routed to the mail service using TCP labels in [swarm/mailserver.yml](swarm/mailserver.yml).
|
Mail ports are exposed by [swarm/core.yml](swarm/core.yml) and routed to [swarm/mailserver.yml](swarm/mailserver.yml) via TCP labels.
|
||||||
|
|
||||||
## 4) Deploy
|
## 5) Deploy
|
||||||
|
|
||||||
make deploy-mailserver
|
`make deploy-mailserver`
|
||||||
|
|
||||||
## 5) Create mailbox accounts
|
## 6) Create mailbox accounts
|
||||||
|
|
||||||
Use the Docker Mailserver setup helper from a manager node:
|
`docker exec -it $(docker ps --filter name=mailserver_mail --format '{{.ID}}' | head -n 1) setup email add user@example.com 'StrongPasswordHere'`
|
||||||
|
|
||||||
docker exec -it $(docker ps --filter name=mailserver_mail --format '{{.ID}}' | head -n 1) setup email add user@example.com 'StrongPasswordHere'
|
`docker exec -it $(docker ps --filter name=mailserver_mail --format '{{.ID}}' | head -n 1) setup alias add postmaster@example.com user@example.com`
|
||||||
|
|
||||||
docker exec -it $(docker ps --filter name=mailserver_mail --format '{{.ID}}' | head -n 1) setup alias add postmaster@example.com user@example.com
|
## 7) Verify
|
||||||
|
|
||||||
## 6) Verify
|
- Check service status: `docker service ls | grep mailserver`
|
||||||
|
- Check logs: `docker service logs -f mailserver_mail`
|
||||||
|
|
||||||
- Check service status: docker service ls | grep mailserver
|
|
||||||
- Check logs: docker service logs -f mailserver_mail
|
|
||||||
- Send a test from an external mailbox and confirm delivery.
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Mail data is persisted in named volumes: mail_data, mail_state, mail_logs, mail_config.
|
|
||||||
- For high deliverability, add PTR/rDNS with your server provider and keep SPF/DKIM/DMARC aligned.
|
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ OVERRIDE_HOSTNAME=mail.furyhawk.lol
|
|||||||
OVERRIDE_DOMAINNAME=furyhawk.lol
|
OVERRIDE_DOMAINNAME=furyhawk.lol
|
||||||
POSTMASTER_ADDRESS=postmaster@furyhawk.lol
|
POSTMASTER_ADDRESS=postmaster@furyhawk.lol
|
||||||
PERMIT_DOCKER=network
|
PERMIT_DOCKER=network
|
||||||
SSL_TYPE=letsencrypt
|
SSL_TYPE=manual
|
||||||
SSL_DOMAIN=mail.furyhawk.lol
|
SSL_CERT_PATH=/tmp/docker-mailserver/ssl/fullchain.pem
|
||||||
|
SSL_KEY_PATH=/tmp/docker-mailserver/ssl/privkey.pem
|
||||||
Reference in New Issue
Block a user