Files
logwisp/test/acl-test.sh
T
lixen 4af072f4e5
Test / test (push) Canceled after 0s
Test / e2e (push) Canceled after 0s
v0.25.0 netacl and fixes
2026-10-04 23:43:52 -04:00

89 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# logwisp acl test: each listener kind admits or refuses peers by address
# before TLS, counts and warns of refusals, rejects rules it can never apply,
# and still hands a finite input whole to the clients it admits. One-shot.
# Ports: 15881-15889 on 127.0.0.1. Requires: bash 5+, curl, nc; Linux.
set -u
. "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh"
RUN=$E2E_DIR/run/acl
AUTH=$RUN
e2e_init "$@"
section "Setup"
need curl nc
ports_free 15881 15882 15884 15885 15889
rm -rf "$RUN"
mkdir -p "$RUN/out" "$LOG"
cd "$RUN" || abort "no run directory"
for v in $(compgen -e LOGWISP_); do unset "$v"; done
export HOME=$RUN
lw() { "$BIN" "$@"; }
get() { curl -s --noproxy '*' --max-time 3 "$@"; } # curl ARGS...: never through a proxy
# exit 45: curl could not bind the address; 7, it bound and found 15889 closed
get --interface 127.0.0.2 http://127.0.0.1:15889/
(($? == 45)) && skip_all "this host cannot send from 127.0.0.2"
seq 1 300 | sed 's/^/edge line /' >"$RUN/edge.log"
add_users "$RUN/users.toml" edge-01
section "Rules"
# rejects SINK PATTERN: lw --check fails on SINK with PATTERN in its error
rejects() { ! lw --check --source null --sink "$1" >/dev/null 2>"$LOG/check.err" && grep -q -- "$2" "$LOG/check.err" && echo 1 || echo 0; }
# warns SINK PATTERN: lw --check builds SINK and WARNs PATTERN
warns() { lw --check --source null --sink "$1" 2>&1 >/dev/null | grep ' WARN ' | grep -q -- "$2" && echo 1 || echo 0; }
check "an IPv4 entry on an IPv6 listener is refused" "$(rejects 'tcp,host=::1,port=15881,acl.allow=10.0.0.0/8' "not of the listener's family")"
check "an acl on a dialer is refused" "$(rejects 'tcp_chain,host=127.0.0.1,port=15881,acl.deny=10.0.0.1' 'unknown key "acl"')"
check "a CIDR with host bits warns, naming the network it matches" "$(warns 'tcp,host=127.0.0.1,port=15881,acl.allow=10.0.0.1/8' 'network 10.0.0.0/8')"
check "a deny list alone on a wildcard listener warns" "$(warns 'tcp,port=15881,acl.deny=10.0.0.1' 'admits all that acl.deny does not list')"
check "an allow entry of a whole family warns" "$(warns 'tcp,port=15881,acl.allow=0.0.0.0/0' 'admits every address of its family')"
section "HTTP sink"
spawn serve "$BIN" --preset "serve,path=$RUN/edge.log,listen=127.0.0.1:15881,allow=127.0.0.0/8,deny=127.0.0.2"
wait_port 15881 || abort "the serve preset did not listen" serve
check "a client the allow list names reads the status" "$(get http://127.0.0.1:15881/status | grep -q '"acl":"allow=1 deny=1"' && echo 1 || echo 0)"
get --interface 127.0.0.2 -o /dev/null http://127.0.0.1:15881/status
rc=$?
check "a denied client is closed before a byte of HTTP (curl exit $rc)" "$( ((rc == 52 || rc == 56)) && echo 1 || echo 0)"
get --interface 127.0.0.2 http://127.0.0.1:15881/status
get --interface 127.0.0.2 http://127.0.0.1:15881/status
check "the status counts every refusal" "$(get http://127.0.0.1:15881/status | grep -q '"acl_denied":3' && echo 1 || echo 0)"
check "and the log warns of them once ($(count serve 'refused by acl'))" \
"$( [[ $(count serve 'refused by acl') == 1 ]] && logged serve 'remote_addr 127.0.0.2:' && echo 1 || echo 0)"
section "TCP sink"
# the lines of a finite input, once the clients had time to connect
seq 1 20000 | sed 's/^/flush /' >"$RUN/flush.in"
{ wait_port 15882; sleep 1; cat "$RUN/flush.in"; } |
lw --sink "tcp,host=127.0.0.1,port=15882,acl.allow=127.0.0.1,buffer_size=20000,client_buffer_size=20000" 2>"$LOG/tcp.err" &
fpid=$!
wait_port 15882
tcp_read 15882 15 >"$RUN/flush.out" &
rpid=$!
denied=$(timeout 3 nc -s 127.0.0.2 127.0.0.1 15882 </dev/null | wc -c)
wait "$rpid" "$fpid"
n=$(grep -c '^flush ' "$RUN/flush.out")
check "a client the acl admits gets all 20000 lines of a finite input ($n)" "$((n == 20000))"
check "one it denies gets nothing ($denied bytes) and is logged" \
"$( ((denied == 0)) && grep -q 'refused by acl.* remote_addr 127.0.0.2:' "$LOG/tcp.err" && echo 1 || echo 0)"
section "Chain sources"
# pin DAEMON: the pin_sha256 a self-signed listener logged
pin() { wait_until 5 logged "$1" pin_sha256 && grep -o 'pin_sha256 "sha256//[^"]*"' "$LOG/$1.out" | head -1 | cut -d'"' -f2; }
received() { cat "$RUN/out/$1"/aggregate*.log 2>/dev/null | grep -c 'edge line'; }
edge() { spawn "$1" "$BIN" --preset "edge,path=$RUN/edge.log,from=start,to=127.0.0.1:$2,transport=$3,pin=$4,user=edge-01,password_file=$RUN/edge-01.pass"; }
spawn agg-allow "$BIN" --preset "aggregator,listen=127.0.0.1:15884,allow=127.0.0.1,users=$RUN/users.toml,out=$RUN/out/allow"
spawn agg-deny "$BIN" --preset "aggregator,listen=127.0.0.1:15885,transport=http,deny=127.0.0.0/8,users=$RUN/users.toml,out=$RUN/out/deny"
wait_port 15884 && wait_port 15885 || abort "an aggregator did not listen" agg-allow
edge edge-allow 15884 tcp "$(pin agg-allow)"
edge edge-deny 15885 http "$(pin agg-deny)"
timeout 3 nc -s 127.0.0.2 127.0.0.1 15884 </dev/null
wait_until 10 eval '(( $(received allow) >= 300 ))'
check "a tcp_chain source admits an edge its allow list names ($(received allow)), and refuses 127.0.0.2" \
"$( (($(received allow) == 300)) && logged agg-allow 'refused by acl.* remote_addr 127.0.0.2:' && echo 1 || echo 0)"
wait_until 10 logged agg-deny 'refused by acl'
check "an http_chain source refuses a denied edge, which delivers nothing ($(received deny))" \
"$(logged agg-deny 'refused by acl' && (($(received deny) == 0)) && echo 1 || echo 0)"
summary