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