Documentation
¶
Overview ¶
Package ddns keeps a dynamic DNS record pointed at this host's public IPv4.
Index ¶
- Constants
- Variables
- func Enabled(getenv func(string) string) bool
- func OSEnv(k string) string
- func StartFromEnv(ctx context.Context, getenv func(string) string, log *slog.Logger, hooks Hooks) (bool, error)
- type Carry
- type Config
- type Detector
- type FetchFunc
- type HTTPProvider
- type Hooks
- type Manager
- type Provider
- type Resource
- type Secrets
- type Settings
- type Status
- type StatusView
- type Update
- type Updater
Constants ¶
const ( EnvEnabled = "APP_DDNS_ENABLED" EnvInterval = "APP_DDNS_INTERVAL" EnvProbeURLs = "APP_DDNS_PROBE_URLS" EnvProbeQuorum = "APP_DDNS_PROBE_QUORUM" EnvTimeout = "APP_DDNS_TIMEOUT" EnvMinUpdateGap = "APP_DDNS_MIN_UPDATE_GAP" EnvReassert = "APP_DDNS_REASSERT_INTERVAL" EnvFailAlertAfter = "APP_DDNS_FAIL_ALERT_AFTER" EnvAllowPrivate = "APP_DDNS_ALLOW_PRIVATE" EnvAllowInsecure = "APP_DDNS_ALLOW_INSECURE_HTTP" EnvUpdateURL = "APP_DDNS_UPDATE_URL" EnvDuckDomains = "APP_DDNS_DUCKDNS_DOMAINS" EnvDuckToken = "APP_DDNS_DUCKDNS_TOKEN" //nolint:gosec // env var name, not a credential )
Env names. The updater is off unless APP_DDNS_ENABLED is on and a target is set.
const ( // ProviderGeneric is an operator-supplied update URL with an {ip} placeholder. ProviderGeneric = "generic" // ProviderDuckDNS is the DuckDNS preset (domains plus token). ProviderDuckDNS = "duckdns" // SourceStored says the setting came from the stored row. SourceStored = "stored" // SourceDefault says nothing is stored, so env defaults apply. SourceDefault = "default" )
Variables ¶
var ( ErrNoQuorum = errors.New("ddns: probes did not agree") ErrNonPublic = errors.New("ddns: detected address is not public") )
Detection outcomes the loop reports as warnings, never as writes.
var ErrNoSecrets = errors.New("storing the dynamic DNS target needs a configured master key")
ErrNoSecrets means no master key is configured, so nothing can be stored.
var ErrNoTarget = errors.New("ddns: enabled but no update target configured")
ErrNoTarget means the updater is enabled but no provider is configured.
Functions ¶
Types ¶
type Carry ¶
Carry is the write history a replacement loop inherits so the minimum gap and reassert timer keep applying.
type Config ¶
type Config struct {
Interval time.Duration
ProbeURLs []string
Quorum int
Timeout time.Duration
MinUpdateGap time.Duration
Reassert time.Duration
FailAlertAfter int
AllowPrivate bool
AllowInsecure bool
}
Config holds the loop settings; zero durations are replaced by defaults.
func ConfigFromEnv ¶
ConfigFromEnv reads the loop settings; getenv is injectable for tests.
type Detector ¶
Detector asks several probe URLs and accepts an address only on agreement.
func NewDetector ¶
NewDetector uses the guarded client, so a probe URL cannot point inside the network.
type HTTPProvider ¶
type HTTPProvider struct {
// contains filtered or unexported fields
}
HTTPProvider calls an operator-supplied update URL with {ip} substituted.
func NewDuckDNS ¶
func NewDuckDNS(domains, token string, cfg Config) (*HTTPProvider, error)
NewDuckDNS is the generic provider preset for the DuckDNS update endpoint.
func NewHTTPProvider ¶
NewHTTPProvider validates the template and registers it with the log masker. accept may be nil (any 2xx).
type Hooks ¶
type Hooks struct {
OnAlert func(msg string)
OnRecover func()
}
Hooks connect the loop to the alert path owned elsewhere; both may be nil.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns the stored setting and keeps one updater loop matching it.
func NewManager ¶
func NewManager(ctx context.Context, s Secrets, getenv func(string) string, log *slog.Logger, hooks Hooks) *Manager
NewManager wires the manager; secrets may be nil, then only env defaults apply and nothing can be stored.
func (*Manager) Apply ¶
Apply validates and stores a change, then reconciles the loop so it takes effect without a restart.
func (*Manager) Reconcile ¶
Reconcile starts, restarts or stops the loop so it matches the effective setting.
func (*Manager) Status ¶
func (m *Manager) Status() StatusView
Status returns the loop snapshot; Running is false when the loop is stopped.
type Resource ¶
type Resource struct {
Settings
Source string `json:"source"`
TargetConfigured bool `json:"target_configured"`
Host string `json:"host,omitempty"`
Running bool `json:"running"`
}
Resource is what the API returns: settings, where they came from, and the host only, never the target.
type Secrets ¶
type Secrets interface {
SetValue(ctx context.Context, serviceName, envKey, plaintext string) error
Resolve(ctx context.Context, serviceName, envKey string) (string, error)
Exists(ctx context.Context, serviceName, envKey string) (bool, error)
DeleteValue(ctx context.Context, serviceName, envKey string) error
}
Secrets is the encrypted store the setting lives in (the secrets manager satisfies it).
type Settings ¶
type Settings struct {
Enabled bool `json:"enabled"`
Provider string `json:"provider"`
Domains string `json:"domains,omitempty"`
IntervalSecs int `json:"interval_seconds"`
ProbeURLs []string `json:"probe_urls"`
ProbeQuorum int `json:"probe_quorum"`
TimeoutSecs int `json:"timeout_seconds"`
MinUpdateGapSecs int `json:"min_update_gap_seconds"`
ReassertSecs int `json:"reassert_seconds"`
FailAlertAfter int `json:"fail_alert_after"`
AllowPrivate bool `json:"allow_private"`
AllowInsecureHTTP bool `json:"allow_insecure_http"`
}
Settings is the stored, non-secret part. Durations are whole seconds.
func DefaultSettings ¶
DefaultSettings seeds the setting from env; it applies only until something is stored.
type Status ¶
type Status struct {
LastCheck time.Time
LastChange time.Time
LastIP string
LastError string
Failures int
AlertActive bool
}
Status is a snapshot for later API and CLI slices. It holds no secrets.
type StatusView ¶
type StatusView struct {
Running bool `json:"running"`
LastCheck time.Time `json:"last_check,omitzero"`
LastChange time.Time `json:"last_change,omitzero"`
LastIP string `json:"last_ip,omitempty"`
LastResult string `json:"last_result"`
LastError string `json:"last_error,omitempty"`
Failures int `json:"consecutive_failures"`
AlertActive bool `json:"alert_active"`
}
StatusView is the loop state for the status read.
type Update ¶
type Update struct {
Enabled *bool
Provider *string
Domains *string
Target *string
IntervalSecs *int
ProbeURLs *[]string
ProbeQuorum *int
TimeoutSecs *int
MinUpdateGapSecs *int
ReassertSecs *int
FailAlertAfter *int
AllowPrivate *bool
AllowInsecureHTTP *bool
}
Update is a partial change; nil fields keep the current value, a non-nil empty Target clears it.
type Updater ¶
type Updater struct {
// contains filtered or unexported fields
}
Updater detects the address and writes it through the provider.