Documentation
¶
Overview ¶
Package dnszones manages whole DNS zones at Cloudflare and Route53: zones, their assigned name servers, record sets with routing policies, delegation checks, propagation checks, zone file import and export, and templates.
Index ¶
- Constants
- Variables
- func ApplyPlan(ctx context.Context, p Provider, zone Zone, plan Plan) (int, error)
- func CheckCapabilities(rs RecordSet, caps Capabilities) error
- func ExportBIND(zone string, sets []RecordSet) string
- func ExportJSON(zone string, sets []RecordSet) ([]byte, error)
- func FQDN(rel, zone string) string
- func FormatRR(rr dns.RR) (string, bool)
- func HasErrors(issues []Issue) bool
- func IsManagedApexType(rs RecordSet) bool
- func NormalizeDomain(d string) string
- func QType(t string) (uint16, error)
- func RelativeName(name, zone string) string
- func SameContent(a, b RecordSet) bool
- func SortSets(sets []RecordSet)
- func ValidateHealthCheck(hc HealthCheck) error
- type Alias
- type Answer
- type Capabilities
- type Change
- type Cloudflare
- func (c *Cloudflare) Capabilities() Capabilities
- func (c *Cloudflare) CreateZone(ctx context.Context, name string) (Zone, error)
- func (c *Cloudflare) DeleteRecordSet(ctx context.Context, zone Zone, k Key) error
- func (c *Cloudflare) DeleteZone(ctx context.Context, id string) error
- func (c *Cloudflare) GetZone(ctx context.Context, id string) (Zone, error)
- func (c *Cloudflare) ListRecordSets(ctx context.Context, zone Zone) ([]RecordSet, error)
- func (c *Cloudflare) ListZones(ctx context.Context) ([]Zone, error)
- func (c *Cloudflare) Name() string
- func (c *Cloudflare) UpsertRecordSet(ctx context.Context, zone Zone, rs RecordSet) error
- type CloudflareError
- type DNSQuerier
- type Delegation
- type Document
- type HealthCheck
- type HealthChecker
- type Issue
- type Key
- type Plan
- type Propagation
- type PropagationAnswer
- type Provider
- type Querier
- type RecordSet
- func Discover(ctx context.Context, q Querier, zone string, extra, servers []string) []RecordSet
- func MergeTemplate(existing, sets []RecordSet) []RecordSet
- func Normalize(rs RecordSet, zone string, defaultTTL int) (RecordSet, error)
- func ParseBIND(text, zone string, defaultTTL int) ([]RecordSet, []string, error)
- func ParseJSON(data []byte) ([]RecordSet, error)
- type ResolverDelegation
- type Route53
- func (r *Route53) Capabilities() Capabilities
- func (r *Route53) CreateHealthCheck(ctx context.Context, hc HealthCheck) (HealthCheck, error)
- func (r *Route53) CreateZone(ctx context.Context, name string) (Zone, error)
- func (r *Route53) DeleteHealthCheck(ctx context.Context, id string) error
- func (r *Route53) DeleteRecordSet(ctx context.Context, zone Zone, k Key) error
- func (r *Route53) DeleteZone(ctx context.Context, id string) error
- func (r *Route53) GetZone(ctx context.Context, id string) (Zone, error)
- func (r *Route53) ListHealthChecks(ctx context.Context) ([]HealthCheck, error)
- func (r *Route53) ListRecordSets(ctx context.Context, zone Zone) ([]RecordSet, error)
- func (r *Route53) ListZones(ctx context.Context) ([]Zone, error)
- func (r *Route53) Name() string
- func (r *Route53) UpsertRecordSet(ctx context.Context, zone Zone, rs RecordSet) error
- type Route53API
- type Template
- type TemplateParam
- type ValidationError
- type Zone
Constants ¶
const ( DelegationNone = "not_delegated" DelegationPartial = "partially_delegated" DelegationDelegated = "delegated" DelegationElsewhere = "delegated_elsewhere" )
Delegation states for a zone.
const ( ProviderCloudflare = "cloudflare" ProviderRoute53 = "route53" )
Provider names, matching the DNS-01 provider names used elsewhere.
const ( RoutingSimple = "simple" RoutingWeighted = "weighted" RoutingFailover = "failover" RoutingMultivalue = "multivalue" )
Routing policies a record set can use. Only Route53 supports anything but simple.
const ( FailoverPrimary = "PRIMARY" FailoverSecondary = "SECONDARY" )
Failover roles for RoutingFailover record sets.
const ( ActionCreate = "create" ActionUpdate = "update" ActionDelete = "delete" ActionUnchanged = "unchanged" )
Plan actions.
const ( SourceResolver = "resolver" SourceAuthoritative = "authoritative" )
Answer sources.
const ( TTLAuto = 1 TTLMin = 30 TTLMax = 604800 )
TTL bounds. TTLAuto (1) is Cloudflare's "automatic".
const ( SeverityError = "error" SeverityWarning = "warning" )
Issue severities.
const ( IssueCNAMEExclusive = "cname_exclusive" IssueApexCNAME = "apex_cname" IssueApexFlattened = "apex_cname_flattened" IssueMixedRouting = "mixed_routing" IssueExists = "exists" )
Issue codes, stable for the dashboard and CLI.
const ( FormatJSON = "json" FormatBIND = "bind" )
Export formats.
const Apex = "@"
Apex is the relative name of a zone's own apex.
const CloudflareBaseURL = "https://api.cloudflare.com/client/v4"
CloudflareBaseURL is Cloudflare's v4 API root.
const ServerSystem = "system"
ServerSystem names the host's own configured resolver.
Variables ¶
var ( // ErrZoneNotFound means no zone the credentials can see matches the reference. ErrZoneNotFound = errors.New("dnszones: zone not found") // ErrUnsupported means the provider has no equivalent for the requested feature. ErrUnsupported = errors.New("dnszones: not supported by this provider") )
var CommonNames = []string{ Apex, "www", "mail", "smtp", "imap", "pop", "webmail", "ftp", "api", "app", "blog", "shop", "cdn", "static", "admin", "dev", "staging", "status", "docs", "m", "autodiscover", "autoconfig", "_dmarc", "default._domainkey", "google._domainkey", "selector1._domainkey", "selector2._domainkey", "k1._domainkey", "s1._domainkey", "s2._domainkey", }
CommonNames are probed when importing a zone from its current servers, since AXFR is almost always refused.
var CommonSRVNames = []string{"_sip._tls", "_sipfederationtls._tcp", "_autodiscover._tcp", "_submission._tcp", "_imaps._tcp", "_caldavs._tcp"}
CommonSRVNames are probed for SRV records only.
var DefaultCheckServers = []string{ServerSystem, "1.1.1.1:53", "8.8.8.8:53"}
DefaultCheckServers are asked for delegation and propagation checks.
var RecordTypes = []string{"A", "AAAA", "CNAME", "TXT", "MX", "CAA", "SRV", "NS"}
RecordTypes are the supported types, in the order the dashboard offers them.
var Templates = []Template{ { ID: "email", Name: "Email (any provider)", Description: "MX, SPF and DMARC for a mail host you run or rent.", Params: append([]TemplateParam{ {Key: "mx_host", Label: "Mail server host", Required: true, Placeholder: "mail.example.com"}, {Key: "spf", Label: "SPF mechanisms", Default: "mx", Placeholder: "mx include:_spf.example.net"}, }, dmarcParams...), // contains filtered or unexported fields }, { ID: "dkim", Name: "DKIM key", Description: "Publish a DKIM public key under a selector.", Params: []TemplateParam{ {Key: "selector", Label: "Selector", Required: true, Placeholder: "default"}, {Key: "public_key", Label: "Public key (base64, without headers)", Required: true}, }, // contains filtered or unexported fields }, { ID: "verification", Name: "Verification TXT", Description: "A TXT token a service asks you to publish to prove ownership.", Params: []TemplateParam{ {Key: "name", Label: "Record name", Default: Apex}, {Key: "value", Label: "Token", Required: true}, }, // contains filtered or unexported fields }, { ID: "www-to-apex", Name: "www to apex", Description: "Point www at the zone apex with a CNAME.", // contains filtered or unexported fields }, { ID: "google-workspace", Name: "Google Workspace", Description: "Google's MX and SPF, plus DMARC.", Params: dmarcParams, // contains filtered or unexported fields }, { ID: "microsoft-365", Name: "Microsoft 365", Description: "Exchange Online MX, SPF and autodiscover, plus DMARC.", Params: append([]TemplateParam{ {Key: "mx_prefix", Label: "MX prefix (your domain with dots as dashes)", Required: true, Placeholder: "example-com"}, }, dmarcParams...), // contains filtered or unexported fields }, { ID: "caa-letsencrypt", Name: "CAA for Let's Encrypt", Description: "Allow only Let's Encrypt to issue certificates, wildcards included.", // contains filtered or unexported fields }, }
Templates lists every built in template.
Functions ¶
func ApplyPlan ¶
ApplyPlan executes a plan: deletes first (so a CNAME can replace an A), then updates and creates. It refuses a blocked plan.
func CheckCapabilities ¶
func CheckCapabilities(rs RecordSet, caps Capabilities) error
CheckCapabilities rejects features the provider does not support.
func ExportBIND ¶
ExportBIND renders sets as a zone file. Routed and alias sets have no zone file form and are listed as comments so nothing is silently dropped.
func ExportJSON ¶
ExportJSON renders sets as an indented Document.
func IsManagedApexType ¶
IsManagedApexType reports whether a set is provider managed (apex NS and SOA).
func NormalizeDomain ¶
NormalizeDomain lowercases d and strips surrounding space and a trailing dot.
func RelativeName ¶
RelativeName turns an FQDN or relative name into a name relative to zone, with Apex for the zone itself.
func SameContent ¶
SameContent reports whether a and b would serve identical answers.
func SortSets ¶
func SortSets(sets []RecordSet)
SortSets orders sets by name (apex first), type, then set identifier.
func ValidateHealthCheck ¶
func ValidateHealthCheck(hc HealthCheck) error
ValidateHealthCheck checks the fields CreateHealthCheck needs.
Types ¶
type Alias ¶
type Alias struct {
DNSName string `json:"dns_name"`
HostedZoneID string `json:"hosted_zone_id"`
EvaluateTargetHealth bool `json:"evaluate_target_health"`
}
Alias is a Route53 alias target. Alias sets are shown but edited only through the provider.
type Answer ¶
type Answer struct {
Server string `json:"server"`
Values []string `json:"values,omitempty"`
TTL uint32 `json:"ttl"`
Rcode string `json:"rcode,omitempty"`
Authoritative bool `json:"authoritative,omitempty"`
CNAME string `json:"cname,omitempty"`
Error string `json:"error,omitempty"`
}
Answer is what one server returned for one question.
type Capabilities ¶
type Capabilities struct {
Proxied bool `json:"proxied"`
Routing bool `json:"routing"`
HealthChecks bool `json:"health_checks"`
ApexCNAME bool `json:"apex_cname"`
}
Capabilities says which optional features a provider supports, so callers can validate before sending anything upstream.
type Change ¶
type Change struct {
Action string `json:"action"`
Set RecordSet `json:"set"`
Before *RecordSet `json:"before,omitempty"`
Issues []Issue `json:"issues,omitempty"`
}
Change is one planned record set change.
type Cloudflare ¶
type Cloudflare struct {
BaseURL string
HTTP *http.Client
AccountID string
// contains filtered or unexported fields
}
Cloudflare implements Provider over Cloudflare's REST API with a scoped API token.
func NewCloudflare ¶
func NewCloudflare(token string) *Cloudflare
NewCloudflare returns a client for token. The token is only ever sent as a bearer header.
func (*Cloudflare) Capabilities ¶
func (c *Cloudflare) Capabilities() Capabilities
Capabilities implements Provider.
func (*Cloudflare) CreateZone ¶
CreateZone implements Provider (POST /zones, type full).
func (*Cloudflare) DeleteRecordSet ¶
DeleteRecordSet implements Provider: deletes every record of name and type.
func (*Cloudflare) DeleteZone ¶
func (c *Cloudflare) DeleteZone(ctx context.Context, id string) error
DeleteZone implements Provider (DELETE /zones/{id}).
func (*Cloudflare) ListRecordSets ¶
ListRecordSets implements Provider (GET /zones/{id}/dns_records), grouping Cloudflare's individual records by name and type.
func (*Cloudflare) ListZones ¶
func (c *Cloudflare) ListZones(ctx context.Context) ([]Zone, error)
ListZones implements Provider (GET /zones).
func (*Cloudflare) UpsertRecordSet ¶
UpsertRecordSet implements Provider: keeps records whose value is wanted (patching TTL or proxied when they differ), creates missing ones, then deletes the rest, so a live value is never briefly absent.
type CloudflareError ¶
CloudflareError is a non-success API response.
func (*CloudflareError) Error ¶
func (e *CloudflareError) Error() string
type DNSQuerier ¶
DNSQuerier is the real Querier, over UDP with a TCP retry on truncation.
func NewDNSQuerier ¶
func NewDNSQuerier(timeout time.Duration) *DNSQuerier
NewDNSQuerier returns a querier with a per query timeout.
type Delegation ¶
type Delegation struct {
Domain string `json:"domain"`
State string `json:"state"`
Expected []string `json:"expected"`
Resolvers []ResolverDelegation `json:"resolvers"`
Elsewhere []string `json:"elsewhere,omitempty"`
}
Delegation compares a zone's assigned name servers with what resolvers see.
func CheckDelegation ¶
func CheckDelegation(ctx context.Context, q Querier, domain string, expected, servers []string) Delegation
CheckDelegation asks each server for the domain's NS set, in parallel.
func ComputeDelegation ¶
func ComputeDelegation(expected []string, answers []Answer) Delegation
ComputeDelegation derives the overall state from per resolver answers. A resolver whose NS set is a subset of the assigned one counts as a match: a registrar given two of Route53's four servers still delegates correctly.
type HealthCheck ¶
type HealthCheck struct {
ID string `json:"id"`
Type string `json:"type"`
IPAddress string `json:"ip_address,omitempty"`
FQDN string `json:"fqdn,omitempty"`
Port int32 `json:"port,omitempty"`
ResourcePath string `json:"resource_path,omitempty"`
RequestInterval int32 `json:"request_interval,omitempty"`
FailureThreshold int32 `json:"failure_threshold,omitempty"`
}
HealthCheck is a Route53 health check usable by failover and multivalue sets.
type HealthChecker ¶
type HealthChecker interface {
ListHealthChecks(ctx context.Context) ([]HealthCheck, error)
CreateHealthCheck(ctx context.Context, hc HealthCheck) (HealthCheck, error)
DeleteHealthCheck(ctx context.Context, id string) error
}
HealthChecker is implemented by providers with health checks (Route53).
type Issue ¶
type Issue struct {
Severity string `json:"severity"`
Code string `json:"code"`
Message string `json:"message"`
}
Issue is one validation or conflict finding.
type Key ¶
type Key struct {
Name string `json:"name"`
Type string `json:"type"`
SetIdentifier string `json:"set_identifier,omitempty"`
}
Key identifies a record set within a zone.
type Plan ¶
type Plan struct {
Changes []Change `json:"changes"`
Summary map[string]int `json:"summary"`
Blocked bool `json:"blocked"`
}
Plan is a diff between a zone and a desired set of records, shown before apply.
func BuildPlan ¶
func BuildPlan(existing, desired []RecordSet, replace bool, caps Capabilities) Plan
BuildPlan diffs desired against existing. With replace, sets in existing but not in desired are deleted, except provider managed apex NS and SOA. Without replace nothing is ever deleted, so a foreign record is never lost.
type Propagation ¶
type Propagation struct {
Name string `json:"name"`
Type string `json:"type"`
Expected []string `json:"expected,omitempty"`
Answers []PropagationAnswer `json:"answers"`
Agree bool `json:"agree"`
}
Propagation is a record's answer across resolvers and the zone's own servers.
func CheckPropagation ¶
func CheckPropagation(ctx context.Context, q Querier, name, typ string, resolvers, authoritative, expected []string) (Propagation, error)
CheckPropagation asks resolvers and, when known, the zone's authoritative servers directly. expected, when set, is what the zone says the answer is.
type PropagationAnswer ¶
type PropagationAnswer struct {
Answer
Source string `json:"source"`
Matches *bool `json:"matches,omitempty"`
}
PropagationAnswer is one server's answer plus how it compares.
type Provider ¶
type Provider interface {
Name() string
Capabilities() Capabilities
ListZones(ctx context.Context) ([]Zone, error)
GetZone(ctx context.Context, id string) (Zone, error)
CreateZone(ctx context.Context, name string) (Zone, error)
DeleteZone(ctx context.Context, id string) error
ListRecordSets(ctx context.Context, zone Zone) ([]RecordSet, error)
UpsertRecordSet(ctx context.Context, zone Zone, rs RecordSet) error
DeleteRecordSet(ctx context.Context, zone Zone, key Key) error
}
Provider is the zone-level surface both Cloudflare and Route53 implement.
type RecordSet ¶
type RecordSet struct {
Name string `json:"name"`
Type string `json:"type"`
TTL int `json:"ttl"`
Values []string `json:"values"`
Proxied bool `json:"proxied,omitempty"`
Routing string `json:"routing,omitempty"`
SetIdentifier string `json:"set_identifier,omitempty"`
Weight *int64 `json:"weight,omitempty"`
Failover string `json:"failover,omitempty"`
HealthCheckID string `json:"health_check_id,omitempty"`
Alias *Alias `json:"alias,omitempty"`
}
RecordSet is every value of one name, type and set identifier. Values use zone file presentation syntax, except TXT which holds the raw text.
func Discover ¶
Discover resolves common names (plus extra) against servers and returns the record sets it found, for an operator to review before importing.
func MergeTemplate ¶
MergeTemplate layers template sets over existing ones. TXT sets keep the other values already published at that name (verification tokens), except a second SPF record, which would make SPF fail.
func Normalize ¶
Normalize validates rs and returns its canonical form: relative lower case name, upper case type, deduplicated canonical values and a defaulted TTL.
func ParseBIND ¶
ParseBIND parses zone file text into record sets relative to zone. SOA, apex NS and unsupported types are skipped with a warning.
type ResolverDelegation ¶
type ResolverDelegation struct {
Server string `json:"server"`
NameServers []string `json:"name_servers,omitempty"`
Verdict string `json:"verdict"`
Error string `json:"error,omitempty"`
}
ResolverDelegation is what one resolver says the domain's name servers are.
type Route53 ¶
type Route53 struct {
API Route53API
// HostedZoneID, when set, is used if the credentials cannot list zones.
HostedZoneID string
}
Route53 implements Provider and HealthChecker over the AWS SDK.
func NewRoute53 ¶
NewRoute53 builds a client from a static access key pair. Route53 is global, so region only picks the signing endpoint.
func (*Route53) Capabilities ¶
func (r *Route53) Capabilities() Capabilities
Capabilities implements Provider.
func (*Route53) CreateHealthCheck ¶
func (r *Route53) CreateHealthCheck(ctx context.Context, hc HealthCheck) (HealthCheck, error)
CreateHealthCheck implements HealthChecker (HTTP, HTTPS or TCP).
func (*Route53) CreateZone ¶
CreateZone implements Provider (CreateHostedZone, public).
func (*Route53) DeleteHealthCheck ¶
DeleteHealthCheck implements HealthChecker.
func (*Route53) DeleteRecordSet ¶
DeleteRecordSet implements Provider. Route53's DELETE needs the set's exact current content, so it is read back first; a missing set is not an error.
func (*Route53) DeleteZone ¶
DeleteZone implements Provider (DeleteHostedZone). Route53 itself refuses while non NS/SOA sets remain, so callers delete those first.
func (*Route53) ListHealthChecks ¶
func (r *Route53) ListHealthChecks(ctx context.Context) ([]HealthCheck, error)
ListHealthChecks implements HealthChecker.
func (*Route53) ListRecordSets ¶
ListRecordSets implements Provider (ListResourceRecordSets, paginated).
type Route53API ¶
type Route53API interface {
ListHostedZones(ctx context.Context, in *route53.ListHostedZonesInput, opts ...func(*route53.Options)) (*route53.ListHostedZonesOutput, error)
GetHostedZone(ctx context.Context, in *route53.GetHostedZoneInput, opts ...func(*route53.Options)) (*route53.GetHostedZoneOutput, error)
CreateHostedZone(ctx context.Context, in *route53.CreateHostedZoneInput, opts ...func(*route53.Options)) (*route53.CreateHostedZoneOutput, error)
DeleteHostedZone(ctx context.Context, in *route53.DeleteHostedZoneInput, opts ...func(*route53.Options)) (*route53.DeleteHostedZoneOutput, error)
ListResourceRecordSets(ctx context.Context, in *route53.ListResourceRecordSetsInput, opts ...func(*route53.Options)) (*route53.ListResourceRecordSetsOutput, error)
ChangeResourceRecordSets(ctx context.Context, in *route53.ChangeResourceRecordSetsInput, opts ...func(*route53.Options)) (*route53.ChangeResourceRecordSetsOutput, error)
ListHealthChecks(ctx context.Context, in *route53.ListHealthChecksInput, opts ...func(*route53.Options)) (*route53.ListHealthChecksOutput, error)
CreateHealthCheck(ctx context.Context, in *route53.CreateHealthCheckInput, opts ...func(*route53.Options)) (*route53.CreateHealthCheckOutput, error)
DeleteHealthCheck(ctx context.Context, in *route53.DeleteHealthCheckInput, opts ...func(*route53.Options)) (*route53.DeleteHealthCheckOutput, error)
}
Route53API is the subset of the AWS SDK client this package calls, so tests use a fake.
type Template ¶
type Template struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Params []TemplateParam `json:"params"`
// contains filtered or unexported fields
}
Template is a named set of records for a common setup. Data only.
func FindTemplate ¶
FindTemplate returns the template with id.
type TemplateParam ¶
type TemplateParam struct {
Key string `json:"key"`
Label string `json:"label"`
Required bool `json:"required"`
Default string `json:"default,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
}
TemplateParam is one input a template needs.
type ValidationError ¶
type ValidationError struct{ Problems []string }
ValidationError carries every problem found in one record set.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
type Zone ¶
type Zone struct {
ID string `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Status string `json:"status,omitempty"`
RecordCount *int `json:"record_count,omitempty"`
NameServers []string `json:"name_servers"`
Private bool `json:"private,omitempty"`
ModifiedAt string `json:"modified_at,omitempty"`
}
Zone is one hosted zone. RecordCount is nil when the provider cannot report it cheaply.