config

package
v0.7.2 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: MIT Imports: 17 Imported by: 0

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

View Source
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.

View Source
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

	// DefaultTunnelDeadTimeout matches fortresskube's dead_server_timeout:
	// neither end of a tunnel gives up on the other first.
	DefaultTunnelDeadTimeout = 3 * time.Second

	// 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

func CheckEdge(edge []byte) error

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

func PolicyETag(policy []byte) string

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

type AccessLog struct {
	On       bool
	MaxSize  int64
	MaxFiles int
}

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
	// Vault, when set, is where the edges that serve the same names share
	// their certificates, ACME account, and challenges.
	Vault *Vault
	// 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
}

func Parse

func Parse(userData, networkConfig, edge, policy []byte) (Config, error)

Parse reads the four parts. policy may be nil: an edge nobody has applied a policy to yet runs the defaults (no block list, DefaultLimits).

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
	// TCPIdleTimeout closes a TCP route's connection after this long
	// with no byte either way; 0 never does. Database tools hold
	// sessions open for hours.
	TCPIdleTimeout time.Duration
	// TCPConnsPerSource is how many connections one source may hold open
	// to one TCP route; connections_per_source counts them too.
	TCPConnsPerSource int
}

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

func (l Limits) BootLimits() Limits

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
	// TunnelDeadTimeout is how long a tunnel connection may go without the
	// dark node acknowledging anything before the edge drops it, so a lost
	// frpc leaves its group and the others carry its share. 0 leaves it to
	// frps's heartbeat. A tunnel keeps the timeout it connected with.
	TunnelDeadTimeout 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

func ParsePolicy(b []byte) (Policy, error)

ParsePolicy reads policy.yml. Empty is the defaults.

func (Policy) AccessLogAnywhere added in v0.3.0

func (p Policy) AccessLogAnywhere() bool

AccessLogAnywhere reports whether any site may write the access log.

func (Policy) RebootFrom

func (p Policy) RebootFrom(cur Policy) bool

RebootFrom reports whether moving from cur to p needs a reboot: only the limits the servers fix when they start do.

func (Policy) Site added in v0.3.0

func (p Policy) Site(host, route string) Site

Site is what applies to a request for host, which frps routed by route, the published name that matched it (host itself, or a wildcard). An entry for host wins over one for route.

type Site added in v0.3.0

type Site struct {
	AccessLog             bool
	MaxBodyBytes          int64 // 0: no limit
	ResponseHeaderTimeout time.Duration
	TCPIdleTimeout        time.Duration // 0: never
	TCPConnsPerSource     int           // 0: no limit
}

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
	// For a TCP route: see Limits.
	TCPIdleTimeout    *time.Duration
	TCPConnsPerSource *int
}

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).

type Vault added in v0.6.0

type Vault struct {
	URL      string // https://vault.example.com:8200
	CA       []byte // PEM; empty trusts the system roots
	Mount    string // the KV v2 mount
	Path     string // the path in it, for these edges only
	RoleID   string
	SecretID string
}

Vault is a KV v2 path several edges share their certificates in, and the AppRole that may use it.

Jump to

Keyboard shortcuts

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