quality / changes (push) Successful in 6s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 27s
quality / web (push) Successful in 49s
quality / api (push) Successful in 1m13s
quality / linux-agent-smoke (push) Failing after 1m31s
quality / docker-check (push) Failing after 4m11s
CD / quality (push) Failing after 8m19s
CD / publish (push) Skipped
85 lines
4.4 KiB
Bash
85 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
|
# All firewall mutations run only after unshare creates BOTH a network and mount
|
|
# namespace. Never installs/runs the agent against the runner's host firewall.
|
|
set -Eeuo pipefail
|
|
[[ $(uname -s) == Linux ]] || { echo 'Linux namespace runner required' >&2; exit 1; }
|
|
for command in unshare ip nft ipset iptables ip6tables python3 curl mount flock timeout; do command -v "$command" >/dev/null; done
|
|
temporary=$(mktemp -d)
|
|
trap 'rm -rf -- "$temporary"' EXIT
|
|
cp apps/api/src/agent-scripts/evofw-firewall.sh "$temporary/agent.sh"
|
|
cp scripts/ci/linux-agent-fixture.py "$temporary/server.py"
|
|
parent_net=$(readlink /proc/self/ns/net)
|
|
parent_mount=$(readlink /proc/self/ns/mnt)
|
|
for backend in nft ipset; do
|
|
rm -f "$temporary/port" "$temporary/port6" "$temporary/invalid"
|
|
unshare --net --mount --pid --fork --mount-proc env EVOFW_TEST_PARENT_NET="$parent_net" EVOFW_TEST_PARENT_MOUNT="$parent_mount" \
|
|
bash -s -- "$temporary" "$backend" <<'CHILD'
|
|
set -Eeuo pipefail
|
|
root=$1
|
|
backend=$2
|
|
unset http_proxy HTTP_PROXY https_proxy HTTPS_PROXY ALL_PROXY all_proxy
|
|
export NO_PROXY='*'
|
|
[[ $(readlink /proc/self/ns/net) != "$EVOFW_TEST_PARENT_NET" ]]
|
|
[[ $(readlink /proc/self/ns/mnt) != "$EVOFW_TEST_PARENT_MOUNT" ]]
|
|
mount --make-rprivate /
|
|
# Agent hardcoded config/state/log paths are private mounts, never host writes.
|
|
for directory in /etc /var/lib /var/log /run; do mount -t tmpfs tmpfs "$directory"; done
|
|
if [[ -n "${EVOFW_TEST_ETC_DATA:-}" ]]; then
|
|
[[ "$EVOFW_TEST_ETC_DATA" == /work/helper-etc ]]
|
|
cp "$EVOFW_TEST_ETC_DATA/protocols" "$EVOFW_TEST_ETC_DATA/services" /etc/
|
|
fi
|
|
mkdir -p /etc/evofw /var/lib/evofw
|
|
ip link set lo up
|
|
ip netns add client
|
|
ip link add server0 type veth peer name client0
|
|
ip link set client0 netns client
|
|
ip addr add 198.18.0.1/24 dev server0
|
|
ip -6 addr add 2001:db8:1::1/64 dev server0 nodad
|
|
ip link set server0 up
|
|
ip -n client addr add 198.18.0.2/24 dev client0
|
|
ip -n client -6 addr add 2001:db8:1::2/64 dev client0 nodad
|
|
ip -n client link set client0 up
|
|
ip -n client link set lo up
|
|
python3 "$root/server.py" "$root" &
|
|
server_pid=$!
|
|
trap 'kill "$server_pid" 2>/dev/null || true; ip netns del client 2>/dev/null || true' EXIT
|
|
for attempt in {1..100}; do [[ ! -f "$root/port" ]] || break; sleep .05; done
|
|
port=$(cat "$root/port")
|
|
port6=$(cat "$root/port6")
|
|
cat > /etc/evofw/agent.conf <<CONF
|
|
EVOFW_CP_URL=http://127.0.0.1:$port
|
|
CLIENT_TOKEN=namespace-test-only
|
|
KERNEL_BACKEND=$backend
|
|
EVOFW_SKIP_SELF_UPDATE=1
|
|
CONF
|
|
nft add table inet unrelated
|
|
nft 'add chain inet unrelated input { type filter hook input priority 10; policy accept; }'
|
|
# A fresh connection through a veth verifies actual packet enforcement.
|
|
ip netns exec client curl --noproxy '*' --max-time 2 -fsS "http://198.18.0.1:$port/echo" >/dev/null
|
|
ip netns exec client curl --noproxy '*' --max-time 2 -gfsS "http://[2001:db8:1::1]:$port6/echo" >/dev/null
|
|
bash "$root/agent.sh"
|
|
python3 -c 'import json; assert json.load(open("/var/lib/evofw/telemetry-state.json"))["pending"] == [], "fixture ACK did not drain durable telemetry"'
|
|
[[ $(cat /var/lib/evofw/last_hash) == "sha256:$(printf '1%.0s' {1..64})" ]]
|
|
nft list table inet unrelated >/dev/null
|
|
if ip netns exec client curl --noproxy '*' --max-time 2 -fsS "http://198.18.0.1:$port/echo" >/dev/null; then echo 'deny did not block packet' >&2; exit 1; fi
|
|
if ip netns exec client curl --noproxy '*' --max-time 2 -gfsS "http://[2001:db8:1::1]:$port6/echo" >/dev/null; then echo 'IPv6 deny did not block packet' >&2; exit 1; fi
|
|
# Same hash must reconcile missing owned state, retaining unrelated rules.
|
|
if [[ "$backend" == nft ]]; then nft delete table inet evofw; else ipset flush evofw_deny_v4; fi
|
|
bash "$root/agent.sh"
|
|
if [[ "$backend" == nft ]]; then nft list table inet evofw >/dev/null; else ipset test evofw_deny_v4 198.18.0.2; fi
|
|
nft list table inet unrelated >/dev/null
|
|
touch "$root/invalid"
|
|
if bash "$root/agent.sh"; then echo 'invalid candidate unexpectedly applied' >&2; exit 1; fi
|
|
[[ $(cat /var/lib/evofw/last_hash) == "sha256:$(printf '1%.0s' {1..64})" ]]
|
|
if [[ "$backend" == nft ]]; then
|
|
nft list set inet evofw deny_v4 | grep -q '198.18.0.2'
|
|
nft list set inet evofw deny_v6 | grep -q '2001:db8:1::2'
|
|
else
|
|
ipset test evofw_deny_v4 198.18.0.2
|
|
ipset test evofw_deny_v6 2001:db8:1::2
|
|
fi
|
|
nft list table inet unrelated >/dev/null
|
|
echo "Linux namespace smoke passed ($backend): IPv4+IPv6 apply, real packet deny, drift repair, invalid-candidate last-good, unrelated table preservation"
|
|
CHILD
|
|
done
|