quality / changes (push) Successful in 7s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 30s
quality / web (push) Successful in 1m3s
quality / api (push) Successful in 1m15s
quality / linux-agent-smoke (push) Failing after 27s
quality / docker-check (push) Failing after 2m28s
CD / quality (push) Failing after 5m52s
CD / publish (push) Skipped
Implement reliable agent apply, telemetry v2, analytics retention, guarded rollouts, enrollment, labels and webhooks. Improve ReUI frontend data safety and bounded list loading, and add Docker smoke, recovery and resource checks. Document local verification and pending native acceptance.
81 lines
4.2 KiB
Bash
81 lines
4.2 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
|
|
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
|