forwardddns

package
v4.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package forwardddns is the DNS provider layer of forwarding entry high availability (docs/architecture/forward-sdk.md section 7.4, L2): Control keeps a route's entry name on the healthy entry nodes' addresses by writing A and AAAA records through a DNS provider's API. The providers are those of H21: Cloudflare (API token), Alibaba Cloud DNS (AccessKey, signature V3 ACS3-HMAC-SHA256), DNSPod (Tencent Cloud API 3.0, TC3-HMAC-SHA256), Huawei Cloud DNS (AK/SK, SDK-HMAC-SHA256) and a generic webhook (an HMAC-SHA256-signed JSON POST).

Every client is the standard library's HTTP client with hand-written request signing; no vendor SDK is linked. A Provider sets a whole record set (one name and type): it adds the new values before it deletes the old ones, so the name never resolves to nothing while it changes. Credentials never appear in an error, a log line or a metric label.

Index

Constants

View Source
const (
	KindCloudflare  = "cloudflare"
	KindAliDNS      = "alidns"
	KindDNSPod      = "dnspod"
	KindHuaweiCloud = "huaweicloud"
	KindWebhook     = "webhook"
)

Provider kinds (H21).

View Source
const (
	TypeA     = "A"
	TypeAAAA  = "AAAA"
	TypeCNAME = "CNAME"
)

Record types.

View Source
const (
	ConfigEndpoint = "endpoint"
	ConfigURL      = "url"
)

ConfigEndpoint overrides a provider API's host; ConfigURL is a webhook's URL.

View Source
const (
	CredentialAPIToken        = "api_token"
	CredentialAccessKeyID     = "access_key_id"
	CredentialAccessKeySecret = "access_key_secret" // #nosec G101 -- a credential's field name, not a credential.
	CredentialSecretID        = "secret_id"
	CredentialSecretKey       = "secret_key"
	CredentialAccessKey       = "access_key"
	CredentialSecret          = "secret"
)

Credential names.

View Source
const (
	CodeRequired      = "required"
	CodeInvalidFormat = "invalid_format"
	CodeUnknownField  = "unknown_field"
)

Field error codes.

View Source
const (
	WebhookEvent           = "forward.dns"
	WebhookHeaderEvent     = "X-AnixOps-Event"
	WebhookHeaderTimestamp = "X-AnixOps-Timestamp"
	WebhookHeaderSignature = "X-AnixOps-Signature"
)

The generic webhook: Control POSTs each change as JSON to the operator's HTTPS URL, signed with the shared secret.

POST <url>
Content-Type: application/json
X-AnixOps-Event: forward.dns
X-AnixOps-Timestamp: <Unix seconds>
X-AnixOps-Signature: sha256=<hex HMAC-SHA256(secret, timestamp + "." + body)>

{"version":1,"action":"set","zone":"example.com","name":"hk.example.com",
 "type":"A","values":["192.0.2.10"],"ttl":60,"timestamp":1759579200}

action "delete" carries no values. Any 2xx answer is success. The receiver should refuse a timestamp more than 5 minutes old.

View Source
const DefaultTimeout = 15 * time.Second

DefaultTimeout bounds one provider HTTP call.

Variables

View Source
var ErrNotFound = errors.New("dns zone not found in the provider account")

ErrNotFound: the zone is not in the account.

Functions

func CheckSet

func CheckSet(set RecordSet, allowEmpty bool) error

CheckSet validates a record set before it is sent.

func InZone

func InZone(name, zone string) bool

InZone reports whether name is zone or below it.

func Kinds

func Kinds() []string

Kinds lists the provider kinds.

func NormalizeName

func NormalizeName(name string) string

NormalizeName lowercases a DNS name and drops its trailing dot.

func ValidName

func ValidName(name string) bool

ValidName reports whether name is a DNS name (no trailing dot).

func WebhookSignature

func WebhookSignature(secret string, timestamp int64, body []byte) string

WebhookSignature is the X-AnixOps-Signature value of body sent at timestamp.

Types

type APIError

type APIError struct {
	Kind    string
	Status  int
	Code    string
	Message string
}

APIError is a provider's refusal. It carries the provider's code and message, never a credential.

func (*APIError) Error

func (e *APIError) Error() string

type FieldError

type FieldError struct {
	Field   string
	Code    string
	Message string
}

FieldError is one reason a provider's settings are refused.

func Validate

func Validate(kind string, config, credentials map[string]string) []FieldError

Validate checks a provider's kind, configuration and credentials.

type Options

type Options struct {
	// Client is the HTTP client; one with DefaultTimeout by default.
	Client *http.Client
	// Now defaults to time.Now; Nonce to 16 random bytes in hex.
	Now   func() time.Time
	Nonce func() string
}

Options are a provider's runtime dependencies.

type Provider

type Provider interface {
	// Kind is the provider's kind.
	Kind() string
	// SetRecords makes the records of set.Type at set.Name exactly
	// set.Values with set.TTL. New values are added before removed ones
	// are deleted. Values must not be empty: use DeleteRecords.
	SetRecords(ctx context.Context, set RecordSet) error
	// DeleteRecords removes every record of rtype at name.
	DeleteRecords(ctx context.Context, zone, name, rtype string) error
}

Provider writes records through one DNS provider account.

func New

func New(kind string, config, credentials map[string]string, opts Options) (Provider, error)

New returns the provider of kind with its configuration and credentials, which Validate accepts.

type RecordSet

type RecordSet struct {
	Zone   string
	Name   string
	Type   string
	Values []string
	TTL    uint32
}

RecordSet is the records of one name and type. Name is fully qualified, inside Zone, without the trailing dot.

type Spec

type Spec struct {
	Kind string
	// Config are the settings it accepts; Required those it needs.
	Config   []string
	Required []string
	// Credentials are the secrets it needs.
	Credentials []string
}

Spec is what a kind of provider takes.

func SpecFor

func SpecFor(kind string) (Spec, bool)

SpecFor returns the spec of kind.

type WebhookPayload

type WebhookPayload struct {
	Version   int      `json:"version"`
	Action    string   `json:"action"`
	Zone      string   `json:"zone"`
	Name      string   `json:"name"`
	Type      string   `json:"type"`
	Values    []string `json:"values,omitempty"`
	TTL       uint32   `json:"ttl,omitempty"`
	Timestamp int64    `json:"timestamp"`
}

WebhookPayload is the body of a webhook call.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL