Phase 0 scaffolding: RFC 2136 plugin groundwork (inactive)
Lays the groundwork for a future CoreDNS rfc2136 plugin that will accept TSIG-authenticated dynamic DNS updates from Caddy (via caddy-dns/rfc2136), enabling self-hosted ACME DNS-01 cert automation without depending on registrar APIs. Nothing in this commit is active at runtime: - Corefile additions are commented out - coredns/Dockerfile references a plugin repo that doesn't exist yet - scripts/acme-add-domain.sh just appends CNAME glue but has nothing to talk to until the plugin is built Architecture and implementation plan: ~/.claude/plans/dood-does-coredns-offer-enumerated-piglet.md Secret management: TSIG key generated and stored in .env.local (gitignored). .env.local.example documents the expected shape.
This commit is contained in:
parent
9e345fa488
commit
48cddc91cf
7
.env.local.example
Normal file
7
.env.local.example
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Template for .env.local — copy to .env.local and fill in real values.
|
||||||
|
# .env.local is gitignored; this file documents what must be in it.
|
||||||
|
|
||||||
|
# TSIG shared secret for rfc2136 plugin + caddy-dns/rfc2136.
|
||||||
|
# Generate with: openssl rand -base64 32
|
||||||
|
# Rotate by regenerating + restarting CoreDNS + Caddy.
|
||||||
|
ACME_TSIG_SECRET=
|
||||||
26
Corefile
26
Corefile
@ -57,3 +57,29 @@ https://.:443 {
|
|||||||
tls /etc/coredns/certs/cert.pem /etc/coredns/certs/key.pem
|
tls /etc/coredns/certs/cert.pem /etc/coredns/certs/key.pem
|
||||||
import common
|
import common
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── PHASE 0 SCAFFOLDING — NOT YET ACTIVE ──────────────────────────
|
||||||
|
# Dynamic-update server for ACME DNS-01 challenges (RFC 2136 + TSIG).
|
||||||
|
# Caddy uses caddy-dns/rfc2136 to push TSIG-signed UPDATE messages here;
|
||||||
|
# the plugin stores TXT records in memory and serves them for Let's
|
||||||
|
# Encrypt's validation queries.
|
||||||
|
#
|
||||||
|
# Activation requires:
|
||||||
|
# 1. The coredns-rfc2136 plugin built into a custom CoreDNS image
|
||||||
|
# (see coredns/Dockerfile and docker-compose.yml build directive).
|
||||||
|
# 2. ACME_TSIG_SECRET set in .env.local (already generated).
|
||||||
|
# 3. zones/supported.systems.zone delegating `auth` sub-zone to dell01:
|
||||||
|
# auth 300 IN NS dns.supported.systems.
|
||||||
|
# 4. FortiWiFi firewall opening UDP/53 to dell01 from 0.0.0.0/0.
|
||||||
|
#
|
||||||
|
# Until those land, this block is a comment. The plan lives at
|
||||||
|
# ~/.claude/plans/dood-does-coredns-offer-enumerated-piglet.md
|
||||||
|
#
|
||||||
|
# .:53 auth.supported.systems {
|
||||||
|
# rfc2136 auth.supported.systems {
|
||||||
|
# tsig-key acme-update-key. hmac-sha256 {$ACME_TSIG_SECRET}
|
||||||
|
# ttl 60
|
||||||
|
# }
|
||||||
|
# errors
|
||||||
|
# log
|
||||||
|
# }
|
||||||
|
|||||||
41
coredns/Dockerfile
Normal file
41
coredns/Dockerfile
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Custom CoreDNS image that bakes in the rfc2136 plugin for accepting
|
||||||
|
# RFC 2136 dynamic updates (TSIG-authenticated). The upstream
|
||||||
|
# coredns/coredns image does NOT include this plugin — CoreDNS itself
|
||||||
|
# has no plugin for accepting dynamic updates anywhere in its ecosystem
|
||||||
|
# as of v1.12.2, so we ship our own.
|
||||||
|
#
|
||||||
|
# Stage 1: build CoreDNS from source with our plugin appended to
|
||||||
|
# plugin.cfg. Stage 2: distroless runtime image.
|
||||||
|
#
|
||||||
|
# Plugin source: <REPO_URL_PLACEHOLDER>
|
||||||
|
# This Dockerfile is currently SCAFFOLDING ONLY — the plugin repo does
|
||||||
|
# not yet exist. Building this image will fail until Phase 1 ships.
|
||||||
|
|
||||||
|
# ─── Stage 1: builder ──────────────────────────────────────────────
|
||||||
|
FROM golang:1.22-alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache git make
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
ARG COREDNS_REF=v1.12.2
|
||||||
|
RUN git clone --depth 1 --branch ${COREDNS_REF} https://github.com/coredns/coredns.git .
|
||||||
|
|
||||||
|
# Inject our plugin into plugin.cfg. Must come BEFORE the `cache` plugin
|
||||||
|
# so authoritative answers from rfc2136 aren't intercepted by cache.
|
||||||
|
ARG PLUGIN_REPO=git.supportedsystems.net/rpm/coredns-rfc2136
|
||||||
|
ARG PLUGIN_REF=latest
|
||||||
|
RUN sed -i "/^cache:cache$/i rfc2136:${PLUGIN_REPO}" plugin.cfg && \
|
||||||
|
go get ${PLUGIN_REPO}@${PLUGIN_REF}
|
||||||
|
|
||||||
|
RUN make GOFLAGS="-ldflags=-w -s"
|
||||||
|
|
||||||
|
# ─── Stage 2: runtime ──────────────────────────────────────────────
|
||||||
|
FROM gcr.io/distroless/static-debian12
|
||||||
|
|
||||||
|
COPY --from=builder /build/coredns /coredns
|
||||||
|
|
||||||
|
# Match upstream's exposed ports.
|
||||||
|
EXPOSE 53 53/udp 853 443 9153 8080
|
||||||
|
|
||||||
|
ENTRYPOINT ["/coredns"]
|
||||||
|
CMD ["-conf", "/etc/coredns/Corefile"]
|
||||||
66
scripts/acme-add-domain.sh
Executable file
66
scripts/acme-add-domain.sh
Executable file
@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Bootstraps a domain for self-hosted ACME DNS-01 cert automation.
|
||||||
|
#
|
||||||
|
# Adds a single `_acme-challenge.<domain> CNAME <uuid>.auth.supported.systems`
|
||||||
|
# record to the zone file. After this one-time edit + push, all future cert
|
||||||
|
# issuance and renewal for the domain happens via dynamic RFC 2136 UPDATEs
|
||||||
|
# to the auth.supported.systems sub-zone (served by CoreDNS + rfc2136 plugin
|
||||||
|
# on dell01) — no further zone-file churn.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/acme-add-domain.sh example.com
|
||||||
|
#
|
||||||
|
# After running:
|
||||||
|
# 1. Verify the line was added correctly: `tail -3 zones/example.com.zone`
|
||||||
|
# 2. Commit: `git add zones/example.com.zone`
|
||||||
|
# 3. Push: rsync to dell01, `make prep`
|
||||||
|
# 4. Configure Caddy with the same UUID (see plan Phase 6).
|
||||||
|
#
|
||||||
|
# This script is SCAFFOLDING — the upstream rfc2136 plugin and the
|
||||||
|
# auth.supported.systems delegation must be operational before the
|
||||||
|
# generated CNAMEs actually do anything useful.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DOMAIN="${1:?usage: $(basename "$0") <domain>}"
|
||||||
|
ZONE_FILE="zones/${DOMAIN}.zone"
|
||||||
|
|
||||||
|
# Must run from the repo root so the relative zone path resolves.
|
||||||
|
if [[ ! -f "$ZONE_FILE" ]]; then
|
||||||
|
echo "Zone file not found: $ZONE_FILE" >&2
|
||||||
|
echo "Run from the coredns repo root, and ensure the zone exists." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Refuse to add a duplicate.
|
||||||
|
if grep -qE "^_acme-challenge\b" "$ZONE_FILE"; then
|
||||||
|
echo "_acme-challenge record already present in $ZONE_FILE — skipping." >&2
|
||||||
|
echo "Existing line(s):" >&2
|
||||||
|
grep -E "^_acme-challenge\b" "$ZONE_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||||
|
LINE=$(printf "_acme-challenge\t300\tIN\tCNAME\t%s.auth.supported.systems" "$UUID")
|
||||||
|
|
||||||
|
echo "$LINE" >> "$ZONE_FILE"
|
||||||
|
|
||||||
|
echo "Added to $ZONE_FILE:"
|
||||||
|
echo " $LINE"
|
||||||
|
echo ""
|
||||||
|
echo "Caddyfile snippet for ${DOMAIN}:"
|
||||||
|
cat <<EOF
|
||||||
|
${DOMAIN} {
|
||||||
|
tls {
|
||||||
|
dns rfc2136 {
|
||||||
|
key_name acme-update-key.
|
||||||
|
key_alg hmac-sha256
|
||||||
|
key {env.ACME_TSIG_SECRET}
|
||||||
|
server dns.supported.systems:53
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# ... rest of site config
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
echo ""
|
||||||
|
echo "Next steps: git commit, rsync to dell01, run 'make prep'."
|
||||||
Loading…
x
Reference in New Issue
Block a user