Hurricane Electric requires asymmetric transfer config:
- AXFR pull from 216.218.133.2 (slave.dns.he.net / ns4.he.net)
- NOTIFY destination 216.218.130.2 (ns1.he.net)
CoreDNS's transfer plugin uses a single bidirectional `to` list for
both, which is fine in principle but breaks in a confirmed bug: any
`to` with more than one specific IPv4 silently kills server-block
listener startup (no error, zones load, but :53 never binds).
Reproduced on 1.11.3 + 1.12.2 even with a minimal fresh `docker run`.
Workaround:
- Corefile keeps `transfer { to * }` (open AXFR; firewall does the
real source-IP filtering on TCP/53)
- scripts/notify-he.py crafts and sends NOTIFY messages directly to
216.218.130.2 (only). Pure-stdlib Python — no dependencies.
- Makefile `prep` target runs notify-he.py after prepare-zones.sh
so every zone-bump fires NOTIFY automatically.
Verified end-to-end: HE acks NOTIFY (rcode=0) for the 10 zones it
hosts as secondaries; remaining 81 return REFUSED (rcode=5) because
HE doesn't have them configured yet. Note: HE's free slave service
acks NOTIFY but only actually re-pulls AXFR on its hourly poll cycle
(observed behavior — they're poll-based by design). NOTIFY still
useful long-term in case HE changes that behavior; harmless either way.
108 lines
4.0 KiB
Makefile
108 lines
4.0 KiB
Makefile
.DEFAULT_GOAL := help
|
|
SHELL := /usr/bin/env bash
|
|
COMPOSE := docker compose
|
|
|
|
# Source .env so $(CADDY_HOSTNAME) etc. are available in recipes.
|
|
include .env
|
|
export
|
|
|
|
.PHONY: help prep certs up down restart logs logs-caddy ps test test-tls \
|
|
test-public reload clean tls-up cert-watch caddy-rebuild
|
|
|
|
help: ## Show this help
|
|
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-14s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
|
|
|
prep: ## Re-inject SOA + bump serial, then NOTIFY HE (auto-fires AXFR)
|
|
@./scripts/prepare-zones.sh
|
|
@./scripts/notify-he.py --quiet || echo " (NOTIFY had failures; HE will still re-poll on SOA refresh)"
|
|
|
|
certs: ## Generate self-signed dev cert (only useful if not using Caddy ACME)
|
|
@./scripts/generate-certs.sh
|
|
|
|
caddy-rebuild: ## Rebuild the Caddy image (after editing caddy/Dockerfile)
|
|
$(COMPOSE) build --no-cache caddy
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Production / Let's Encrypt flow
|
|
# ---------------------------------------------------------------------------
|
|
|
|
tls-up: prep ## Bring up Caddy → wait for cert → start CoreDNS (one command)
|
|
@if [ -z "$$VULTR_API_KEY" ]; then \
|
|
echo "ERROR: VULTR_API_KEY is not exported. Set it in your shell:"; \
|
|
echo " export VULTR_API_KEY=..."; \
|
|
exit 1; \
|
|
fi
|
|
@mkdir -p caddy-data caddy-config
|
|
$(COMPOSE) up -d caddy
|
|
@echo ""
|
|
@echo "Waiting for Caddy to provision cert for $(CADDY_HOSTNAME)..."
|
|
@echo "(DNS-01 via Vultr typically takes 30-90s; press Ctrl-C to abort)"
|
|
@for i in $$(seq 1 90); do \
|
|
if [ -e caddy-data/caddy/cert.pem ]; then \
|
|
echo ""; echo " ✓ cert ready after $${i}0s"; break; \
|
|
fi; \
|
|
printf '.'; sleep 10; \
|
|
done
|
|
@test -e caddy-data/caddy/cert.pem || \
|
|
(echo ""; echo "FAILED — see logs: make logs-caddy"; exit 1)
|
|
$(COMPOSE) up -d coredns
|
|
@sleep 3 && $(COMPOSE) logs --tail=15 coredns
|
|
|
|
cert-watch: ## Tail Caddy logs while it provisions the cert
|
|
$(COMPOSE) logs -f caddy
|
|
|
|
logs-caddy: ## Tail Caddy logs
|
|
$(COMPOSE) logs -f caddy
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Day-to-day operations
|
|
# ---------------------------------------------------------------------------
|
|
|
|
down: ## Stop & remove all containers
|
|
$(COMPOSE) down
|
|
|
|
restart: ## Restart CoreDNS (does not re-prep zones / re-issue cert)
|
|
$(COMPOSE) restart coredns
|
|
|
|
reload: prep ## Re-prep zones; CoreDNS auto-plugin picks changes up
|
|
@echo "Zones re-prepared. CoreDNS reloads files every 30s (auto plugin)."
|
|
|
|
logs: ## Tail CoreDNS logs
|
|
$(COMPOSE) logs -f coredns
|
|
|
|
ps: ## Show container status
|
|
$(COMPOSE) ps
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Smoke tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
test: ## Smoke-test plain DNS
|
|
@echo "Querying acrazy.org @ 127.0.0.1:$(DNS_PORT) (plain DNS)"
|
|
@dig @127.0.0.1 -p $(DNS_PORT) acrazy.org SOA +short
|
|
@dig @127.0.0.1 -p $(DNS_PORT) acrazy.org NS +short
|
|
@dig @127.0.0.1 -p $(DNS_PORT) or.acrazy.org A +short
|
|
|
|
test-tls: ## Smoke-test DoT + DoH against LOCAL endpoints (trusts cert via system CAs)
|
|
@echo "=== DoT @ 127.0.0.1:$(DOT_PORT), expecting cert for $(CADDY_HOSTNAME) ==="
|
|
@dig @127.0.0.1 -p $(DOT_PORT) +tls +tls-hostname=$(CADDY_HOSTNAME) \
|
|
acrazy.org SOA +short
|
|
@echo ""
|
|
@echo "=== DoH @ https://$(CADDY_HOSTNAME):$(DOH_PORT)/dns-query ==="
|
|
@dig @$(CADDY_HOSTNAME) -p $(DOH_PORT) +https acrazy.org A +short
|
|
|
|
test-public: ## Smoke-test using the public hostname (DoT/DoH ports must be open + DNS A record set)
|
|
@echo "=== DoT on public hostname @ port 853 ==="
|
|
@dig @$(CADDY_HOSTNAME) +tls cloudflare.com A +short
|
|
@echo "=== DoH on public hostname @ port 443 ==="
|
|
@dig @$(CADDY_HOSTNAME) +https cloudflare.com A +short
|
|
|
|
clean: down ## Remove containers + prepared zones + dev self-signed certs
|
|
rm -rf zones-prepared/*.zone certs/*.pem
|
|
|
|
clean-caddy: down ## Also wipe Caddy's data dir (forces re-issuance from scratch!)
|
|
@echo "About to delete caddy-data/ — this will force re-issuance from LE."
|
|
@echo "Hit Ctrl-C in 5s to abort..."
|
|
@sleep 5
|
|
rm -rf caddy-data caddy-config
|