Files
fips/packaging/debian/postinst
T
Johnathan Corgan 23c6609a6e Ship fips0 nftables security baseline (Linux)
Add a default-deny nftables ruleset for the fips0 mesh interface as
a packaged operator asset, with a companion fips-firewall.service
oneshot unit for systemd hosts. Both are shipped disabled — the
baseline is an operator conffile and the unit is intentionally not
enabled in postinst. Activation is an explicit one-liner:

  sudo systemctl enable --now fips-firewall.service

This is deliberate: silently mutating host firewall state on package
install is hostile across the axes that matter (collisions with
existing operator nftables / Docker / OPNsense rulesets, surprise
behaviour for hosts that already filter elsewhere, conversion of an
explicit security decision into an invisible one). The opt-in
posture preserves operator agency.

The baseline closes a real default-exposure gap: any service on a
mesh host bound to a wildcard address (0.0.0.0 or [::]) is
reachable from every authenticated peer in the mesh by default.
Identity on the mesh is the peer's npub but identity is not
authorization, and the mesh is closer to a shared LAN than to the
public internet. With this filter loaded, the surface is closed
unless a drop-in opens it explicitly.

Baseline shape:
- Early-return for non-fips0 traffic (every other firewall left
  undisturbed)
- conntrack established/related accept (replies to outbound flows)
- ICMPv6 echo-request accept (ping6 reachability)
- include "/etc/fips/fips.d/*.nft" — operator-supplied allowances
- counter drop default

The accompanying docs/fips-security.md lays out the threat model
(npub-authenticated mesh is closer to a shared LAN than to the
public internet — identity is not authorization), the activation
workflow, drop-in extension recipes (allow inbound SSH from a
specific peer fd97:.../128, allow HTTP from one /64, etc), drop
visibility / debugging via the journal log rule and the drop
counter, coexistence with the runtime-managed `inet fips_gateway`
table, what the baseline does NOT cover (outbound, application
auth, mesh handshake ACL = PR #50 / IDEA-0047 territory), and
future cross-OS work (macOS PF baseline, OpenWrt fw4, gateway
abstraction).

Packaging:
- packaging/common/fips.nft       → /etc/fips/fips.nft (conffile)
- packaging/debian/fips-firewall.service → /lib/systemd/system/
- docs/fips-security.md           → /usr/share/doc/fips/
- postinst creates /etc/fips/fips.d/ (mode 0755) on configure
- prerm stops/disables fips-firewall.service on remove/purge

OpenWrt fw4 path and macOS PF baseline are deferred — separate
asymmetries, separate work.
2026-04-30 03:10:56 +00:00

48 lines
1.6 KiB
Bash
Executable File

#!/bin/sh
# FIPS post-install script for Debian/Ubuntu
set -e
case "$1" in
configure)
# Create fips system group for control socket access
if ! getent group fips >/dev/null 2>&1; then
groupadd --system fips
fi
# Drop-in directory for operator nftables rules included by
# /etc/fips/fips.nft. Empty by default; the include glob matches
# nothing cleanly out of the box.
if [ ! -d /etc/fips/fips.d ]; then
mkdir -p /etc/fips/fips.d
chmod 755 /etc/fips/fips.d
fi
# Ensure runtime directory exists with correct ownership
if [ -d /run/systemd/system ]; then
systemd-tmpfiles --create /usr/lib/tmpfiles.d/fips.conf 2>/dev/null || true
fi
# Reload systemd and enable services. fips-firewall.service is
# intentionally NOT enabled here — operators opt in explicitly
# with `systemctl enable --now fips-firewall.service`. See
# /usr/share/doc/fips/fips-security.md for the rationale.
if [ -d /run/systemd/system ]; then
systemctl daemon-reload
systemctl enable fips.service 2>/dev/null || true
systemctl enable fips-dns.service 2>/dev/null || true
# On upgrade, restart services that were running before
if [ -n "$2" ]; then
systemctl start fips.service 2>/dev/null || true
if systemctl is-enabled --quiet fips-dns.service 2>/dev/null; then
systemctl start fips-dns.service 2>/dev/null || true
fi
fi
fi
;;
esac
#DEBHELPER#
exit 0