From 7f3f9bff6ed31d6da7af7bc80f1c229b510c3736 Mon Sep 17 00:00:00 2001 From: DATA Date: Sun, 12 Apr 2026 15:19:47 +0200 Subject: [PATCH] Add RUN.md quick reference; fix status display in firewall script - RUN.md: start/stop/inspect/smoke-test commands for the hardened DeerFlow stack on data-nuc, including the docker compose -f overlay invocation and a copy-paste smoke test that verifies allow + block destinations from inside the container. - scripts/deerflow-firewall.sh: status now uses iptables -nvL so the input-interface column is included, and the awk filter shows the header plus all rules matching br-deerflow. The previous version used -nL which omits the interface column entirely, so the grep found nothing even when the rules were correctly installed. --- RUN.md | 112 +++++++++++++++++++++++++++++++++++ scripts/deerflow-firewall.sh | 5 +- 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 RUN.md diff --git a/RUN.md b/RUN.md new file mode 100644 index 0000000..06c5e9d --- /dev/null +++ b/RUN.md @@ -0,0 +1,112 @@ +# Running the hardened DeerFlow + +Quick reference for starting, stopping, and inspecting the hardened +DeerFlow stack on **data-nuc**. For the full security rationale see +[HARDENING.md](HARDENING.md). + +## Prerequisites (one-time, already done on data-nuc) + +- `docker` + `docker compose` available +- `/etc/nixos/configuration.nix` imports `scripts/deerflow-firewall.nix` + (verify: `grep deerflow-firewall /etc/nixos/configuration.nix`) +- After config change: `sudo nixos-rebuild switch` +- `systemctl status deerflow-firewall` shows `active (exited)` +- `.env` exists in the repo root with a real `OLLAMA_CLOUD_API_KEY` + (template: `.env.example`) + +## Start + +```bash +cd /home/data/deerflow-factory +docker compose \ + -f deer-flow/docker/docker-compose.yaml \ + -f docker/docker-compose.override.yaml \ + up -d +``` + +The override file pins the upstream `deer-flow` Docker network to a +stable Linux bridge name `br-deerflow` so the host firewall can +reference it. The firewall rules already in `DOCKER-USER` will activate +the moment the bridge appears — no manual action needed. + +## Stop + +```bash +cd /home/data/deerflow-factory +docker compose \ + -f deer-flow/docker/docker-compose.yaml \ + -f docker/docker-compose.override.yaml \ + down +``` + +The firewall rules **stay in place** but become inert (no traffic +matches `-i br-deerflow` once the bridge is gone). They re-activate the +next time you bring the stack up. + +## Inspect + +| What | Command | +|---|---| +| Compose status | `docker compose -f deer-flow/docker/docker-compose.yaml -f docker/docker-compose.override.yaml ps` | +| Container logs | `docker compose -f deer-flow/docker/docker-compose.yaml -f docker/docker-compose.override.yaml logs -f ` | +| Firewall service | `systemctl status deerflow-firewall` | +| Firewall rules | `sudo scripts/deerflow-firewall.sh status` | +| Bridge present? | `ip link show br-deerflow` | + +## Smoke-test the egress firewall + +After the first `up`, verify the container's egress is correctly +constrained. Replace `` with the name of any DeerFlow container +that has `curl` available (try `frontend`, `langgraph`, or `provisioner`): + +```bash +docker compose -f deer-flow/docker/docker-compose.yaml \ + -f docker/docker-compose.override.yaml \ + exec sh -c ' + set -e + echo "[+] Searx (allow):" + curl -s -o /dev/null -w " %{http_code}\n" --max-time 5 http://10.67.67.1:8888/ || echo " fail" + echo "[+] Internet (allow):" + curl -s -o /dev/null -w " %{http_code}\n" --max-time 5 https://api.cloudflare.com/ || echo " fail" + echo "[-] Home LAN gateway (block):" + curl -s -o /dev/null -w " %{http_code}\n" --max-time 5 http://192.168.3.1/ || echo " blocked" + echo "[-] Other Wireguard host (block):" + curl -s -o /dev/null -w " %{http_code}\n" --max-time 5 http://10.67.67.16/ || echo " blocked" +' +``` + +Expected: + +``` +[+] Searx (allow): + 200 +[+] Internet (allow): + 200 (or 4xx — anything that is not "fail" means egress worked) +[-] Home LAN gateway (block): + blocked (curl exits non-zero with "host prohibited") +[-] Other Wireguard host (block): + blocked +``` + +If a "block" line returns an HTTP code instead of "blocked", the +firewall is not constraining that destination — stop the container and +check `sudo scripts/deerflow-firewall.sh status`. + +## Reset firewall after changes to the script + +If you edit `scripts/deerflow-firewall.sh`: + +```bash +sudo systemctl restart deerflow-firewall +sudo scripts/deerflow-firewall.sh status +``` + +## Disable the egress firewall (not recommended) + +```bash +sudo systemctl stop deerflow-firewall # rules removed +sudo systemctl disable deerflow-firewall # no auto-start at boot +``` + +To remove permanently: delete the `imports` entry from +`/etc/nixos/configuration.nix` and `sudo nixos-rebuild switch`. diff --git a/scripts/deerflow-firewall.sh b/scripts/deerflow-firewall.sh index b08e899..0b01d47 100755 --- a/scripts/deerflow-firewall.sh +++ b/scripts/deerflow-firewall.sh @@ -119,8 +119,9 @@ cmd_down() { cmd_status() { require_chain - echo "DOCKER-USER chain (relevant rules):" - iptables -w -nL "$CHAIN" --line-numbers | grep -E "$BRIDGE|^Chain|^num" || true + echo "DOCKER-USER chain (rules matching $BRIDGE):" + # -nvL prints the input interface column so we can grep for our bridge. + iptables -w -nvL "$CHAIN" --line-numbers | awk -v b="$BRIDGE" 'NR<=2 || $0 ~ b' if ip link show "$BRIDGE" >/dev/null 2>&1; then echo