Documentation
¶
Overview ¶
Package cli implements the gatewaydnsd command line.
It lives in internal/ rather than in the command itself so that it can be tested without spawning a process: every subcommand takes its output writers as arguments and returns an error rather than calling os.Exit. A CLI that can only be tested by running it is a CLI whose error paths are not tested.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrUsage = errors.New("invalid usage")
ErrUsage reports that the command line itself was wrong, as opposed to the command having failed. Callers distinguish the two because a usage error deserves the help text and a failure does not.
Functions ¶
Types ¶
type APIConfig ¶
type APIConfig struct {
// Addr is where the management API listens. Empty disables it entirely.
//
// The example configuration binds loopback. Putting this on an address the
// rest of the network can reach means every device on it can try the token,
// which is a different security posture and should be a decision rather
// than a default.
Addr string `json:"addr"`
// Token authenticates every request. There is no way to disable
// authentication; see the api package.
Token string `json:"token"`
// TokenFile reads the token from a file instead, which is how a token
// avoids being in a configuration file that gets copied into a support
// ticket or committed to a repository.
TokenFile string `json:"token_file"`
// AllowedOrigins enables CORS for exactly these origins. Empty refuses
// every cross-origin request.
AllowedOrigins []string `json:"allowed_origins"`
// ExposeLog enables the query-log endpoints, which read a browsing history.
// It is separate from the rest of the API for that reason.
ExposeLog bool `json:"expose_log"`
// ReadOnly refuses every mutating request.
ReadOnly bool `json:"read_only"`
// MaxPageSize bounds how many query-log rows one request may read. Zero
// takes the API's own default. It is the only thing standing between an
// authenticated-but-restricted client and the whole retained history, one
// page at a time.
MaxPageSize int `json:"max_page_size"`
// LogRetention bounds the query log: entries older than MaxAge and beyond
// MaxEntries are discarded. Zero takes the storage default of a week or a
// million entries.
//
// It is configuration rather than a constant because the log is a record of
// what everyone on the network looked up, and how long that is kept is the
// operator's decision, not this daemon's.
LogRetention RetentionConfig `json:"log_retention"`
}
APIConfig configures the management API.
It is off unless an address is given, and that default is deliberate: the API reads the query log and changes the filtering policy, so the smallest attack surface is the one that does not exist. An operator who wants it has to say where it listens and supply a token.
type CacheConfig ¶
type CacheConfig struct {
Enabled *bool `json:"enabled,omitempty"`
MaxEntries int `json:"max_entries"`
MinTTL Duration `json:"min_ttl"`
MaxTTL Duration `json:"max_ttl"`
// ServeStale keeps answering from expired entries for this long when the
// upstream cannot be reached (RFC 8767). Zero disables it. It is a
// deliberate correctness trade and so is off unless asked for.
ServeStale Duration `json:"serve_stale"`
}
CacheConfig sizes the cache.
type Config ¶
type Config struct {
Version int `json:"version"`
Listen ListenConfig `json:"listen"`
Upstream UpstreamConfig `json:"upstream"`
Cache CacheConfig `json:"cache"`
Policy PolicyConfig `json:"policy"`
Logging LoggingConfig `json:"logging"`
API APIConfig `json:"api"`
}
Config is the daemon's configuration file.
It is the daemon's own schema rather than the engine's. The engine's config package deliberately covers only the subsystems it owns — logging, events, metrics — because a configuration key is a compatibility promise from the moment an operator writes it into a file. Listen addresses, upstream lists and blocklist URLs are the daemon's business, so they are declared here, where the thing that reads them lives.
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns the configuration a daemon starts with when told nothing else. It is a working forwarding-and-caching resolver.
func LoadConfig ¶
LoadConfig reads a configuration, starting from the defaults so that a file need only state what it changes.
Unknown fields are an error. A typo'd key that is silently ignored is how an operator ends up believing a setting is on when it is off, and for a filtering resolver that setting may be the one doing the filtering.
type Duration ¶
Duration is a time.Duration written the way a person writes one.
A configuration file is edited by hand, and time.Duration marshals as an integer count of nanoseconds — so "five seconds" is 5000000000 in the document this daemon PRINTS as its own documented default. Nobody reads that correctly and nobody types it correctly. This accepts "5s", "24h", "500ms" and emits the same, while still reading a bare number as nanoseconds so that a file written against the older schema keeps working.
func (Duration) MarshalJSON ¶
MarshalJSON implements json.Marshaler.
func (*Duration) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.
type ListenConfig ¶
type ListenConfig struct {
// Addr is the address to bind, "host:port". Port 53 is privileged on every
// Unix, so a daemon binding it either starts as root and drops privileges
// or is granted CAP_NET_BIND_SERVICE.
Addr string `json:"addr"`
// UDP and TCP each default to true. Both are wanted in almost every
// deployment: a client that receives a truncated UDP answer retries over
// TCP, and disabling TCP makes every large answer unresolvable.
UDP *bool `json:"udp,omitempty"`
TCP *bool `json:"tcp,omitempty"`
MaxInFlight int `json:"max_in_flight"`
MaxTCPConns int `json:"max_tcp_conns"`
QueryTimeout Duration `json:"query_timeout"`
}
ListenConfig says where to accept queries.
type LoggingConfig ¶
LoggingConfig configures output.
type PolicyConfig ¶
type PolicyConfig struct {
// Blocklists and Allowlists are paths to list files. Remote URLs are not
// fetched by the daemon: downloading, verifying and refreshing them is a
// separate concern with its own failure modes, and a resolver that cannot
// start because a list server is down is worse than one with a stale list.
Blocklists []string `json:"blocklists"`
Allowlists []string `json:"allowlists"`
// Block is "nxdomain", "refused" or "nodata".
Block string `json:"block"`
}
PolicyConfig configures filtering.
type RetentionConfig ¶
type RetentionConfig struct {
MaxAge Duration `json:"max_age"`
MaxEntries int `json:"max_entries"`
}
RetentionConfig bounds the query log.
type UpstreamConfig ¶
type UpstreamConfig struct {
// Servers are endpoints, each "scheme://address": udp, tcp, tls or https.
// A preset name may be used instead of a URL.
Servers []string `json:"servers"`
// Strategy is "sequential", "race", "round-robin" or "random".
Strategy string `json:"strategy"`
// Randomize enables DNS-0x20 case randomisation against upstreams that
// tolerate it. It is off by default because a small number of servers
// normalise case in replies, and against one of those it breaks every query
// rather than degrading.
Randomize bool `json:"randomize"`
Timeout Duration `json:"timeout"`
}
UpstreamConfig says where queries go.