|
|
@@ -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
|