coredns/secondary/docker-compose.yml
Ryan Malloy 618e9504e7 secondary: scaffold public CoreDNS secondary on ns.supported.systems
Adds a second non-HE public secondary that pulls AXFR from dell01 (the
hidden primary at 154.27.180.210) and answers public queries on
ns.supported.systems (64.177.113.227, 2001:19f0:5c00:4daa:5400:6ff:fe2d:38fa).

  secondary/
    Corefile                            generated, 84 zones + REFUSED catch-all
    docker-compose.yml                  CoreDNS in host-net mode
    Makefile                            up/down/logs/regen/test/axfr-test
    .env / .env.example                 image pin + bind IPs
    scripts/generate-secondary-corefile.sh  reads ../zones/*.zone

  scripts/notify-he.py → notify-secondaries.py
                                        adds 64.177.113.227 as a second
                                        NOTIFY target alongside HE's
                                        216.218.130.2

Uses CoreDNS's `bind` plugin to avoid colliding with systemd-resolved
on loopback :53. Authoritative-only — non-listed zones get REFUSED, no
recursion. AXFR pull requires opening TCP/53 on dell01's FortiWiFi for
the secondary's IP (manual step, separate from this commit).
2026-05-20 18:40:11 -06:00

34 lines
1.2 KiB
YAML

services:
coredns:
image: ${COREDNS_IMAGE}
container_name: coredns-secondary
restart: unless-stopped
command: ["-conf", "/etc/coredns/Corefile"]
# Host networking is required for two reasons:
#
# 1. CoreDNS's `secondary` plugin authorizes inbound NOTIFY by
# source IP (`transfer from 154.27.180.210`). With bridge
# networking, Docker's userland proxy rewrites the visible
# source to a 172.x docker0 address, and the auth check fails.
#
# 2. AXFR responses can be large; UDP/TCP source-IP preservation
# matters for both directions of the conversation.
#
# Host networking means this container OWNS host ports 53/udp + 53/tcp.
# On a dedicated NS box that's exactly what you want.
network_mode: host
volumes:
- ./Corefile:/etc/coredns/Corefile:ro
# CoreDNS's distroless image has no shell, so the conventional
# `wget /health` healthcheck silently fails. The `-version` flag
# exits 0 only if the binary is runnable — a thin but honest probe.
healthcheck:
test: ["CMD", "/coredns", "-version"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s