Documentation
¶
Overview ¶
Package config reads the edge's config. It comes in four parts, and each has one owner:
fortress.yml baked into the ISO by fortressctl bake: what the edge
trusts and how it runs. A new ISO changes it.
user-data the NoCloud drive: fqdn, the edge's DNS name. Nothing
else in it is read.
network-config the NoCloud drive: the static address.
policy.yml the /var disk, from fortressctl apply: who may reach the
edge and how much. It changes while the edge runs.
None of them holds a secret.
Index ¶
Constants ¶
const ( FrpsBindAddr = "127.0.0.1" FrpsTCPPort = 7000 // dark-node control (wss) FrpsQUICPort = 443 // dark-node QUIC on the public UDP port; TCP control stays on loopback FrpWebsocketPath = "/~!frp" // Operator API, served on the tunnel SNI (mTLS already enforced there). OpsPathPrefix = "/~!ops/" OpsLogsPath = OpsPathPrefix + "logs" OpsAccessPath = OpsPathPrefix + "access" OpsStatusPath = OpsPathPrefix + "status" // OpsMetricsPath is the Prometheus text format of what status counts. OpsMetricsPath = OpsPathPrefix + "metrics" // OpsPolicyPath reads the policy (GET) and replaces it (PUT). OpsPolicyPath = OpsPathPrefix + "policy" )
frps listens on loopback for dark-node control, which the edge reaches in-process. QUIC, when enabled, binds FrpsQUICPort on the public address instead.
const ( DefaultNTP = "pool.ntp.org" // DefaultRenewInterval is how often ACME certificates are checked for // renewal when fortress.yml sets no renew_interval. DefaultRenewInterval = 4 * time.Hour // DefaultTunnelGrace covers a reboot of the edge, and a dark node // restarting or upgraded; maxTunnelGrace bounds it at a month. DefaultTunnelGrace = 10 * time.Minute // The access log's defaults keep it at 24 MiB, well inside a 64 MiB // data disk. DefaultAccessLogMaxSize = 8 << 20 DefaultAccessLogMaxFiles = 3 // EdgeFile is the baked config, at BakedFile in the initramfs. EdgeFile = "fortress.yml" BakedFile = "/etc/fortressedge/" + EdgeFile // PolicyName is the runtime config, stored at PolicyFile. PolicyName = "policy.yml" // The persistent disk mounts at /var; state and logs live there. DataDir = "/var/fortressedge" PolicyFile = DataDir + "/" + PolicyName CertsDir = DataDir + "/certs" // RunClientCA holds the fortress.yml client_ca for this boot, for frps, // which reads its trust anchor from a file. The edge holds no CA key. RunClientCA = "/run/fortressedge/client_ca.crt" LogDir = "/var/log/fortressedge" LogFile = LogDir + "/current.log" // AccessLogDir holds the access log (policy.yml's access_log), one JSON // line per site request, apart from the edge's own log. AccessLogDir = LogDir + "/access" )
Variables ¶
This section is empty.
Functions ¶
func CheckEdge ¶
CheckEdge validates fortress.yml on its own, as fortressctl bake does before it puts the file into an ISO.
func ControlAddr ¶
func ControlAddr() string
func EdgeKeys ¶ added in v0.2.0
func EdgeKeys() []string
EdgeKeys lists the keys fortress.yml takes.
func PolicyETag ¶
PolicyETag names a policy.yml body: its SHA-256, quoted as an HTTP entity tag. The ops API sends it with the policy; fortressctl diff compares it with the file in git.
Types ¶
type AccessLog ¶ added in v0.3.0
AccessLog writes one line per site request to AccessLogDir, in files of up to MaxSize bytes, keeping MaxFiles.
type Config ¶
type Config struct {
Iface string
Addr netip.Prefix // static address of Iface, with prefix length
Gateway netip.Addr
DNS []netip.Addr
Tunnel string // the fqdn: SNI dark nodes and operators connect to
// Disk is the device boot mounted at /var; not part of the config.
Disk string
// NTP are the time servers, host or host:port, which the clock keeps
// to while the edge runs (internal/clock).
NTP []string
ACME string // ACME directory URL; empty is Let's Encrypt
// ACMECA is the PEM trust anchor for the directory's TLS certificate.
// Empty uses the system roots. Set, it is the only pool the ACME client trusts.
ACMECA []byte
// RenewInterval is how often every ACME certificate is checked, and
// renewed once it is due (the last third of its life, or when the CA
// says so through ARI).
RenewInterval time.Duration
QUIC bool // dark-node QUIC on UDP 443
// ClientCA is the PEM trust anchor from fortress.yml (client_ca) for
// dark-node, operator, and log-reader certificates.
ClientCA []byte
// ClientCAPath is the file listeners load this boot. Set after boot
// resolves client_ca; not part of fortress.yml.
ClientCAPath string
Policy
}
type InvalidError ¶
type InvalidError struct{ Err error }
InvalidError is a config that parsed but is not acceptable. Callers map it to HTTP 400; other errors are failures to store or apply.
func (*InvalidError) Error ¶
func (e *InvalidError) Error() string
func (*InvalidError) Unwrap ¶
func (e *InvalidError) Unwrap() error
type Limits ¶
type Limits struct {
ConnsPerSource int // open connections
RequestsPerSecond int // sustained
RequestBurst int // at once
NewConnsPerSecond int // TCP SYNs, dropped in XDP
NewConnBurst int // SYNs at once
Ban time.Duration // XDP ban for a source that keeps being refused; 0 never bans
BanAfter int // refusals within BanWindow that earn a ban
BanWindow time.Duration
MaxConns int // all sources, per port; 0 sizes it from memory
MaxHeaderBytes int // request line and headers
MaxURIBytes int // request target
MaxBodyBytes int64 // request body; 0: no limit
MaxHTTP2Streams int // concurrent streams per HTTP/2 connection
// ResponseHeaderTimeout is how long a request waits for the origin's
// response headers; a stream that sends them with its first event
// (server-sent events, long polling) is cut after it. Whole seconds.
ResponseHeaderTimeout time.Duration
}
Limits protect the edge from one source (an IPv4 address or an IPv6 /64). A rate or count of 0 turns that limit off.
func DefaultLimits ¶
func DefaultLimits() Limits
DefaultLimits are generous: many people can share one IPv4 address.
func (Limits) BootLimits ¶
BootLimits are the limits the HTTP servers and listeners fix when they start; a change to any of them reboots. The rest apply at once.
type Policy ¶
type Policy struct {
Block []netip.Prefix // XDP drop list; host or CIDR
// Exempt are sources the per-address limits and bans skip: a load
// generator, a monitoring probe, an office behind one address.
Exempt []netip.Prefix
Limits Limits
// AccessLog is the access log for every site, unless Sites turns it
// off or on for one.
AccessLog AccessLog
Trace Trace
// TunnelGrace is how long a site whose tunnel left, or that a dark
// node published before a boot, is answered 503 before it goes silent
// like any name the edge does not serve. 0 never answers it.
TunnelGrace time.Duration
// Sites are settings for one site, by the name its dark node publishes
// (app.example.com, *.example.com) or a name a wildcard covers.
Sites map[string]SitePolicy
}
Policy is policy.yml: what the edge lets through. It is the part an operator changes while the edge runs.
func ParsePolicy ¶
ParsePolicy reads policy.yml. Empty is the defaults.
func (Policy) AccessLogAnywhere ¶ added in v0.3.0
AccessLogAnywhere reports whether any site may write the access log.
func (Policy) RebootFrom ¶
RebootFrom reports whether moving from cur to p needs a reboot: only the limits the servers fix when they start do.
type Site ¶ added in v0.3.0
type Site struct {
AccessLog bool
MaxBodyBytes int64 // 0: no limit
ResponseHeaderTimeout time.Duration
}
Site is the settings in force for one request.
type SitePolicy ¶ added in v0.3.0
type SitePolicy struct {
AccessLog *bool
MaxBodyBytes *int64 // 0: no limit
ResponseHeaderTimeout time.Duration
}
SitePolicy overrides edge-wide settings for one site. A nil or zero field keeps the edge-wide one.
type Trace ¶ added in v0.3.0
type Trace struct {
// TrustIncoming continues a visitor's valid traceparent. Off, every
// request starts a new trace, and the visitor's is only logged, as a
// link: an outsider cannot choose the trace a request lands in.
TrustIncoming bool
}
Trace is how the edge joins a request to a W3C trace (traceparent).