#!/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