coredns/scripts/generate-certs.sh
Ryan Malloy 066ba1892a coredns: DoT (:853) + DoH (:443) listeners with self-signed cert
- New Corefile snippet (common) shared across plain DNS / DoT / DoH so
  zone-loading + forward + cache stay DRY across all three transports
- scripts/generate-certs.sh: openssl-only self-signed RSA cert with SANs
  for localhost / 127.0.0.1 / ::1 / coredns / dns.local. Idempotent —
  skips regeneration if cert is valid >24h ahead; FORCE=1 to rotate.
- Key chmod is 0644 so the CoreDNS container's nonroot user can read it
  via the bind mount. Acceptable for local dev; production should mount
  real certs with proper UID/GID.
- DOT_PORT=8853, DOH_PORT=8443 (avoids Caddy already-on-443 collision)
- Makefile: `make certs`, `make test-tls`
- All three transports verified end-to-end (dig +tls, dig +https,
  curl with raw RFC 8484 wire format)
2026-05-14 01:12:25 -06:00

48 lines
1.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Generate a self-signed RSA cert for CoreDNS DoT/DoH (local dev only).
# Production deployments should mount real Let's Encrypt certs (e.g. from
# Caddy's shared volume) into certs/ instead.
#
# SANs cover the names a local resolver client is likely to use:
# - localhost (loopback by name)
# - 127.0.0.1 / ::1 (loopback by IP)
# - coredns (docker container DNS name)
# - dns.local (convenient pinning hostname)
set -euo pipefail
CERT_DIR="${CERT_DIR:-certs}"
DAYS="${DAYS:-825}" # 825 is the historical macOS/Apple cap; safe ceiling
mkdir -p "$CERT_DIR"
if [[ -f "$CERT_DIR/cert.pem" && -f "$CERT_DIR/key.pem" ]]; then
# Skip regeneration if not expired and re-prep was not forced.
if [[ "${FORCE:-0}" != "1" ]]; then
# openssl returns 0 if cert is valid beyond the given window (24h here).
if openssl x509 -checkend 86400 -noout -in "$CERT_DIR/cert.pem" >/dev/null 2>&1; then
echo "Cert at $CERT_DIR/cert.pem still valid (>24h). Set FORCE=1 to regenerate."
exit 0
fi
fi
fi
# Single-shot self-signed cert with SANs. -addext requires openssl >= 1.1.1.
openssl req -x509 -newkey rsa:2048 -nodes -days "$DAYS" \
-keyout "$CERT_DIR/key.pem" \
-out "$CERT_DIR/cert.pem" \
-subj "/CN=coredns-local" \
-addext "subjectAltName=DNS:localhost,DNS:coredns,DNS:dns.local,IP:127.0.0.1,IP:::1" \
>/dev/null 2>&1
# 0644 (not 0600) on the key so the CoreDNS container's `nonroot` user
# can read it via the bind mount. Acceptable for a local-dev self-signed
# cert whose private key never leaves this directory. For production
# certs, mount with explicit UID/GID via :ro,uid=65532 or use a tmpfs/
# secret instead.
chmod 644 "$CERT_DIR/key.pem"
chmod 644 "$CERT_DIR/cert.pem"
echo "Generated $CERT_DIR/{cert,key}.pem (valid ${DAYS} days)"
openssl x509 -in "$CERT_DIR/cert.pem" -noout -subject -issuer -dates \
-ext subjectAltName 2>&1 | sed 's/^/ /'