Documentation
¶
Overview ¶
Package ownappfirewall owns the closed network authorization boundary for own-application v2. It never changes the host's general firewall or Docker tables.
Index ¶
- Constants
- func Bridge(network string) string
- func DefinitionDigest() string
- func MAC(network string) string
- func Remove(ctx context.Context, network string) error
- func RestoreExitCode(err error) int
- func RestoreMain(path string, reviewed func(workspace, network string) (Receipt, error)) (err error)
- func RestoreUnit(network string) string
- func RestoreUnitFile(network, executable string) (string, error)
- func Resume(ctx context.Context, network, id, reviewedDigest string) error
- func Run(ctx context.Context, name string, args []string, input []byte) ([]byte, error)
- func StopWatcher(ctx context.Context, network string, remove bool) error
- func Table(network string) string
- func VerifyRunning(ctx context.Context, network, id, reviewedDigest string) (err error)
- func WatchDecision(verify error, egressAt, now time.Time) error
- func WatchMain(path string) error
- func WatchUnit(network string) string
- type Guard
- func (g *Guard) Apply(ctx context.Context) (Receipt, error)
- func (g *Guard) ObserveEgress(ctx context.Context, workspace string) error
- func (g *Guard) Remove(ctx context.Context) error
- func (g *Guard) StartWatcher(ctx context.Context, id string) error
- func (g *Guard) Verify(ctx context.Context) error
- func (g *Guard) VerifyBridge() error
- func (g *Guard) VerifyWatcher(ctx context.Context) error
- type PermanentRefusal
- type Policy
- type Receipt
- type Runner
- type SpecialUse
Constants ¶
const EgressMaxAge = 90 * time.Second
const EgressRefresh = 30 * time.Second
const EgressSet = "household_egress_v4"
Rules exist before the bridge or container. Inet prerouting runs after conntrack but before DNAT; forward repeats destination checks after DNAT. Bridge filtering covers same-bridge peers without depending on br_netfilter. Only conntrack replies to admitted inbound requests may return to a private peer. The application cannot initiate even a connection to its ingress router.
const HostSettleTime = 5 * time.Minute
HostSettleTime is how long after the kernel boot a host-facts mismatch is still retried: interfaces and routes that come up late must not stop the application for good.
const MinimumSystemd = 244
MinimumSystemd is the oldest systemd that accepts Restart=on-failure on a Type=oneshot unit (systemd 244).
const Owner = "stackkit.own-egress/v1"
const RestorePermanentExit = 78
RestorePermanentExit is the restore unit's exit status for a refusal that a retry cannot change: the rule definition or the signed record differ from the snapshot, or the host's network facts still differ once the boot has settled. The unit names it in RestartPreventExitStatus, so systemd stops retrying; the application stays stopped until a reviewed update or start. 78 is EX_CONFIG.
const RestoreTimeout = 150 * time.Second
RestoreTimeout bounds one restore attempt. It leaves margin over the watcher's 20 second notify wait plus discovery, apply and verification.
const WatchInterval = 3 * time.Second
Variables ¶
This section is empty.
Functions ¶
func DefinitionDigest ¶
func DefinitionDigest() string
DefinitionDigest binds the rule compiler, registry data, dynamic host exclusion contract and restart policy to review and signed receipts. Changing enforcement requires changing this definition, and invalidates older reviews.
func RestoreExitCode ¶
RestoreExitCode maps a RestoreMain result to the unit's exit status.
func RestoreMain ¶
func RestoreMain(path string, reviewed func(workspace, network string) (Receipt, error)) (err error)
RestoreMain is the restore unit's only action. Once a start completed in the running boot it does nothing: the reviewed lifecycle owns that boot, and a flushed or changed table is never silently reinstalled. Otherwise it loads the signed reviewed receipt, observes the host again and requires the exact reviewed static ruleset, observes a fresh egress answer, applies the same rules and requires the reviewed receipt, checks the bridge, and only then delegates the start to the watcher. Any failure leaves the application stopped. systemd retries, except after a PermanentRefusal (RestoreExitCode maps it to RestorePermanentExit). An ingress-only policy observes no host facts and needs no egress echo, so it also restores on an offline host.
func RestoreUnit ¶
RestoreUnit is the persistent, enabled unit that brings the application back after a host reboot: it re-applies the exact reviewed ruleset and then hands the start to the watcher. It never starts the container itself.
func RestoreUnitFile ¶
RestoreUnitFile renders the boot-time unit. A failed attempt is retried 30 seconds after it ends, without limit, unless it exits with RestorePermanentExit; every attempt re-verifies everything, so a retry never starts the application without its rules. Restart= on a oneshot unit needs systemd 244 or newer (installRestore checks it).
func Resume ¶
Resume never reapplies rules or adopts fresh host facts. Backup recovery must use the exact reviewed apply snapshot and a still-fresh echo.
func StopWatcher ¶
StopWatcher ends the application's lifetime custody: it first disables (and stops a running attempt of) the boot-time restore, so a stopped application stays stopped across a reboot, then stops the live watcher, whose ExecStopPost kills the container. remove also deletes the restore unit and both snapshots.
func VerifyRunning ¶
func WatchDecision ¶
WatchDecision is the fail-closed lifetime decision, independent of event transport. Missing tables, rule changes, extra networks and stale echoes all terminate the unit and hence trigger systemd's exact-container kill.
Types ¶
type Guard ¶
type Guard struct {
Policy Policy
// contains filtered or unexported fields
}
func New ¶
New observes the host for the static policy of a firewalled application. Without outbound Internet the policy is ingress-only and needs no host facts: no uplink, gateway or route inventory, so a disconnected host can still run an application that only publishes ports.
func (*Guard) ObserveEgress ¶
ObserveEgress uses only the installation's already-configured Me origin. No registration, no echo route, or an unusable answer means no grant. The source-bound direct IPv4 transport excludes proxies and another uplink. The ingress-only policy grants no destination, so it needs no echo: only the workspace is recorded for the watcher and the boot-time restore.
func (*Guard) StartWatcher ¶
StartWatcher delegates the ONLY start to the kit unit. systemd waits for READY after initial verification and start; ExecStopPost kills the exact ID even when the watcher is killed, crashes or fails during startup.
func (*Guard) VerifyBridge ¶
VerifyBridge refuses rootless/remote daemon namespaces: rules in this host namespace cannot authorize a bridge that exists only inside another one. It is called after stopped container/network creation, before any start.
type PermanentRefusal ¶
type PermanentRefusal struct{ Err error }
PermanentRefusal marks a restore refusal that no retry can change.
func (PermanentRefusal) Error ¶
func (p PermanentRefusal) Error() string
func (PermanentRefusal) Unwrap ¶
func (p PermanentRefusal) Unwrap() error
type Policy ¶
type Policy struct {
Network string `json:"network"`
IngressOnly bool `json:"ingressOnly,omitempty"`
HomePrefixes []string `json:"homePrefixes"`
Gateway string `json:"gateway"`
Uplink string `json:"uplink"`
UplinkSource string `json:"uplinkSource"`
NextHops []string `json:"nextHops"`
}
Policy is the static ruleset of one firewalled own application. A firewalled application has outbound Internet, published host ports, or both. IngressOnly is the variant without outbound Internet: the container may only answer connections made to it (its published ports and its router), so it needs no uplink, gateway, egress echo or host facts at all.
func IngressPolicy ¶
IngressPolicy is the complete static policy of a firewalled application without outbound Internet. It depends on the network name only.
func (Policy) AllowsDestination ¶
AllowsDestination is the address portion of the policy. IPv6 is deliberately denied entirely; IPv4-mapped IPv6 is not unwrapped into an IPv4 permission. The ingress-only variant allows no destination at all.
type SpecialUse ¶
type SpecialUse struct {
Prefix string `json:"prefix"`
Name string `json:"name"`
Registry string `json:"registry"`
}
func SpecialRanges ¶
func SpecialRanges() []SpecialUse