Network isolation: egress firewall + named bridge

Adds the host-level egress firewall recommended by the upstream
DeerFlow team's "run in a VLAN" guidance, adapted to a Fritzbox-only
home network where LAN VLANs are not available.

- docker/docker-compose.override.yaml: pins the upstream deer-flow
  Docker network to a stable Linux bridge name br-deerflow so the
  firewall can address it without guessing Docker's auto-generated
  br-<hash>. Used as a -f overlay on top of the upstream compose file.

- scripts/deerflow-firewall.sh: idempotent up/down/status wrapper that
  installs DOCKER-USER iptables rules. Allowlist for 10.67.67.1 (Searx)
  and 10.67.67.2 (XTTS/Whisper/Ollama-local), hard block for
  192.168.3.0/24 (home LAN), 10.0.0.0/8, 172.16.0.0/12. Stateful return
  rule keeps inbound LAN access to published ports working.

- scripts/deerflow-firewall.nix: NixOS module snippet defining a
  systemd unit ordered After=docker.service so the rules survive
  dockerd restarts and follow its lifecycle. Copy into
  configuration.nix and nixos-rebuild switch.

- HARDENING.md: new section 2.7 "Network isolation (egress firewall)"
  with allow/block tables, bring-up steps, and smoke-test commands.

Guarantees: rules match on -i br-deerflow, so if the bridge does not
exist, the rules are no-ops and do not affect any other container
(paperclip, telebrowser, openclaw-gateway, ...). Stopping the
container leaves the rules in place but inert; stopping the systemd
unit removes them.
This commit is contained in:
2026-04-12 14:56:26 +02:00
parent 6de0bf9f5b
commit 75315d958e
4 changed files with 298 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
# NixOS module snippet for the DeerFlow egress firewall.
#
# Copy the systemd.services block below into your /etc/nixos/configuration.nix
# (or import this file from there). After `sudo nixos-rebuild switch`, the
# unit `deerflow-firewall.service` is started automatically after Docker and
# stays "active (exited)" so the rules persist for the lifetime of dockerd.
#
# Activation order:
# docker.service -> deerflow-firewall.service
#
# The unit pulls the script straight from the repo at
# /home/data/deerflow-factory/scripts/deerflow-firewall.sh — change the path
# below if you check the repo out somewhere else.
#
# Disable / remove:
# sudo systemctl stop deerflow-firewall # rules go down
# sudo systemctl disable deerflow-firewall # no auto-start
# ...then remove the block from configuration.nix and rebuild.
#
# Verify:
# systemctl status deerflow-firewall
# sudo /home/data/deerflow-factory/scripts/deerflow-firewall.sh status
{ config, pkgs, ... }:
{
systemd.services.deerflow-firewall = {
description = "DeerFlow container egress firewall";
# Make sure dockerd has created the DOCKER-USER chain before we touch it,
# and rerun the unit when docker restarts so our rules are reapplied.
after = [ "docker.service" ];
requires = [ "docker.service" ];
partOf = [ "docker.service" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.iptables pkgs.iproute2 pkgs.bash ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
# Adjust this path if you store the repo elsewhere.
ExecStart = "/home/data/deerflow-factory/scripts/deerflow-firewall.sh up";
ExecStop = "/home/data/deerflow-factory/scripts/deerflow-firewall.sh down";
};
};
}