dfd5d731ee
- LocalAPIConfig.island_bridge + island_guest_addr (+ IslandEnabled, Validate all-or-nothing + CIDR guard) - buildBringUpConfig attaches static net1 (island) on provision + DR when set; absent otherwise (pre-R-50 byte-for-byte). Plumbed from cfg.LocalAPI at both RunBringUp sites. Endpoint already follows listen_addr (A0: no template change). - healer stays eth0-only (A3 verify-only) — red-proof test locks the scoping - example config + firewall example rewritten for the island; REUSE updated - 3 non-hollow tests; full green. MinAgent unchanged. Coupling: host-install island config requires agent >= 0.96.0 (vouch first).
50 lines
2.9 KiB
Plaintext
50 lines
2.9 KiB
Plaintext
# felhom-agent local API — host firewall narrowing (doc 03 §6; R-50 island update 2026-07-25)
|
|
#
|
|
# Defense-in-depth for the per-guest local API (the controller→agent channel). The PER-GUEST BEARER
|
|
# TOKEN + the served-leaf pin are the authorization gate; a firewall rule is only an ADDITIONAL layer
|
|
# limiting who can even open the port.
|
|
#
|
|
# === R-50 ISLAND INSTALL (the default on a fresh appliance) =================================
|
|
# The agent binds local_api.listen_addr on the HOST-INTERNAL island bridge — 169.254.253.1:8443 on
|
|
# vmbr9, a bridge with NO physical port (bridge-ports none). That bind is the security win:
|
|
# * Nothing listens on the LAN IP at all, so no LAN host (or off-site attacker on the LAN) can
|
|
# reach the local API — the LAN:8443 surface is CLOSED by the bind, not by a rule.
|
|
# * vmbr9 has no uplink, so 169.254.253.1:8443 is reachable ONLY from the one guest wired to the
|
|
# /30 (169.254.253.2) — the controller. The portless bridge is the isolation.
|
|
# So on an island install NO firewall rule is required for exposure; the topology provides it. If you
|
|
# want belt-and-suspenders, restrict the port to the island bridge (it changes nothing, since nothing
|
|
# off-bridge can route to a portless bridge anyway):
|
|
#
|
|
# nft add rule inet filter input iifname != "vmbr9" ip daddr 169.254.253.1 tcp dport 8443 drop
|
|
#
|
|
# Verify: from the guest, a TLS connect to 169.254.253.1:8443 succeeds; there is no LAN listener to
|
|
# probe (`ss -lnt 'sport = :8443'` shows only the island IP).
|
|
#
|
|
# === LEGACY LAN BIND (byo, --no-island, or an explicit --bridge-ip) =========================
|
|
# When the agent still binds a LAN bridge IP (e.g. 192.168.0.162:8443), the port is exposed to the
|
|
# whole LAN and the subnet-narrowing rule below is worth applying. Replace the bridge IP, port, and
|
|
# the guest bridge subnet with this host's values.
|
|
#
|
|
# Option A — nftables (recommended on PVE 8/9; inet filter table). Insert ABOVE any accept:
|
|
#
|
|
# nft add rule inet filter input ip daddr 192.168.0.162 tcp dport 8443 \
|
|
# ip saddr != 192.168.0.0/24 drop
|
|
# nft add rule inet filter input ip daddr 192.168.0.162 tcp dport 8443 \
|
|
# ip saddr 192.168.0.0/24 accept
|
|
#
|
|
# Option B — iptables:
|
|
#
|
|
# iptables -A INPUT -d 192.168.0.162 -p tcp --dport 8443 -s 192.168.0.0/24 -j ACCEPT
|
|
# iptables -A INPUT -d 192.168.0.162 -p tcp --dport 8443 -j DROP
|
|
#
|
|
# Option C — PVE host firewall (/etc/pve/nodes/<node>/host.fw), if the PVE firewall is enabled.
|
|
# Add under [RULES] (and ensure the firewall is enabled in cluster.fw / host.fw):
|
|
#
|
|
# [RULES]
|
|
# IN ACCEPT -source 192.168.0.0/24 -dport 8443 -proto tcp -log nolog
|
|
# IN DROP -dport 8443 -proto tcp -log nolog
|
|
#
|
|
# Apply at HOST SETUP — a host-level packet-filter change, intentionally OUTSIDE the agent's
|
|
# 3-exception privileged fence (the agent never mutates the host firewall at runtime). The token +
|
|
# leaf-pin still gate the request regardless of which bind is in force.
|