Skip to content

Nuface Blog

隨意隨手記 Casual Notes

Menu
  • Home
  • About
  • Services
  • Blog
  • Contact
  • Privacy Policy
  • Login
Menu

Postfix + Let’s Encrypt + BIND9 + DANE TLSA 指紋自動更新完整教學

Posted on 2025-12-032025-12-03 by Rico

📌 前言

在上一章介紹 DANE 時,我們提到 DANE(DNS-based Authentication of Named Entities)透過 DNSSEC 加簽的 TLSA 記錄,讓寄件端 MTA 在進行 SMTP over TLS(port 25)時:

  • 驗證 MX 主機憑證是否與 DNS 宣告相符合
  • 直接在 DNSSEC 保護的 zone 裡「鎖定」你要使用的公鑰
  • 防止 MITM、SSL 降級攻擊、憑證替換攻擊

若使用 TLSA(3 1 1)模式,就表示綁定的是「伺服器公鑰指紋」。
因此只要 你的憑證更新,公鑰可能跟著變動 → TLSA 記錄也必須同步更新。

Let’s Encrypt 憑證 90 天更新一次。
若 TLSA 沒同步更新,就會造成使用 DANE 的收件端拒收郵件,例如:

TLSA mismatch — certificate does not match DNSSEC TLSA

因此:

🎯 憑證自動更新 + TLSA 自動更新 + BIND DNSSEC 自動簽署 = 必須全自動化

這篇文章將示範我在 mail.it.nuface.tw 伺服器上的完整自動化流程。


🧩 系統架構與前置條件

本文章以你的實際環境為例:

項目設定
郵件網域it.nuface.tw
MXmail.it.nuface.tw
IPx.x.x.x
DNS自建 BIND9(DNSSEC 已啟動),上層註冊商為中華電信
DNSSEC已啟用,子域與 A 記錄皆有 RRSIG
TLSA 記錄_25._tcp.mail.it.nuface.tw
PostfixDocker Container
憑證Let’s Encrypt(容器內 /etc/letsencrypt/live/...)
自動更新模式不寄 email、寫 log 到 /var/log/dane-update.log

🧩 TLSA 使用的模式:3 1 1

本教學使用最常見、最安全的模式:

Usage  = 3  (DANE-EE: End Entity Cert)
Selector = 1  (SubjectPublicKeyInfo)
Match Type = 1 (SHA-256)

因此 TLSA 格式為:

_25._tcp.mail.it.nuface.tw IN TLSA 3 1 1 <hash>

🛠 第一步:從憑證產生 TLSA 指紋(hash)

在 mail container(或 host mount)執行:

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"'

輸出例:

3a5b7c9d2f4e8a0123456789abcdef0123456789abcdef0123456789abcdef0

這一串就是 TLSA 使用的 <hash>。


🛠 第二步:BIND9 zone 檔加入 TLSA 記錄(單行格式)

zone db.nuface.tw 前段如下:

mail.it            IN A    x.x.x.x

在附近加上:

_25._tcp.mail.it  IN TLSA 3 1 1 3a5b7c9d2f4e8a0123456789abcdef0123456789abcdef0123456789abcdef0

若之前有舊的 TLSA 記錄,請用腳本自動取代,不要手動更新。


🛠 第三步:建立自動化腳本 /usr/local/sbin/update-tlsa.sh

此腳本功能:

✔ 自動重新計算 TLSA hash
✔ zone 檔自動更新(單行格式)
✔ SOA serial 自動 +1
✔ rndc reload nuface.tw
✔ BIND9 自動 DNSSEC 重簽(你有 inline-signing)
✔ 寫 log 到 /var/log/dane-update.log

以下即為可直接使用的版本:


📄 /usr/local/sbin/update-tlsa.sh

#!/bin/bash
set -euo pipefail

ZONE="/etc/bind/zones/ext/db.nuface.tw"
ORIGIN="nuface.tw."
TLSA_NAME="_25._tcp.mail.it"
CERT="/etc/letsencrypt/live/mail.it.nuface.tw/fullchain.pem"
LOG="/var/log/dane-update.log"

timestamp() { date "+%Y-%m-%d %H:%M:%S"; }

echo "[$(timestamp)] Starting TLSA update..." >> "$LOG"

# 1. 產生新 hash
NEW_HASH=$(openssl x509 -in "$CERT" -noout -pubkey \
  | openssl pkey -pubin -outform DER \
  | openssl sha256 -binary \
  | hexdump -ve '32/1 "%02x"')

if [[ -z "$NEW_HASH" ]]; then
  echo "[$(timestamp)] ERROR: failed to calculate TLSA hash" >> "$LOG"
  exit 1
fi

echo "[$(timestamp)] New TLSA hash: $NEW_HASH" >> "$LOG"

