Skip to content

Nuface Blog

隨意隨手記 Casual Notes

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

Postfix + Let’s Encrypt + BIND9 + DANE Fully Automated TLSA Update Guide

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

— Example: mail.it.nuface.tw (DNSSEC-enabled)

📌 Introduction

DANE (DNS-based Authentication of Named Entities) brings strong, DNSSEC-backed authentication to SMTP over TLS. By publishing a TLSA record protected by DNSSEC, you can let sending MTAs verify:

  • the exact server certificate (or public key)
  • with no dependency on public CA trust
  • protecting against MITM, TLS downgrades, certificate swapping attacks

When using the recommended mode:

TLSA 3 1 1

You bind the certificate’s SubjectPublicKeyInfo fingerprint (SHA-256) into DNS.

❗ Why automation is mandatory?

Let’s Encrypt renews certificates every 90 days.
If the public key changes but your TLSA record does not update, DANE-enabled receivers will reject your mail:

TLSA mismatch – certificate does not match TLSA record

This guide shows how to build full automation based on your real production environment:

✔ Let’s Encrypt auto-renew
✔ TLSA auto-regeneration
✔ BIND9 DNSSEC inline signing
✔ Automatic SOA serial increment
✔ Automatic rndc reload
✔ No service interruption
✔ Logging only (no email alerts)


🧩 Environment Overview (Real System)

ComponentValue
Domainnuface.tw
Mail domainit.nuface.tw
MXmail.it.nuface.tw
IPx.x.x.x
DNSSelf-hosted BIND9, DNSSEC-enabled
DNSSECEnabled with inline-signing + dnssec-policy
TLSA_25._tcp.mail.it.nuface.tw
PostfixDocker container
Certificates/etc/letsencrypt/live/mail.it.nuface.tw/fullchain.pem
Integration ModeNo email notifications, log-only
TLSA FormatSingle-line format

Everything here reflects your live setup.


🧩 TLSA Mode Used: 3 1 1

We use:

  • Usage = 3 (DANE-EE)
  • Selector = 1 (SubjectPublicKeyInfo)
  • Matching = 1 (SHA-256 hash)

Final format:

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

🛠 Step 1 — Generate TLSA (3 1 1) Hash From Certificate

Run this inside your mail container (or host with cert mounted):

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

Example output:

3a5b7c9d2f4e8a0123456789abcdef0123456789abcdef0123456789abcdef0

This value becomes the <hash> in your TLSA record.


🛠 Step 2 — Add TLSA to Your BIND9 Zone (Single-Line Format)

In your db.nuface.tw:

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

Add:

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

(Because $ORIGIN nuface.tw., mail.it expands to mail.it.nuface.tw.)


🛠 Step 3 — Create the Automation Script

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

This script:

✔ Regenerates TLSA hash
✔ Updates your zone file
✔ Auto-increments SOA serial
✔ Executes rndc reload nuface.tw
✔ Lets BIND9 auto-sign DNSSEC via inline-signing
✔ Logs to /var/log/dane-update.log
✔ No email sent (as requested)


Full Script (Ready to Use)

#!/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. Generate new TLSA 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. Update TLSA line in zone
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. Update 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"

Make executable:

chmod +x /usr/local/sbin/update-tlsa.sh

🛠 Step 4 — Integrate With Your Existing renewssl.sh

Your current script detects certificate updates via mtime.
Modify the “certificate changed” section:

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

  # NEW: Automatically update TLSA record
  /usr/local/sbin/update-tlsa.sh
fi

Now everything is fully automated.


🧪 Step 5 — Verifying DNSSEC + TLSA

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

You should see:

  • Your new TLSA hash
  • RRSIG signatures
  • ad (Authenticated Data)

🧪 Step 6 — Testing DANE (Outbound)

Use Postfix’s built-in tester:

posttls-finger some-dane-enabled-domain

Or send a real email to a DANE domain and check logs:

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

You should see:

Verified TLS connection established using DANE

🧩 Step 7 — Troubleshooting

IssueCauseFix
DANE mismatchTLSA not updatedRun update-tlsa.sh manually
DNS zone not reloadingSerial not incrementedCheck sed serial update
DNSSEC signatures missinginline-signing disabledFix zone config
Empty hashWrong cert pathCheck CERT= variable
Postfix ignoring DANEsmtp_dns_support_level wrongSet to dnssec

🎯 Final Result

With:

  • Let’s Encrypt automatic renewal
  • TLSA regeneration
  • BIND9 DNSSEC inline-signing
  • Postfix DANE-enabled outbound
  • Zero manual steps

You now have a fully automated, production-ready DANE deployment for:

mail.it.nuface.tw

This architecture protects you from:

  • MITM
  • TLS downgrading
  • DNS tampering
  • Certificate replacement attacks
  • Insecure MX routing

You now operate one of the few DANE-secured corporate mail systems in the region—excellent work.

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