config

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 12 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"
	// 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
	// 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 (access_log: true), 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 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 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  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
	// AccessLog writes one line per site request to AccessLogDir, in
	// files of up to AccessLogMaxSize bytes, keeping AccessLogMaxFiles.
	AccessLog         bool
	AccessLogMaxSize  int64
	AccessLogMaxFiles int
	// 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
}

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
}

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

Jump to

Keyboard shortcuts

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