Files
fips/testing/firewall/generate-configs.sh
Johnathan Corgan 0e42c789be Let the firewall suite float its docker subnet
The compose pinned 172.32.0.0/24 with a per-container ipv4_address, so two
concurrent runs asked docker for the same address space and the second failed
with a pool-overlap error. Request no subnet and let docker assign one from the
daemon pool. Peers address each other by the docker hostname the compose already
sets (host-a, host-b) rather than by literal IP; docker's embedded DNS is
per-network, so the same hostname in two runs resolves inside each run's own
subnet. Nothing this suite asserts on moves as a result: its checks run over the
fips0 overlay, whose addresses are derived from node npubs and are independent of
docker addressing, and the packaged nftables ruleset matches on interface rather
than on any address.

The generated config directory moves under the run suffix for the same reason it
did in the acl suite, and the run teardown gains the matching removal so a run
no longer leaves its directory behind.

Case (b) also wrote curl's output to a fixed path under /tmp. Two concurrent
runs shared that one file, and either run's cleanup landing between the other's
write and read left an empty read, failing the http_code check for a reason
having nothing to do with the firewall. It uses mktemp now.

As in the acl suite, the floating subnet removes one of the two obstacles to
concurrent runs. The compose project name is still fixed; the local CI runner
scopes it externally, a bare hand run does not, and the comment at the site says
so rather than claiming the file is self-sufficient.
2026-07-25 19:01:56 +00:00

121 lines
3.3 KiB
Bash
Executable File

#!/bin/bash
# Generate fixtures for the firewall integration test.
#
# Two FIPS nodes (a, b). node-b mounts the production fips.nft baseline
# plus a single drop-in (.nft) under /etc/fips/fips.d/ that allows TCP
# port 22 inbound — the test asserts this is honored. node-a is a
# probe-only node with no firewall.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Scoped by the per-run suffix because this directory is wiped and rewritten
# below: two runs sharing one output directory would delete each other's
# fixtures out from under running containers. Unset (a bare hand run, or the
# GitHub-hosted path) it collapses to the historical "generated-configs".
GENERATED_DIR="$SCRIPT_DIR/generated-configs${FIPS_CI_NAME_SUFFIX:-}"
# Deterministic test identities (mirrors the acl-allowlist style).
NPUB_A="npub1sjlh2c3x9w7kjsqg2ay080n2lff2uvt325vpan33ke34rn8l5jcqawh57m"
KEY_A="0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"
NPUB_B="npub1tdwa4vjrjl33pcjdpf2t4p027nl86xrx24g4d3avg4vwvayr3g8qhd84le"
KEY_B="b102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fb0"
write_file() {
local path="$1"
mkdir -p "$(dirname "$path")"
cat > "$path"
}
write_hosts_file() {
local node="$1"
write_file "$GENERATED_DIR/$node/hosts" <<EOF
node-a $NPUB_A
node-b $NPUB_B
EOF
}
# Peers are addressed by the docker hostname the compose file assigns (host-a,
# host-b), not by IP. The network requests no subnet so that two concurrent
# runs cannot collide on one address range, which means neither node's address
# is knowable before `docker compose up`.
echo "Generating firewall fixtures..."
rm -rf "$GENERATED_DIR"
# ── node-a ────────────────────────────────────────────────────────────
write_file "$GENERATED_DIR/node-a/fips.yaml" <<EOF
node:
identity:
persistent: true
tun:
enabled: true
name: fips0
mtu: 1280
dns:
enabled: true
transports:
udp:
bind_addr: "0.0.0.0:2121"
peers:
- npub: "$NPUB_B"
alias: "node-b"
addresses:
- transport: udp
addr: "host-b:2121"
connect_policy: auto_connect
EOF
write_file "$GENERATED_DIR/node-a/fips.key" <<EOF
$KEY_A
EOF
# ── node-b ────────────────────────────────────────────────────────────
write_file "$GENERATED_DIR/node-b/fips.yaml" <<EOF
node:
identity:
persistent: true
tun:
enabled: true
name: fips0
mtu: 1280
dns:
enabled: true
transports:
udp:
bind_addr: "0.0.0.0:2121"
peers:
- npub: "$NPUB_A"
alias: "node-a"
addresses:
- transport: udp
addr: "host-a:2121"
connect_policy: auto_connect
EOF
write_file "$GENERATED_DIR/node-b/fips.key" <<EOF
$KEY_B
EOF
# ── node-b drop-in: allow inbound TCP/22 (Case d) ─────────────────────
# The simplest possible operator-supplied allowance, matching the
# fips.nft header example. The test asserts this rule unblocks an
# otherwise-DROP'd TCP/22 SYN.
write_file "$GENERATED_DIR/node-b/fips.d/services.nft" <<'EOF'
tcp dport 22 accept
EOF
write_hosts_file node-a
write_hosts_file node-b
echo "Firewall fixtures written to $GENERATED_DIR"