Zum Hauptinhalt springen

HTTP-Monitore, White-Label, eigene Domain. In 3 Minuten online.

Monitore & Gruppen

  • Unbegrenzte HTTP-Monitore
  • Gruppierung nach Dienst oder Region

100 % anpassbar

  • Logo & Farbpalette
  • Titel & SEO-Meta
  • Freier Inhalt
Neu Neue Funktion

White-Label

  • Kein CaptainDNS-Branding
  • Eigene Domain per CNAME
  • Automatisches TLS

Echtzeit & Verlauf

  • Synchron mit deinen Monitoren
  • 30-Tage-Verlauf
  • Incidents & Wartungen

DKIM in Ihrer CI/CD validieren: defekte Records vor der Produktion abfangen

Von CaptainDNS
Veröffentlicht am 19. Mai 2026

CI/CD-Pipeline mit DKIM-Validierung: pre-commit-Hooks, GitHub Actions, GitLab CI, Terraform und Smoke-Test nach dem Deploy
TL;DR
  • Ein defekter DKIM-Record fällt selten in funktionalen Tests auf, bricht aber die Zustellbarkeit ab der ersten E-Mail nach dem Deploy
  • Lokale Linter prüfen die Syntax; die CaptainDNS-API /v1/dkim/validate prüft den realen, im DNS veröffentlichten Zustand
  • GitHub Actions, GitLab CI und Terraform-Hooks können einen Merge oder ein apply bei unzureichendem DKIM-Score blockieren
  • Die pre-commit-Validierung spart CI-Zeit, ersetzt aber keine Prüfung des im DNS veröffentlichten Zustands
  • Die Bündelung der SPF-, DKIM- und DMARC-Validierungen in einem einzigen Stage verhindert kaskadierende Regressionen

Sie versionieren Ihr DNS in Git, Ihre DKIM-Schlüsselrotationen laufen über einen Pull Request, und alles geht gut, bis Gmail Ihre Mails plötzlich in den Spam einsortiert. Die Ursache: ein vom DNS-Provider abgeschnittenes p=-Tag oder eine Signatur, die nicht mehr zum veröffentlichten Selektor passt. Der Bug zeigt sich weder in terraform plan noch in den Anwendungs-Integrationstests.

Die Antwort steckt in einem Wort: shift-left. Statt das Problem in der Produktion zu entdecken, validieren Sie jede DKIM-Änderung vor dem Merge, dann vor dem apply, dann nach dem Deploy. Dieser Artikel zeigt, wie Sie diese Prüfungen in GitHub Actions, GitLab CI, Terraform und einen pre-commit-Hook einbinden, mit copy-paste-fertigen Beispielen.

Für eine schnelle Syntaxprüfung eines Eintrags bietet der DKIM Syntax Check eine öffentliche API, die in jeder Pipeline wiederverwendbar ist.

Warum ein defekter DKIM-Record in der Produktion landet

Vier Hauptursachen erklären, wie ein ungültiger DKIM-Record die Standardabwehrmechanismen eines DNS-Deployments umgeht.

Manuelle Bearbeitung der DNS-Records. Ein Operator kopiert einen öffentlichen Schlüssel von einem Transaktions-Provider, vergisst ein Zeichen oder vertauscht zwei base64-Blöcke. Ohne Syntaxvalidierung wird der Eintrag genau so veröffentlicht.

Schlüsselrotation ohne Validierung. Das neue Schlüsselpaar wird erzeugt, der öffentliche Schlüssel kodiert, aber die Rotations-Pipeline prüft nicht erneut, ob die base64-Zeichenkette nach dem Export konsistent bleibt. Eine Abschneidung bei 254 Zeichen in manchen Admin-Oberflächen genügt, um die Signatur zu zerstören.

Ein DNS-Provider, der in zwei Chunks splittet. Eine TXT-Zeichenkette über 255 Zeichen muss vom DNS-Server fragmentiert und mit Anführungszeichen wieder zusammengesetzt werden. Manche Provider (oder UIs) splitten die Zeichenkette ohne Anführungszeichen: der Resolver erhält zwei getrennte Zeichenketten, und der rekonstruierte Schlüssel ist ungültig.

Kein Post-Deploy-Canary. Der Bug zeigt sich erst bei den ersten Mails nach der Änderung. Bis die Information ankommt (DMARC-Report, Support-Ticket), wurden bereits tausende Nachrichten als Spam eingestuft.

