PowerDNS – TLSA – Auto Update
Du nutzt PowerDNS, hast deinen Mailserver mit DANE abgesichert und setzt auf Let’s Encrypt? Dann kennst du das Problem: Bei jeder Zertifikatsänderung ändert sich auch der Hashwert deines TLSA-Eintrags im DNS. Das heißt, du musst bei jeder Erneuerung daran denken, den Eintrag anzupassen – oder du überlässt das einfach einem Script.
Das Script läuft direkt auf dem Server, auf dem dein Master-DNS läuft.
Ich verwende dafür zwei Scripte: Das erste prüft den aktuellen Zustand und ruft das zweite auf, wenn es Abweichungen gibt. Diese Methode funktioniert, wenn dein TLSA-Eintrag nach folgendem Muster aufgebaut ist:
3 1 1 ahdbflafdbfjlasdbhf...
Los geht´s, wir starten mit dem Script tlsa.sh im Ordner /opt/scripts/dnssec
#!/bin/bash
# Usage: ./tlsa.sh <hostname> <port> <protocol> [<nameserver>]
# Example: ./tlsa.sh mx.mailserver.de 25 tcp 8.8.8.8
if [ "$#" -lt 3 ] || [ "$#" -gt 4 ]; then
echo "Usage: $0 <hostname> <port> <protocol> [<nameserver>]"
exit 1
fi
HOSTNAME=$1
PORT=$2
PROTO=$3
NAMESERVER=$4
EMAIL="meine@email.de" # Set your email address here
# Check if required tools are installed
for cmd in openssl dig xxd mail; do
if ! command -v $cmd &>/dev/null; then
echo "Error: '$cmd' is not installed. Install it and try again."
exit 1
fi
done
# Format the hostname for TLSA query
TLSA_HOSTNAME="_${PORT}._${PROTO}.${HOSTNAME}"
# Get the TLSA record from DNS (with optional NS)
if [ -n "$NAMESERVER" ]; then
TLSA_RECORD=$(dig +short @${NAMESERVER} TLSA $TLSA_HOSTNAME | awk '{print $4 $5 $6 $7}')
else
TLSA_RECORD=$(dig +short TLSA $TLSA_HOSTNAME | awk '{print $4 $5 $6 $7}')
fi
if [ -z "$TLSA_RECORD" ]; then
echo "Error: No TLSA record found for $TLSA_HOSTNAME"
exit 1
fi
echo "TLSA-Record: $TLSA_RECORD"
# Retrieve the certificate from the server and calculate the public key hash
CALCULATED_HASH=$(echo | openssl s_client -connect $HOSTNAME:$PORT -starttls smtp -showcerts 2>/dev/null | \
awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/' | \
openssl x509 -pubkey -noout | openssl pkey -pubin -outform DER | \
openssl dgst -sha256 -binary | xxd -p -c 32)
echo "Calculated Hash: $CALCULATED_HASH"
# Convert both hashes to lowercase for comparison
TLSA_RECORD_LOWER=$(echo "$TLSA_RECORD" | tr '[:upper:]' '[:lower:]')
CALCULATED_HASH_LOWER=$(echo "$CALCULATED_HASH" | tr '[:upper:]' '[:lower:]')
# Compare calculated hash with TLSA record
if [ "$CALCULATED_HASH_LOWER" == "$TLSA_RECORD_LOWER" ]; then
echo "✅ TLSA record matches the certificate!"
else
echo "❌ TLSA record does NOT match the certificate!"
echo -e "Subject: TLSA Record Mismatch\n\nThe TLSA record for $HOSTNAME on port $PORT ($PROTO) does not match the calculated hash." | mail -s "TLSA Record Mismatch" $EMAIL
./update_tlsa.sh "$CALCULATED_HASH_LOWER"
fi
Das zweite Script liegt im gleichen Ordner und heißt update_tlsa.sh
.
WICHTIG: In Zeile 23 musst du die Angaben an deinen Mailserver und das Format deines TLSA-Eintrags anpassen!
#!/bin/bash
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <calculated_hash>"
exit 1
fi
CALCULATED_HASH=$1
# PowerDNS-Konfigurationsvariablen
POWERDNS_SERVER="localhost" # PowerDNS-Server IP
API_KEY="geheimXd" # Dein API-Schlüssel
ZONE_NAME="mailserver.de" # Die Domain deines Mailservers
# API-Endpunkt und Server-Konfiguration
API_URL="http://$POWERDNS_SERVER:8081/api/v1/servers/localhost/zones/$ZONE_NAME"
JSON_PAYLOAD=$(cat <<EOF
{
"rrsets": [
{
"comments": [],
"name": "_25._tcp.mx.$ZONE_NAME.",
"records": [
{
"content": "3 1 1 $CALCULATED_HASH",
"disabled": false
}
],
"ttl": 300,
"type": "TLSA",
"changetype": "REPLACE"
}
]
}
EOF
)
# API-Anfrage zur Aktualisierung des TLSA-Records mit API-Schlüssel
curl -v -X PATCH "$API_URL" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD"
Der Aufruf erfolgt wie folgt:
Das Scripts, URL deines Mailservers, Port, Protokoll, DNS-Server IP
/opt/scripts/dnssec/tlsa.sh mx.mailserver.de 25 tcp 8.8.8.8