Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(dns): add DNS auto-provisioning via RFC 2136 dynamic updates#24

Draft
fobispo-tc wants to merge 2 commits into
godaddy:mainfrom
fobispo-tc:feat/dns-auto-provisioning
Draft

feat(dns): add DNS auto-provisioning via RFC 2136 dynamic updates#24
fobispo-tc wants to merge 2 commits into
godaddy:mainfrom
fobispo-tc:feat/dns-auto-provisioning

Conversation

@fobispo-tc
Copy link
Copy Markdown

Summary

  • Add port.DNSProvisioner interface for automatic DNS record creation/deletion
  • Implement RFC 2136 (DDNS) adapter with TSIG authentication using miekg/dns
  • Hook provisioning into VerifyDNS (auto-create before verify) and Revoke (best-effort cleanup)
  • Add dns.provisioner config section — independent of the dns.type verifier setting
  • Includes the _ans-badge URL fix from PR fix(domain): point _ans-badge DNS record URL at transparency log #23 (badge records now point to TL, not agent endpoint)

Motivation

In org-level deployments where the RA controls the DNS zone, requiring operators to manually add _ans, _ans-badge, and _443._tcp TLSA records between VerifyACME and VerifyDNS is unnecessary friction. This feature lets the RA provision them automatically via RFC 2136 dynamic DNS updates, collapsing the registration flow from 4 steps to 2.

Configuration

dns:
  type: lookup                      # verifier (unchanged)
  server: "8.8.8.8:53"
  provisioner:                      # NEW — optional
    type: ddns
    ddns:
      server: "ns1.example.com:53"  # authoritative nameserver
      zone: "example.com."          # zone to update
      tsig-name: "ans-updater."     # TSIG key name
      tsig-secret: "base64..."      # TSIG shared secret
      tsig-algorithm: "hmac-sha256" # default
      timeout: 5s                   # default

When dns.provisioner is absent or empty, behavior is unchanged (manual DNS).

Design Decisions

  1. Separate DNSProvisioner interface — not combined with DNSVerifier (ISP compliance, matches existing port pattern)
  2. Provisioning inside VerifyDNS — TLSA records need server cert fingerprint (only available after VerifyACME), so provisioning can't happen earlier
  3. Best-effort cleanup on Revoke — DNS deletion failure is logged but doesn't block revocation (TL event is authoritative)
  4. Independent config — verifier and provisioner are orthogonal (dns.type: lookup + dns.provisioner.type: ddns)

Files Changed

File Change
internal/port/dns.go Add DNSProvisioner interface
internal/adapter/dns/ddns.go New — RFC 2136 adapter with TSIG
internal/adapter/dns/ddns_test.go New — 10 tests against in-process UDP server
internal/adapter/dns/noop_provisioner.go New — no-op for dev/test
internal/config/config.go Add provisioner config structs + validation + PublicBaseURL
internal/domain/dnsrecords.go Accept tlPublicBaseURL param for badge URL fix
internal/domain/dnsrecords_test.go 2 new badge URL tests
internal/ra/service/registration.go Add dnsProvisioner field + builder
internal/ra/service/lifecycle.go Hook provisioning into VerifyDNS + Revoke cleanup
internal/ra/handler/*.go Thread TL public URL for badge fix
cmd/ans-ra/main.go Wire provisioner from config
config/ra-{local,docker}.yaml Document new config options

Test plan

  • All 23 packages pass (go test ./...)
  • 10 new DDNS adapter tests (TXT, TLSA, delete, idempotency, TSIG, error cases)
  • 2 new badge URL domain tests
  • Clean build (go build ./...)
  • Deploy with DDNS config against a real BIND/PowerDNS server
  • Register agent → verify records auto-created
  • Revoke agent → verify records auto-deleted

@fobispo-tc fobispo-tc force-pushed the feat/dns-auto-provisioning branch from 94a3cf2 to 12d27cd Compare May 22, 2026 00:52
Add a port.DNSProvisioner interface and a DDNS adapter that
auto-creates and deletes DNS records using RFC 2136 dynamic updates
with TSIG authentication.

When a DNS provisioner is configured:
- Registration issues certs and provisions DNS in one call,
  going straight to ACTIVE (no ACME challenge, no verify-dns)
- Revoke auto-deletes DNS records (best-effort)
- registration.domain-suffix auto-qualifies short hostnames
  (e.g. "my-agent" → "my-agent.agents.example.com")

When no provisioner is configured, the original ACME-based flow
is unchanged.

Includes the _ans-badge URL fix: badge records point to the TL
badge endpoint when tl-client.public-base-url is configured.

Signed-off-by: Fernando Obispo <[email protected]>
Signed-off-by: Francisco Obispo <[email protected]>
@fobispo-tc fobispo-tc force-pushed the feat/dns-auto-provisioning branch from 12d27cd to 7b0872c Compare May 22, 2026 06:40
- Use url.JoinPath for badge URL construction instead of string
  concatenation (safer path joining and escaping)
- Rename WithTLPublicBaseURL parameter from 'url' to 'publicBaseURL'
  to avoid shadowing net/url
- Don't default PublicBaseURL to BaseURL — only use it when
  explicitly configured, preventing internal hostnames from leaking
  into public DNS records

Signed-off-by: Fernando Obispo <[email protected]>
Signed-off-by: Francisco Obispo <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant