60 lines
2.6 KiB
Go
60 lines
2.6 KiB
Go
// 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.<domain>" 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)
|
||
|
|
}
|
||
|
|
}
|