A change made over the network answers with a one-time confirm_token and closes its connection (HTTP/2: a GOAWAY). The web UI and hc send the token straight back to /confirm, which accepts it only for the newest change, from the same user and API token, the same address, and over a new connection: proof that the client can still reach the API through the new ruleset (an established connection would get through anyway). A change that cuts its client off can't be confirmed so, and rolls back by itself. The setting manual_confirm (hc settings set --auto-confirm off) and hc --no-confirm leave confirming to a person. Changes through the local socket still need no confirmation. e2e: 136 checks, among them a lockout change rolled back without anyone confirming. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
3.8 KiB
Go
96 lines
3.8 KiB
Go
package model
|
|
|
|
// Settings are the firewall-wide settings: default policies and the rules
|
|
// Housecarl adds by itself ("built-ins"), each of which can be turned off.
|
|
type Settings struct {
|
|
// Policies for packets no rule decided: accept or drop.
|
|
InputPolicy string `json:"input_policy"`
|
|
ForwardPolicy string `json:"forward_policy"`
|
|
OutputPolicy string `json:"output_policy"`
|
|
// LogPolicyDrops logs (rate-limited) what the input and forward
|
|
// policies drop.
|
|
LogPolicyDrops bool `json:"log_policy_drops,omitempty"`
|
|
|
|
Builtins Builtins `json:"builtins"`
|
|
|
|
// AntispoofV4 and AntispoofV6 are source addresses that can't come
|
|
// from the internet: dropped on external zones (Builtins.Antispoof),
|
|
// together with OwnPrefixes.
|
|
AntispoofV4 []string `json:"antispoof_v4"`
|
|
AntispoofV6 []string `json:"antispoof_v6"`
|
|
// OwnPrefixes are your own public prefixes (your delegated IPv6
|
|
// prefix, say): traffic claiming to come from them can't arrive from
|
|
// outside.
|
|
OwnPrefixes []string `json:"own_prefixes,omitempty"`
|
|
|
|
// Flowtable devices: forwarded TCP and UDP connections between them
|
|
// skip the rules once established (fast path). Empty: off. Names
|
|
// must be exact (no wildcards).
|
|
FlowtableDevices []string `json:"flowtable_devices,omitempty"`
|
|
|
|
// ProbationSeconds is how long a change made over the network has to
|
|
// be confirmed before it is rolled back (0: the default).
|
|
ProbationSeconds int `json:"probation_seconds,omitempty"`
|
|
// ManualConfirm: changes made over the network wait for a person to
|
|
// confirm them. Otherwise the client that made a change confirms it by
|
|
// itself once it reaches the API again over a new connection, from the
|
|
// same address: proof that it can still manage the firewall.
|
|
ManualConfirm bool `json:"manual_confirm,omitempty"`
|
|
}
|
|
|
|
// Builtins are rules Housecarl adds itself, shown as "added by Housecarl".
|
|
type Builtins struct {
|
|
// Loopback accepts everything on lo.
|
|
Loopback bool `json:"loopback"`
|
|
// Established accepts packets of established and related connections.
|
|
Established bool `json:"established"`
|
|
// DropInvalid drops packets conntrack finds invalid.
|
|
DropInvalid bool `json:"drop_invalid"`
|
|
// ICMPv6 accepts the ICMPv6 IPv6 needs (RFC 4890): errors, echo, and
|
|
// neighbour discovery and MLD on the local link.
|
|
ICMPv6 bool `json:"icmpv6"`
|
|
// PingInternal accepts ICMP echo to the firewall from internal zones.
|
|
PingInternal bool `json:"ping_internal"`
|
|
// DHCPv6Client accepts DHCPv6 replies on external zones (an ISP's
|
|
// prefix delegation).
|
|
DHCPv6Client bool `json:"dhcpv6_client"`
|
|
// Antispoof drops impossible source addresses arriving on external
|
|
// zones (AntispoofV4, AntispoofV6, OwnPrefixes).
|
|
Antispoof bool `json:"antispoof"`
|
|
// MSSClamp clamps TCP MSS to the route MTU on external zones (needed
|
|
// with PPPoE).
|
|
MSSClamp bool `json:"mss_clamp"`
|
|
}
|
|
|
|
// Policies.
|
|
const (
|
|
PolicyAccept = "accept"
|
|
PolicyDrop = "drop"
|
|
)
|
|
|
|
// DefaultProbation is how long a network change has to be confirmed.
|
|
const DefaultProbation = 120
|
|
|
|
// DefaultSettings are the settings of a new database: drop what isn't
|
|
// allowed in and through, allow out, every built-in on.
|
|
func DefaultSettings() Settings {
|
|
return Settings{
|
|
InputPolicy: PolicyDrop,
|
|
ForwardPolicy: PolicyDrop,
|
|
OutputPolicy: PolicyAccept,
|
|
Builtins: Builtins{Loopback: true, Established: true, DropInvalid: true, ICMPv6: true,
|
|
PingInternal: true, DHCPv6Client: true, Antispoof: true, MSSClamp: false},
|
|
AntispoofV4: []string{"0.0.0.0/8", "10.0.0.0/8", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12",
|
|
"192.0.2.0/24", "192.168.0.0/16", "198.51.100.0/24", "203.0.113.0/24", "224.0.0.0/4", "240.0.0.0/4"},
|
|
AntispoofV6: []string{"::1/128", "::/128", "fc00::/7", "2001:db8::/32", "ff00::/8"},
|
|
}
|
|
}
|
|
|
|
// Probation is the probation in seconds, with its default.
|
|
func (s Settings) Probation() int {
|
|
if s.ProbationSeconds > 0 {
|
|
return s.ProbationSeconds
|
|
}
|
|
return DefaultProbation
|
|
}
|