3.3 KiB
PowerDNS APIs for CoreDNS
A Go service exposing the subset of the PowerDNS Authoritative HTTP API that
acme.sh's dns_pdns provider uses, translating rrset operations into the
MySQL table read by the CoreDNS mysql plugin. Contract in one sentence:
PowerDNS at the HTTP edge, CoreDNS rows at the DB edge.
Configuration
Configuration is environment variables only — there is no config file.
| Variable | Purpose | Default |
|---|---|---|
PDNS_API_KEY |
Shared secret for X-API-Key |
(required, no default) |
LISTEN_ADDR |
HTTP listen address | :3000 |
MYSQL_DSN |
Go MySQL DSN (user:pass@tcp(host:3306)/dbname?parseTime=true) |
(required) |
TABLE_PREFIX |
Table name prefix; must mirror the Corefile table_prefix directive (e.g. coredns_) |
coredns_ |
PDNS_SERVER_ID |
Server id in API paths | localhost |
DEFAULT_TTL |
TTL when acme.sh doesn't send one | 120 |
LOG_LEVEL |
debug / info / warn / error |
info |
PUID |
UID to drop privileges to after startup | (required) |
PGID |
GID to drop privileges to after startup | (required) |
Startup fails fast, naming the missing variable, if a required one is absent.
Privilege drop
The container starts the binary as root. Before serving any traffic, the
binary irrevocably drops to PGID/PUID (setgroups → setgid → setuid).
The image does not declare a USER directive — the drop is the binary's
job, not the container's. If started as a non-root user already, the drop is
skipped and the process serves as the invoking user.
Running
docker run --rm \
--publish 3000:3000 \
--env PDNS_API_KEY=changeme \
--env MYSQL_DSN="coredns:coredns@tcp(127.0.0.1:3306)/coredns?parseTime=true" \
--env TABLE_PREFIX=coredns_ \
--env PDNS_SERVER_ID=localhost \
--env DEFAULT_TTL=120 \
--env LOG_LEVEL=info \
--env PUID=1000 \
--env PGID=1000 \
git.bjphoster.com/source/coredns-powerdns-api-wrapper:latest
/healthz is an unauthenticated health check that pings MySQL; it exposes no
data.
acme.sh usage
docker run -it --rm -v ~/acme.sh:/acme.sh \
-e PDNS_Url="https://your-endpoint" \
-e PDNS_ServerId="localhost" \
-e PDNS_Token="$PDNS_API_KEY" \
-e PDNS_Ttl="120" \
neilpang/acme.sh --issue --staging --dns dns_pdns \
-d example.com -d www.example.com
For production issuance, drop --staging (and optionally add
--server letsencrypt).
Implemented API surface, all under /api/v1, all requiring X-API-Key:
GET /api/v1/servers/{server_id}/zonesGET /api/v1/servers/{server_id}/zones/{zone_id}PATCH /api/v1/servers/{server_id}/zones/{zone_id}(rrsets,REPLACE/DELETE)PUT /api/v1/servers/{server_id}/zones/{zone_id}/notify
Propagation / --dnssleep guidance
Record writes to an existing zone are visible to CoreDNS immediately —
record lookups hit MySQL live, so no --dnssleep is needed for TXT changes.
Only newly created zones are subject to the Corefile's zone_update_interval
(120s), since that setting only gates the cached zone list. Verified
empirically; see docs/SEMANTICS.md.
Known limitations
- Only the acme.sh API slice is implemented: no zone CRUD beyond
GET, no cryptokeys/metadata/search/TSIG, no DNSSEC. - This service serves no DNS itself — CoreDNS remains the resolver.
- rrset types supported for writes:
TXT,A,AAAA,CNAME,NS.