Sfoglia il codice sorgente

Add a DKIM setup script for the mail domains

Generates one key per domain, publishes the public key as the TXT record at
<selector>._domainkey, registers the key in OpenDKIM's key.table and
signing.table, and restarts OpenDKIM.

The one part that is easy to get wrong: the .txt that opendkim-genkey
writes is not a bare record value, it is a whole zone-file entry, already
split into 250-character strings:

  202610._domainkey.example.com<TAB>IN<TAB>TXT<TAB>( "v=DKIM1; k=rsa; "
    "p=MIIB..." )

There is no flag that suppresses the stub, so the value has to be
extracted: awk -F'"' picks out the quoted character-strings, which are
concatenated the way a resolver would. Stripping only the quotes would
publish the zone-file syntax itself as the TXT value.

DRY_RUN defaults to 1: nothing is sent to DNS, neither table is touched
and nothing is restarted. key.table and signing.table entries are guarded
by grep -qxF, so a second run in the same month adds no duplicates.
Paul-Dieter Klumpp 1 settimana fa
parent
commit
5670b60982
1 ha cambiato i file con 92 aggiunte e 0 eliminazioni
  1. 92 0
      scripts/setup-dkim.sh

+ 92 - 0
scripts/setup-dkim.sh

@@ -0,0 +1,92 @@
+#!/bin/sh
+# Erzeugt je Domain einen DKIM-Schlüssel, legt den öffentlichen Schlüssel als
+# DNS-TXT-Record an, trägt den Schlüssel in OpenDKIM ein und startet OpenDKIM neu.
+#
+# DRY_RUN=1 (Vorgabe) schickt nichts ins DNS und startet nichts neu. Schlüssel
+# und Konfiguration werden trotzdem lokal geschrieben, damit der Wert zum
+# Prüfen überhaupt erst vorliegt. Erst nach der Prüfung auf 0 setzen.
+set -eu
+
+DRY_RUN=1
+CLI=./schlundtech-dns
+KEYDIR=/etc/opendkim/keys
+OPENDKIMDIR=/etc/opendkim
+SELECTOR=$(date +%Y%m)
+DOMAINS="klumpp.de klumpp.net netdome.biz oberrhein.de"
+
+die() { printf '%s\n' "$*" >&2; exit 1; }
+
+run_cli() {
+	if [ "$DRY_RUN" -eq 1 ]; then
+		"$CLI" "$@" --dry-run
+	else
+		"$CLI" "$@"
+	fi
+}
+
+command -v opendkim-genkey >/dev/null 2>&1 || die "opendkim-genkey nicht gefunden"
+[ -x "$CLI" ] || die "$CLI nicht gefunden — vorher 'go build'"
+uid=$(id -u) || die "id -u schlug fehl"
+[ "$uid" -eq 0 ] || die "muss als root laufen, schreibt nach $KEYDIR"
+[ -d "$KEYDIR" ] || die "$KEYDIR fehlt"
+
+cd "$KEYDIR"
+
+for domain in $DOMAINS; do
+	opendkim-genkey -d "$domain" -b 2048 -r -s "$SELECTOR"
+
+	# Nur die zwei neuen Dateien, nicht alles im Verzeichnis.
+	chown opendkim:opendkim "$SELECTOR.txt" "$SELECTOR.private"
+	mv "$SELECTOR.txt" "$domain.txt"
+	mv "$SELECTOR.private" "$domain.private"
+
+	# Die .txt von opendkim-genkey ist KEIN blosser Record-Wert, sondern ein
+	# kompletter Zone-File-Eintrag, vorgeblich in 250-Zeichen-Stuecke geteilt:
+	#
+	#   202610._domainkey.klumpp.de	IN	TXT	( "v=DKIM1; k=rsa; "
+	#     "p=MIIB..."	 "..."
+	#   )
+	#
+	# Die Anfuehrungszeichen trennen die Character-Strings. Nur was zwischen
+	# ihnen steht ist der Record-Wert; der Rest ist Zone-File-Syntax und darf
+	# nicht mit in DNS. awk mit -F'"' liefert genau diese Zwischenstuecke,
+	# die dann aneinandergehaengt werden - genau das, was ein Resolver tut.
+	value=$(awk -F'"' '{ for (i = 2; i <= NF; i += 2) printf "%s", $i }' "$domain.txt")
+	[ -n "$value" ] || die "kein Wert in $domain.txt gefunden"
+	case "$value" in
+	*v=DKIM1*) ;;
+	*) die "$domain.txt enthaelt keinen DKIM-Record" ;;
+	esac
+	case "$value" in
+	*p=*) ;;
+	*) die "$domain.txt enthaelt kein p=" ;;
+	esac
+
+	run_cli records set "$domain" \
+		--name "$SELECTOR._domainkey" --type TXT --value "$value"
+
+	# Ein 2048-Bit-Schluessel ergibt rund 360 Zeichen und ueberschreitet damit
+	# die 255 Bytes je Character-String, die DNS erlaubt. Das ist normal: der
+	# Wert wird in mehrere Strings aufgeteilt und vom Gateway als einer
+	# gespeichert. Im Portal sieht er deshalb quoted und aufgeteilt aus.
+	if [ "${#value}" -gt 255 ]; then
+		printf '%s: %d Zeichen, laenger als 255 — das Gateway teilt den Wert in Character-Strings auf, das ist korrekt\n' \
+			"$domain" "${#value}" >&2
+	fi
+
+	if [ "$DRY_RUN" -eq 0 ]; then
+		keyline="key-$domain $domain:$SELECTOR:$KEYDIR/$domain.private"
+		grep -qxF "$keyline" "$OPENDKIMDIR/key.table" 2>/dev/null ||
+			printf '%s\n' "$keyline" >>"$OPENDKIMDIR/key.table"
+
+		signline="*@$domain key-$domain"
+		grep -qxF "$signline" "$OPENDKIMDIR/signing.table" 2>/dev/null ||
+			printf '%s\n' "$signline" >>"$OPENDKIMDIR/signing.table"
+	fi
+done
+
+if [ "$DRY_RUN" -eq 1 ]; then
+	printf 'nur Vorschau: kein DNS-Record gesetzt, key.table/signing.table unveraendert, kein Neustart — DRY_RUN=0 zum Ausfuehren\n' >&2
+else
+	systemctl restart opendkim
+fi