Shift-left: vor dem Merge validieren

Das Prinzip ist einfach: jede Änderung an einem mailbezogenen DNS-Record durchläuft eine automatische Validierung im Pull Request. Sie haben zwei Optionen für die Validierung.

Lokaler Open-Source-Linter. Werkzeuge wie dkim-checker (eine Go-CLI auf Basis von github.com/emersion/go-msgauth/dkim) oder eigene Skripte prüfen die Syntax eines Eintrags, die Konsistenz der Tags (v=DKIM1, k=rsa, p=...) und die Schlüssellänge. Vorteil: keine Netzwerkabhängigkeit, schnell. Nachteil: keine Aussage darüber, ob der Schlüssel tatsächlich veröffentlicht und erreichbar ist.

Cloud-Linter über API. Der CaptainDNS-Endpoint /v1/dkim/validate akzeptiert eine Domain und einen Selektor, fragt das veröffentlichte DNS ab, parst den Eintrag und liefert einen normalisierten Score, einen Zustand (valid, invalid, warning) und Empfehlungen. Vorteil: validiert den realen, von außen beobachteten Zustand, genau wie ein empfangender MX. Nachteil: hängt vom Netzwerk, vom API-Kontingent und vom DNS ab.

KriteriumLokaler LinterCaptainDNS-API
Syntax (v=, k=, p=)JaJa
Schlüssel im DNS veröffentlichtNeinJa
Effektive SchlüsselgrößeTeilweiseJa
Normalisierter ScoreNeinJa (im Backend berechnet)
NetzwerkabhängigkeitNeinJa
Anwendungsfallpre-commit, Syntax-LintPR-Validierung, post-deploy

Wichtiger Hinweis: Score, Schwellen und Empfehlungen werden von der CaptainDNS-API im Backend berechnet. Der CI-Client konsumiert einen bereits normalisierten Wert und führt keine eigene Berechnung durch.

Kombinieren Sie beides: lokaler Linter im pre-commit für schnelle Iterationen, API in der CI für die finale Validierung vor dem Merge.

GitHub-Actions-Pipeline zur Validierung eines DNS-Pull-Requests

Hier ein Workflow, der bei jedem Pull Request mit Änderungen an DNS-Dateien ausgelöst wird. Er extrahiert die DKIM-Records, ruft die CaptainDNS-API auf und blockiert den Merge, wenn der Zustand invalid oder der Score kleiner als 50 ist.

name: Validate DKIM records

on:
  pull_request:
    paths:
      - 'dns/**.tf'
      - 'dns/**.yaml'

