antony@notes:~/container$ cat "Podman-Start-Docker-Mail-Server-with-TLS.md"
Podman Start Docker Mail Server with TLS
Podman Start Docker Mail Server with TLS
環境資訊
| 項目 | 值 |
|---|---|
| 域名 | kubeantony.com |
| 主機名 | mx1.kubeantony.com |
| 容器引擎 | Podman(--network=host) |
| 網路環境 | 內網,無 Public IP |
| DNS 管理 | Cloudflare |
| 套用服務 | SMTP(25/465/587)、IMAP(993)、POP3(995)、Rspamd Web UI |
方案比較
| 方案 A:mkcert 自簽憑證 | 方案 B:Let’s Encrypt + Cloudflare DNS | |
|---|---|---|
| 費用 | 免費 | 免費 |
| 需要 Public IP | 否 | 否(DNS-01 驗證不需要) |
| 客戶端信任 | 需手動匯入 CA 憑證 | 自動信任(公開 CA) |
| 有效期限 | 10 年 | 90 天(自動續期) |
| 適用場景 | 內部測試 / 個人使用 | 正式環境 / 對外服務 |
| 維護難度 | 低(幾乎不用管) | 低(cron 自動續期) |
方案 A:使用 braveantony/mkcert 自簽憑證
A-1:產生自簽憑證
# 下載 mkcert 工具
cd /opt
sudo git clone https://github.com/braveantony/mkcert.git
sudo chmod +x /opt/mkcert/certctl編輯設定檔:
sudo nano /opt/mkcert/config修改為以下內容:
# CA 相關設定
CA_KEY="ca.key"
CA_CRT="ca.crt"
CA_DAYS=3650
CA_SUBJ="/C=TW/ST=Taipei/L=Taipei/O=kubeantony/OU=Mail/CN=kubeantony Root CA"
# Server 相關設定
DOMAIN_NAME="kubeantony.com"
SERVER_KEY="${DOMAIN_NAME}.key"
SERVER_CSR="${DOMAIN_NAME}.csr"
SERVER_CRT="${DOMAIN_NAME}.crt"
SERVER_DAYS=3650
SERVER_SUBJ="/C=TW/ST=Taipei/L=Taipei/O=kubeantony/OU=Mail/CN=mx1.${DOMAIN_NAME}"
V3_EXT="v3.ext"
ALT_NAMES=(
"DNS.1=*.${DOMAIN_NAME}"
"DNS.2=${DOMAIN_NAME}"
"DNS.3=mx1.${DOMAIN_NAME}"
)# 產生憑證
sudo /opt/mkcert/certctl
# 確認結果
ls -l /opt/mkcert/kubeantony.com/
# 預期:ca.crt ca.key kubeantony.com.crt kubeantony.com.keyA-2:部署憑證
sudo mkdir -p /opt/zfs/dkmail/conf/ssl
sudo cp /opt/mkcert/kubeantony.com/kubeantony.com.key /opt/zfs/dkmail/conf/ssl/
sudo cp /opt/mkcert/kubeantony.com/kubeantony.com.crt /opt/zfs/dkmail/conf/ssl/
# 產生 fullchain(伺服器憑證 + CA 憑證)
sudo bash -c 'cat /opt/mkcert/kubeantony.com/kubeantony.com.crt \
/opt/mkcert/kubeantony.com/ca.crt \
> /opt/zfs/dkmail/conf/ssl/fullchain.pem'
sudo chmod 644 /opt/zfs/dkmail/conf/ssl/fullchain.pem
sudo chmod 600 /opt/zfs/dkmail/conf/ssl/kubeantony.com.keyA-3:客戶端匯入 CA 根憑證
自簽憑證需要在每台客戶端手動匯入 ca.crt:
| 客戶端 | 匯入方式 |
|---|---|
| Thunderbird | 設定 → 隱私與安全 → 憑證 → 匯入 → 選 ca.crt → 勾選「信任此 CA 識別網站」 |
| Windows | 雙擊 ca.crt → 安裝憑證 → 「受信任的根憑證授權單位」 |
| macOS | 雙擊 ca.crt → 加入鑰匙圈 → 設定為「永遠信任」 |
| Linux | sudo cp ca.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificates |
完成後,跳至「共通步驟:設定 Mail Server 並啟動容器」。
方案 B:Let’s Encrypt + Cloudflare DNS-01 驗證
核心原理:acme.sh 透過 Cloudflare API 在 DNS 自動建立
_acme-challengeTXT 記錄, Let’s Encrypt 驗證該記錄後簽發憑證。全程不需要 Public IP 或開放任何 Port。
B-1:在 Cloudflare 建立 API Token
- 登入 Cloudflare Dashboard
- 右上角點擊個人頭像 → My Profile → API Tokens
- 點擊 Create Token
- 選擇 Edit zone DNS 模板(Use template)
- 設定權限:
- Permissions:
Zone/DNS/Edit - Zone Resources:
Include→Specific zone→kubeantony.com
- Permissions:
- 建立後複製 Token(只會顯示一次)
- 到
kubeantony.com的 Overview 頁面,記下右側的 Zone ID
B-2:安裝 acme.sh
# 安裝 acme.sh(不需要 root,但建議用 root 方便管理憑證路徑)
sudo -i
curl https://get.acme.sh | sh -s email=bigred@kubeantony.com
# 讓 acme.sh 指令在當前 shell 可用
source ~/.bashrc
# 設定預設 CA 為 Let's Encrypt
~/.acme.sh/acme.sh --set-default-ca --server letsencryptB-3:簽發憑證
# 設定 Cloudflare API 認證
export CF_Token="你的_Cloudflare_API_Token"
export CF_Zone_ID="你的_Zone_ID"
# 簽發萬用字元憑證(含 kubeantony.com 和 *.kubeantony.com)
~/.acme.sh/acme.sh --issue \
--dns dns_cf \
-d kubeantony.com \
-d '*.kubeantony.com'acme.sh 會自動透過 Cloudflare API 建立 TXT 記錄進行驗證, 驗證通過後自動清除 TXT 記錄。整個過程完全不需要 Public IP。
B-4:安裝憑證到 Mail Server 目錄
sudo mkdir -p /opt/zfs/dkmail/conf/ssl
~/.acme.sh/acme.sh --install-cert \
-d kubeantony.com \
-d '*.kubeantony.com' \
--key-file /opt/zfs/dkmail/conf/ssl/kubeantony.com.key \
--fullchain-file /opt/zfs/dkmail/conf/ssl/fullchain.pem \
--reloadcmd "podman restart dkmail"
sudo chmod 644 /opt/zfs/dkmail/conf/ssl/fullchain.pem
sudo chmod 600 /opt/zfs/dkmail/conf/ssl/kubeantony.com.key說明:
--install-cert會將憑證複製到你指定的路徑--reloadcmd會在每次續期後自動重啟容器套用新憑證- acme.sh 會自動建立 cron job,每天檢查是否需要續期(到期前 30 天自動續期)
B-5:確認自動續期 cron
crontab -l
# 應該會看到類似這行:
# 0 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null完成後,跳至「共通步驟:設定 Mail Server 並啟動容器」。
共通步驟:設定 Mail Server 並啟動容器
以下步驟不論方案 A 或方案 B 都相同。此時你的 /opt/zfs/dkmail/conf/ssl/ 應該已有:
/opt/zfs/dkmail/conf/ssl/
├── fullchain.pem # 完整憑證鏈
└── kubeantony.com.key # 私鑰C-1:設定 Postfix(SMTP TLS)
sudo tee /opt/zfs/dkmail/conf/postfix-main.cf << 'EOF'
# TLS 設定
smtpd_tls_cert_file = /etc/ssl/mail/fullchain.pem
smtpd_tls_key_file = /etc/ssl/mail/kubeantony.com.key
smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1
smtpd_tls_mandatory_ciphers = medium
# 寄出郵件也啟用 TLS
smtp_tls_security_level = may
smtp_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1
EOFC-2:設定 Dovecot(IMAP/POP3 TLS)
sudo mkdir -p /opt/zfs/dkmail/conf/dovecot
sudo tee /opt/zfs/dkmail/conf/dovecot/10-ssl.conf << 'EOF'
ssl = required
ssl_cert = </etc/ssl/mail/fullchain.pem
ssl_key = </etc/ssl/mail/kubeantony.com.key
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes
EOF注意: Dovecot 設定中
<是必要語法,代表「讀取此檔案內容」,不是打字錯誤。
C-3:停止舊容器並重新啟動
sudo podman stop dkmail
sudo podman rm dkmailsudo podman run --name dkmail -itd \
-h mx1.kubeantony.com --network=host \
-e NETWORK_INTERFACE=ens18 \
-e SSL_TYPE=manual \
-e SSL_CERT_PATH=/etc/ssl/mail/fullchain.pem \
-e SSL_KEY_PATH=/etc/ssl/mail/kubeantony.com.key \
--dns 192.168.11.253 --dns-search kubeantony.com \
-v /opt/zfs/dkmail/data:/var/mail \
-v /opt/zfs/dkmail/conf/postfix-main.cf:/tmp/docker-mailserver/postfix-main.cf:ro \
-v /opt/zfs/dkmail/conf/rspamd:/tmp/docker-mailserver/rspamd \
-v /opt/zfs/dkmail/conf/ssl:/etc/ssl/mail:ro \
-v /opt/zfs/dkmail/conf/dovecot/10-ssl.conf:/etc/dovecot/conf.d/10-ssl.conf:ro \
dkmail.rspamd與你原始指令相比新增的項目:
| 新增項目 | 說明 |
|---|---|
-e SSL_TYPE=manual | 告訴 docker-mailserver 使用手動指定的憑證 |
-e SSL_CERT_PATH=... | 容器內憑證路徑 |
-e SSL_KEY_PATH=... | 容器內私鑰路徑 |
-v .../ssl:/etc/ssl/mail:ro | 掛載憑證目錄 |
-v .../10-ssl.conf:... | 覆蓋 Dovecot SSL 設定 |
C-4:Rspamd Web UI 的 TLS(選用,透過 Nginx 反向代理)
Rspamd Web UI 本身不支援 TLS,需要用 Nginx 做反向代理:
sudo apt install -y nginx
sudo mkdir -p /etc/nginx/ssl
sudo cp /opt/zfs/dkmail/conf/ssl/fullchain.pem /etc/nginx/ssl/
sudo cp /opt/zfs/dkmail/conf/ssl/kubeantony.com.key /etc/nginx/ssl/sudo tee /etc/nginx/sites-available/rspamd << 'EOF'
server {
listen 443 ssl;
server_name mx1.kubeantony.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/kubeantony.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://127.0.0.1:11334;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/rspamd /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl restart nginx驗證 TLS 是否生效
# SMTP STARTTLS(Port 587)
openssl s_client -connect mx1.kubeantony.com:587 -starttls smtp
# SMTPS(Port 465)
openssl s_client -connect mx1.kubeantony.com:465
# IMAPS(Port 993)
openssl s_client -connect mx1.kubeantony.com:993
# POP3S(Port 995)
openssl s_client -connect mx1.kubeantony.com:995正常輸出應包含:
- 方案 A(自簽):
issuer: CN = kubeantony Root CA,Verify return code: 21— 這是正常的 - 方案 B(Let’s Encrypt):
issuer: C = US, O = Let's Encrypt,Verify return code: 0 (ok)
完整目錄結構
/opt/zfs/dkmail/
├── data/ # 郵件資料(/var/mail)
└── conf/
├── postfix-main.cf # Postfix TLS 設定
├── ssl/
│ ├── fullchain.pem # 完整憑證鏈
│ └── kubeantony.com.key # 私鑰
├── dovecot/
│ └── 10-ssl.conf # Dovecot SSL 設定
└── rspamd/
└── override.d/
├── options.inc
├── worker-controller.inc
└── hfilter.conf