—— 以 mail.it.nuface.tw + BIND9 自建 DNS 為實例
0. 環境說明
- 網域:
nuface.tw - 郵件網域:
it.nuface.tw - MX 記錄:
it.nuface.tw. MX 10 mail.it.nuface.tw. - 郵件主機 IP:
mail.it.nuface.tw. A x.x.x.x - DNS:自建 BIND9(上層中華電信,只負責 DS)
- DNSSEC:
nuface.tw/it.nuface.tw/mail.it.nuface.tw都已啟用並簽署 - Mail Server:Postfix(Docker)
- 憑證路徑(容器內):
/etc/letsencrypt/live/mail.it.nuface.tw/fullchain.pem/etc/letsencrypt/live/mail.it.nuface.tw/privkey.pem
目標:
✅ 為 mail.it.nuface.tw:25 建立 TLSA 記錄
✅ 在 BIND9 zone 中加入 DANE
✅ 讓 Postfix 對外寄信時啟用 DANE 驗證對方憑證
✅ 對外來信者提供 DANE 能驗證的憑證資訊
1️⃣ 從憑證產生 TLSA 指紋(3 1 1)
我們使用常見且實務上最好用的組合:
- 用途(Usage):
3= DANE-EE(只綁定這張伺服器憑證) - 選擇器(Selector):
1= 主體公鑰(SubjectPublicKeyInfo) - 匹配方式(Matching type):
1= SHA-256
也就是:
TLSA 3 1 1 <指紋>
在你的 mail 容器裡執行(或掛載憑證後,在主機跑也可以):
CERT=/etc/letsencrypt/live/mail.it.nuface.tw/fullchain.pem
openssl x509 -in "$CERT" -noout -pubkey \
| openssl pkey -pubin -outform DER \
| openssl sha256 -binary \
| hexdump -ve '32/1 "%02x" "\n"'
輸出會像這樣(示意):
3a5b7c9d2f4e8a......
這一長串 64 個十六進位字元,就是 TLSA 3 1 1 要用的值。
2️⃣ 在 BIND9 zone 檔加入 TLSA 記錄
你的 db.nuface.tw 目前長這樣(節錄):
$TTL 12H
$ORIGIN nuface.tw.
...
it.nuface.tw. IN TXT "v=spf1 a mx ip4:x.x.x.x ~all"
it.nuface.tw. IN MX 10 mail.it.nuface.tw.
it IN A x.x.x.x
mail.it IN A x.x.x.x
...
在這個檔案裡新增 TLSA 記錄(建議放在 mail.it 那一段附近):
; DANE for SMTP (TCP/25) on mail.it.nuface.tw
_25._tcp.mail.it IN TLSA 3 1 1 (
3a5b7c9d2f4e8a0123456789abcdef0123456789abcdef0123456789abcdef0
)
說明:
- 因為
$ORIGIN nuface.tw.
→mail.it其實是mail.it.nuface.tw.
→_25._tcp.mail.it對外就是_25._tcp.mail.it.nuface.tw. IN TLSA 3 1 1:對應剛剛產出的指紋- 可以視需要分行或寫一行,只要 BIND 語法正確即可
⚠ 記得同步:
@ IN SOA ns1.nuface.tw. postmaster.nuface.tw. (
2025112005 ; Serial(+1)
...
)
改完 serial 後:
rndc reload nuface.tw
# 若你使用 inline-signing / auto-dnssec,BIND 會自動重簽
3️⃣ 確認 TLSA 已生效
在外部機器(或任何支援 DNSSEC 的 resolver)測試:
dig _25._tcp.mail.it.nuface.tw TLSA +dnssec
你應該會看到:
- 有
TLSA記錄 - 同一筆 RRset 有
RRSIG(代表已簽署) - 回應標記帶有
ad(Authenticated Data)
代表 DANE 的「DNS 部分」已經 OK。
4️⃣ 設定 Postfix 啟用 DANE(對外寄信)
DANE 確實發揮作用的地方,是 你對外「寄信」時,驗證對方的 MX + TLSA。
因此主要調整的是 傳出去那一側(smtp_*)。
編輯 main.cf(容器內):
# 啟用 DNSSEC 驗證
smtp_dns_support_level = dnssec
# 對外寄信預設使用 DANE
smtp_tls_security_level = dane
# 建議開啟 TLS log,便於觀察 DANE 狀態
smtp_tls_loglevel = 1
# 你的憑證(給對方驗證你,用於 inbound)
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.it.nuface.tw/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.it.nuface.tw/privkey.pem
# inbound 建議仍使用 may / encrypt 規則(依你原本設定)
smtpd_tls_security_level = may
注意:
smtp_*是「Postfix 當 client 對外連線」smtpd_*是「Postfix 當 server 收信」- DANE 對「你收信」來說,只需要正確的 TLSA + DNSSEC 就好,Postfix 不需特別開啟什麼。
重啟 Postfix 容器或 reload:
postfix reload
5️⃣ 測試 DANE 是否生效
- 先找一個也支援 DANE 的收信方(例如某些歐洲政府或測試網域),或你自己另一個實驗網域
- 寄一封信過去,觀察 log:
grep -i dane /var/log/mail.log
你應該會看到類似:
Verified TLS connection established using DANE
如果你只想確認「出站有啟用 DANE 機制」,也可以用 postfix tls(Postfix 3.4+)或 posttls-finger 去測試其他有 TLSA 的主機。
6️⃣ 常見踩雷點
- 憑證更新後忘了重算 TLSA
- Let’s Encrypt 90 天更新
- 每次更新後要重新產生 TLSA 的指紋,更新 zone、重簽、reload
- 指紋算錯:用錯檔案 / 指令少一段管線
- 建議固定寫成 script,每次更新時自動跑
- 只對
nuface.tw啟用 DNSSEC,忘了it.nuface.tw子域- 你現在兩者都 OK,很棒 👍
- BIND 沒有重簽 / serial 沒更新
- 記得
SOA serial +1+rndc reload或檢查 auto-dnssec 狀態
- 記得
7️⃣ 結語
你目前的架構:
- 自建 BIND9 + DNSSEC
- 以
mail.it.nuface.tw為 MX - 搭配 Let’s Encrypt 憑證
- 再加上 TLSA + Postfix DANE
已經是非常完整、教科書等級的郵件安全實作。