jobs:
  validate-dkim:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Detect changed DNS files
        id: changes
        uses: dorny/paths-filter@v3
        with:
          filters: |
            dns:
              - 'dns/**'

      - name: Validate DKIM via CaptainDNS API
        if: steps.changes.outputs.dns == 'true'
        env:
          CDNS_API_KEY: ${{ secrets.CAPTAINDNS_API_KEY }}
        run: |
          set -euo pipefail
          for entry in $(yq '.dkim_selectors[]' dns/email.yaml); do
            domain=$(echo "$entry" | yq '.domain')
            selector=$(echo "$entry" | yq '.selector')

            response=$(curl -sS -X POST \
              -H "Authorization: Bearer ${CDNS_API_KEY}" \
              -H "Content-Type: application/json" \
              -d "{\"domain\":\"${domain}\",\"selector\":\"${selector}\"}" \
              https://api.captaindns.com/public/v1/dkim/validate)

            state=$(echo "$response" | jq -r '.state')
            score=$(echo "$response" | jq -r '.score')

            echo "Selector ${selector} for ${domain}: state=${state}, score=${score}"

            if [ "$state" = "invalid" ] || [ "$score" -lt 50 ]; then
              echo "::error::DKIM validation failed for ${selector}._domainkey.${domain}"
              exit 1
            fi
          done

Der API-Schlüssel wird über Ihre CaptainDNS-Konsole bereitgestellt und anschließend als GitHub-Secret hinterlegt (Settings > Secrets and variables > Actions). Verwenden Sie einen Schlüssel mit dem Scope dkim:read, um den Schaden im Falle eines Leaks zu begrenzen.

GitHub-Actions-Pipeline, die einen DKIM-Record über die CaptainDNS-API validiert

GitLab CI: Pipeline mit validate-Stage

In GitLab bleibt die Logik identisch. Die folgende .gitlab-ci.yml ergänzt einen Cache, um unveränderte Records nicht erneut zu validieren, und schickt im Fehlerfall eine Slack-Benachrichtigung.

stages:
  - validate
  - apply

validate-dkim:
  stage: validate
  image: alpine:3.20
  before_script:
    - apk add --no-cache curl jq yq bash
  cache:
    key: dkim-validation-$CI_COMMIT_REF_SLUG
    paths:
      - .dkim-cache/
  script:
    - mkdir -p .dkim-cache
    - |
      bash -c '
      set -euo pipefail
      for entry in $(yq ".dkim_selectors[]" dns/email.yaml); do
        domain=$(echo "$entry" | yq ".domain")
        selector=$(echo "$entry" | yq ".selector")
        cache_key=".dkim-cache/${domain}_${selector}.json"

        response=$(curl -sS -X POST \
          -H "Authorization: Bearer ${CAPTAINDNS_API_KEY}" \
          -H "Content-Type: application/json" \
          -d "{\"domain\":\"${domain}\",\"selector\":\"${selector}\"}" \
          https://api.captaindns.com/public/v1/dkim/validate)

        echo "$response" > "$cache_key"
        state=$(echo "$response" | jq -r ".state")
        score=$(echo "$response" | jq -r ".score")

        if [ "$state" = "invalid" ] || [ "$score" -lt 50 ]; then
          curl -X POST -H "Content-Type: application/json" \
            -d "{\"text\":\"DKIM validation failed: ${selector}._domainkey.${domain} (score=${score})\"}" \
            "$SLACK_WEBHOOK_URL"
          exit 1
        fi
      done
      '
  only:
    changes:
      - dns/**/*

Der GitLab-Cache hält die Antworten zwischen zwei Runs auf demselben Branch vor. Wenn sich ein Selektor nicht geändert hat, wird die API trotzdem aufgerufen (der DNS-Zustand kann sich unabhängig vom Code ändern), aber die Historie erlaubt das Nachvollziehen von Regressionen.

Terraform pre-apply: Validierung vor der DNS-Veröffentlichung

Wenn Ihr DNS von Terraform, Pulumi oder OpenTofu verwaltet wird, findet die logische Validierung vor apply statt. Der folgende Hook extrahiert die geänderten TXT-Records aus dem JSON-Plan, validiert sie über die CaptainDNS-API und blockiert das Apply bei Fehler.

#!/usr/bin/env bash
# scripts/dkim-preapply.sh
set -euo pipefail

terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json

# Geänderte DKIM-TXT-Records extrahieren
jq -r '
  .resource_changes[]
  | select(.type == "cloudflare_record" or .type == "aws_route53_record")
  | select(.change.after.type == "TXT")
  | select(.change.after.name | test("_domainkey"))
  | "\(.change.after.name)|\(.change.after.value // (.change.after.records | join("")))"
' tfplan.json > dkim-changes.txt

while IFS='|' read -r fqdn value; do
  # fqdn: selector._domainkey.captaindns.com
  selector="${fqdn%%._domainkey.*}"
  domain="${fqdn#*._domainkey.}"

  response=$(curl -sS -X POST \
    -H "Authorization: Bearer ${CDNS_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "{\"domain\":\"${domain}\",\"selector\":\"${selector}\",\"record\":\"${value}\"}" \
    https://api.captaindns.com/public/v1/dkim/validate)

  state=$(echo "$response" | jq -r '.state')
  if [ "$state" = "invalid" ]; then
    echo "Pre-apply DKIM check failed: ${fqdn}"
    echo "$response" | jq '.recommendations'
    exit 1
  fi
done < dkim-changes.txt

terraform apply tfplan.binary

Hängen Sie dieses Skript an ein make plan && make validate && make apply oder an die Atlantis-Stage, wenn Sie dieses GitOps-Muster nutzen. Die gleiche Logik gilt für Pulumi (über pulumi preview --json) und OpenTofu, da das JSON-Plan-Format kompatibel ist.

Lokales Lint im pre-commit

Um CI-Minuten zu sparen, fügen Sie einen pre-commit-Hook hinzu, der die Syntax der DKIM-Records noch vor dem Push validiert. Das Framework pre-commit.com ermöglicht die Orchestrierung mehrerer Python- oder Bash-Hooks.

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: dkim-syntax-lint
        name: DKIM syntax lint
        entry: scripts/dkim-lint.sh
        language: script
        files: ^dns/.*\.(tf|yaml)$
        pass_filenames: true

Das Skript dkim-lint.sh kann auf einen lokalen Go- oder Python-Parser zurückgreifen, der das Vorhandensein der Pflichttags (v=DKIM1, p=...) und die base64-Gültigkeit des öffentlichen Schlüssels prüft. Das reicht, um 80 % der Copy-Paste-Fehler abzufangen.

Wichtige Einschränkung: ein pre-commit-Lint kann weder den im DNS veröffentlichten Zustand prüfen noch Konflikte mit einem bereits aktiven Selektor. Die API-Validierung in der CI bleibt für diese Fälle unverzichtbar.

Integrationstests nach dem Deploy

Sobald das DNS angewendet wurde, prüfen Sie, dass die End-to-End-Kette funktioniert. Ein Smoke-Test schickt eine echte Mail und prüft den Header Authentication-Results.

#!/usr/bin/env bash
# scripts/dkim-smoke-test.sh
set -euo pipefail

# Eine Test-Mail über swaks senden
swaks --to test-canary@captaindns.com \
      --from noreply@captaindns.com \
      --server smtp.captaindns.com \
      --header "Subject: DKIM canary $(date +%s)" \
      --body "Canary message"

# 60 Sekunden auf Zustellung warten
sleep 60

# Den Authentication-Results-Header über IMAP oder einen eigenen Endpoint prüfen
result=$(curl -sS "https://canary.captaindns.com/last-mail/auth-results")

if ! echo "$result" | grep -q "dkim=pass"; then
  echo "Canary DKIM check FAILED: $result"
  # Automatischer Rollback
  terraform apply -auto-approve -var "dkim_selector=previous"
  exit 1
fi

Sie können auch synthetische Dienste wie Mail-Tester oder GlockApps verwenden, die eine API zum Abruf der Authentifizierungsergebnisse einer an eine Wegwerf-Adresse gesendeten Mail bereitstellen. Die Feedback-Schleife wird: Mail senden, Ergebnis lesen, dann DNS zurückrollen, wenn DKIM scheitert.

Für eine punktuelle Prüfung ohne Pipeline fragt der DKIM Record Check das DNS ab und zeigt den vollständigen Zustand des Selektors in wenigen Sekunden.

Mit SPF / DMARC kombinieren

DKIM allein zu validieren reicht nicht. Ein valid DKIM bei kaputtem SPF führt zu dmarc=fail für Mails, die nicht durch DKIM signiert sind. Ein schlecht ausgerichtetes DMARC verschlechtert die Zustellbarkeit selbst bei einem perfekten DKIM.

Eine vollständige Pipeline muss daher die drei Protokolle in derselben Stage validieren:

validate-email-auth:
  stage: validate
  script:
    - bash scripts/validate-spf.sh dns/email.yaml
    - bash scripts/validate-dkim.sh dns/email.yaml
    - bash scripts/validate-dmarc.sh dns/email.yaml
    - bash scripts/validate-bimi.sh dns/email.yaml  # optional

Jedes Skript ruft den entsprechenden Endpoint auf: /v1/spf/validate, /v1/dkim/validate, /v1/dmarc/validate. Die Bündelung von HTTP-Client, Response-Parsing und Schwellen in einem gemeinsamen Modul vermeidet Duplikation.

Eine einzige CI-Stage, die SPF, DKIM, DMARC und BIMI parallel über die CaptainDNS-API validiert

Empfohlener Ausrollungsplan

  1. Inventar: Listen Sie Ihre aktiven DKIM-Selektoren pro Domain und Subdomain auf
  2. CaptainDNS-API-Schlüssel mit Scope dkim:read + spf:read + dmarc:read bereitstellen
  3. pre-commit-Hook hinzufügen für das lokale Syntax-Lint
  4. CI-Workflow einrichten, der geänderte DNS-Dateien im PR validiert
  5. Terraform an einen pre-apply-Hook anschließen, der ungültige Records blockiert
  6. Post-Deploy-Canary aktivieren: eine Test-Mail nach jedem apply
  7. Auf weitere Protokolle ausweiten: SPF, DMARC, BIMI in derselben Validierungs-Stage

FAQ

Warum DKIM in der CI/CD validieren?

Ein defekter DKIM-Record erzeugt keinen sichtbaren Fehler auf DNS-Seite. Er bricht nur die Zustellbarkeit der Mails, was sich erst nach einigen Stunden über DMARC-Reports oder Nutzerrückmeldungen zeigt. Eine Validierung in der CI ermöglicht es, den Merge zu blockieren, bevor tausende Mails mit einer ungültigen Signatur versendet werden.

Was ist der Unterschied zwischen lokaler Validierung und API-Validierung?

Ein lokaler Linter prüft nur die Syntax eines Eintrags (Tags, base64-Format). Die CaptainDNS-API fragt das veröffentlichte DNS ab, parst den Eintrag genau so, wie ein Empfänger ihn sieht, und liefert einen im Backend berechneten Score zurück. Lokal ist schnell, aber blind für den real veröffentlichten Zustand.

Wie integriere ich die DKIM-Validierung in GitHub Actions?

Erstellen Sie einen Workflow, der bei Pull Requests auf den DNS-Ordner ausgelöst wird. Holen Sie die Paare aus Domain und Selektor, rufen Sie den Endpoint /v1/dkim/validate per curl auf, parsen Sie die Antwort mit jq und lassen Sie den Job scheitern, wenn der Zustand invalid oder der Score unter Ihrem Schwellenwert liegt (50 ist ein sinnvoller Ausgangspunkt).

Kann man ein terraform apply bei ungültigem DKIM blockieren?

Ja. Führen Sie terraform plan -out=tfplan aus, dann terraform show -json, um die geänderten TXT-Records zu extrahieren. Validieren Sie jeden Eintrag über die CaptainDNS-API, bevor Sie terraform apply starten. Wenn die Validierung fehlschlägt, verlassen Sie das Skript mit einem Exit-Code ungleich null, um das Apply zu blockieren.

Sollte man auch SPF und DMARC in derselben Pipeline validieren?

Ja. DKIM, SPF und DMARC sind voneinander abhängig. Ein isoliertes gültiges DKIM garantiert keine Zustellbarkeit, wenn SPF scheitert oder DMARC nicht ausgerichtet ist. Validieren Sie die drei in derselben CI-Stage, mit fail-fast beim ersten Fehler oder einem konsolidierten Report.

Welche Secrets sind für die CaptainDNS-API in der CI nötig?

Ein einziges Secret reicht: CAPTAINDNS_API_KEY. Provisionieren Sie ihn aus der Konsole, scopen Sie ihn auf die nötigen Endpoints (lesen DKIM/SPF/DMARC) und speichern Sie ihn im Secret-Manager Ihrer CI. Vermeiden Sie Admin-Keys in einer CI-Pipeline, die nur Lesezugriff benötigt.

Was tun, wenn der Record lokal passt, aber nach dem Deploy scheitert?

Drei häufige Ursachen: unvollständige DNS-Propagation (30 Minuten warten), DNS-Provider, der den TXT ohne Anführungszeichen gesplittet hat (mit dig +short TXT selektor._domainkey.domain prüfen), oder negativer Cache auf einem zwischengeschalteten Resolver. Der Post-Deploy-Smoke-Test sollte mit exponentiellem Backoff erneut versuchen, bevor ein Rollback ausgelöst wird.

Glossar

  • Shift-left: Praxis, Qualitätskontrollen in frühere Phasen des Entwicklungszyklus zu verlagern (PR, pre-commit), statt sie in der Produktion zu belassen.
  • Pre-commit-Hook: lokal vor jedem Git-Commit ausgeführtes Skript, das den Commit bei Fehler blockieren kann.
  • Pre-apply-Hook: vor terraform apply (oder dem Pulumi/OpenTofu-Äquivalent) ausgeführtes Skript, das die geplanten Änderungen validiert.
  • Canary: Test nach dem Deployment mit reduziertem Volumen, der Regressionen erkennt, bevor sie den gesamten Traffic betreffen.
  • DKIM-Score: normalisierter Wert (0 bis 100), den die CaptainDNS-API aus Syntax, Schlüsselgröße, Algorithmus und RFC-Empfehlungen berechnet.
  • ARC (Authenticated Received Chain): Protokoll nach RFC 8617, das die DKIM/SPF-Authentifizierungsergebnisse beim Weiterleiten von E-Mails bewahrt.

Testen Sie Ihre Integration mit dem DKIM Syntax Check: das Werkzeug stellt dieselbe API bereit, die in der CI/CD verwendet wird, ideal um einen Record vor dem Hinzufügen zu Ihrer Pipeline zu validieren.


Verwandte DKIM-Leitfäden zum Weiterlesen

Quellen

Ähnliche Artikel