# 2. 更新 zone:取代舊 TLSA 行
sed -i \
  "s/^${TLSA_NAME}[[:space:]]\+IN[[:space:]]\+TLSA.*/${TLSA_NAME} IN TLSA 3 1 1 ${NEW_HASH}/" \
  "$ZONE"

echo "[$(timestamp)] TLSA record updated in zone file." >> "$LOG"

# 3. 更新 SOA serial
SERIAL=$(grep -A1 "SOA" "$ZONE" | awk 'NR==2 {print $1}')
NEXT=$((SERIAL + 1))

sed -i "s/$SERIAL ; Serial/$NEXT ; Serial/" "$ZONE"
echo "[$(timestamp)] SOA serial updated: $SERIAL -> $NEXT" >> "$LOG"

# 4. reload zone
rndc reload nuface.tw
echo "[$(timestamp)] rndc reload executed." >> "$LOG"

echo "[$(timestamp)] TLSA update completed." >> "$LOG"

🛠 第四步:整合到 renewssl.sh

在你原本的 script 中:

if [ "$after" -gt "$before" ]; then
    ...
    docker exec "$MAIL_CTN" postfix reload || true
    docker restart "$DOVECOT_CTN" || true

下面加一句(主機端執行):

/usr/local/sbin/update-tlsa.sh

完整範例:

if [ "$after" -gt "$before" ]; then
  echo "$after" > "$STAMP_FILE"
  echo "[renewssl] Certificates changed, applying reloads..."

  docker restart "$WEB_CTN" || true
  docker exec "$MAIL_CTN" postfix reload || true
  docker restart "$DOVECOT_CTN" || true

  # 新增:TLSA 自動更新
  /usr/local/sbin/update-tlsa.sh
fi

完成後 → 你的憑證連帶 TLSA 會跟著自動更新。


🧪 第五步:驗證 TLSA + DNSSEC

查 TLSA 是否正確:

dig +dnssec _25._tcp.mail.it.nuface.tw TLSA

你應該看到:

  • 你的新 hash
  • RRSIG(DNSSEC 簽章)
  • 回應 flags 中有 ad(Authenticated Data)

🧪 第六步:測試 DANE 邏輯(Outbound)

測試你對外寄信時 DANE 行為:

posttls-finger dane-test.somerandomdomain

或者寄到已部署 DANE 的網域,查看 log:

grep -i dane /var/log/mail.log

成功範例:

Verified TLS connection established using DANE

🧪 第七步:常見錯誤排查

問題原因解法
TLSA mismatchhash 未更新手動執行 update-tlsa.sh
zone serial 未更新所以 slave 不同步檢查 sed / serial
DNSSEC RRSIG 過期BIND9 未重簽確認 inline-signing 是否正常
hash 長度錯誤TLSA 格式錯誤或空值檢查 openssl 管線
Postfix 不使用 DANEsmtp_dns_support_level 未設設為 dnssec

🎯 結語

完成以上步驟後,你現在擁有:

✔ DNSSEC 保護的 MX

✔ 自動更新的 TLSA(3 1 1)

✔ BIND9 inline-signing 自動簽署

✔ Postfix 出站 DANE 驗證

✔ 全自動化 Let’s Encrypt + TLSA + DNSSEC pipeline

你的 mail.it.nuface.tw 已經是高強度郵件安全架構,可抵抗:

  • MITM
  • DNS spoofing
  • TLS downgrading
  • certificate replacement attack

Recent Posts

  • Postfix + Let’s Encrypt + BIND9 + DANE Fully Automated TLSA Update Guide
  • Postfix + Let’s Encrypt + BIND9 + DANE TLSA 指紋自動更新完整教學
  • Deploying DANE in Postfix
  • 如何在 Postfix 中部署 DANE
  • DANE: DNSSEC-Based TLS Protection

Recent Comments

  1. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on High Availability Architecture, Failover, GeoDNS, Monitoring, and Email Abuse Automation (SOAR)
  2. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on MariaDB + PostfixAdmin: The Core of Virtual Domain & Mailbox Management
  3. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on Daily Operations, Monitoring, and Performance Tuning for an Enterprise Mail System
  4. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on Final Chapter: Complete Troubleshooting Guide & Frequently Asked Questions (FAQ)
  5. Building a Complete Enterprise-Grade Mail System (Overview) - Nuface Blog on Network Architecture, DNS Configuration, TLS Design, and Postfix/Dovecot SNI Explained

Archives

  • December 2025
  • November 2025
  • October 2025

Categories

  • AI
  • Apache
  • Cybersecurity
  • Database
  • DNS
  • Docker
  • Fail2Ban
  • FileSystem
  • Firewall
  • Linux
  • LLM
  • Mail
  • N8N
  • OpenLdap
  • OPNsense
  • PHP
  • QoS
  • Samba
  • Switch
  • Virtualization
  • VPN
  • WordPress
© 2025 Nuface Blog | Powered by Superbs Personal Blog theme