// Package dnspublish publishes an arbitrary-name/arbitrary-value DNS TXT record via one // of four provider APIs (Cloudflare, Route53, DigitalOcean, Google Cloud DNS) — used to // automate what would otherwise be an admin manually pasting a DKIM TXT record into // their DNS provider's own UI. Deliberately separate from internal/acmecert's DNS-01 // provider wiring: go-acme/lego's challenge.Provider interface is hardcoded to // "_acme-challenge." naming and an ACME-specific value (confirmed by reading // lego's dns01.GetChallengeInfo and a concrete provider), so it can't be reused for a // record with an arbitrary name and value like a DKIM record. package dnspublish import "fmt" // Credentials selects a provider and holds every provider's own field set — fields // unrelated to the selected Provider are simply ignored. Mirrors the flat-struct shape // internal/config's [LetsEncrypt] section already uses for the same reason: these are // admin-entered settings where only one provider is ever configured at a time. type Credentials struct { Provider string // "cloudflare", "route53", "digitalocean", "gcloud" // ZoneName is the DNS zone/domain resource the provider hosts, e.g. "example.com" // — used to resolve that provider's own zone/domain ID before publishing. ZoneName string CloudflareAPIToken string Route53AccessKeyID string Route53SecretAccessKey string Route53Region string DigitalOceanAPIToken string GCloudProject string // GCloudServiceAccountJSON is the service account key's raw JSON content (not a // file path) — unlike [LetsEncrypt]'s global, file-path-based setting, this needs // to work for per-domain, DB-stored credentials too. GCloudServiceAccountJSON string } // txtTTL is a fixed, low TTL for every record this package writes — not configurable // in this pass, so a rotation's new value propagates reasonably quickly regardless of // what the zone's other records use. const txtTTL = 300 // SetTXTRecord creates or updates (upserts) a TXT record at recordFQDN (e.g. // "selector._domainkey.example.com") to value, in the zone named by creds.ZoneName, via // whichever provider creds.Provider selects. func SetTXTRecord(creds Credentials, recordFQDN, value string) error { switch creds.Provider { case "cloudflare": return setTXTRecordCloudflare(creds, recordFQDN, value) case "route53": return setTXTRecordRoute53(creds, recordFQDN, value) case "digitalocean": return setTXTRecordDigitalOcean(creds, recordFQDN, value) case "gcloud": return setTXTRecordGCloud(creds, recordFQDN, value) default: return fmt.Errorf("dnspublish: unknown or unset DNS provider %q", creds.Provider) } }