v0.26.1 per-client connection caps
This commit is contained in:
1 parent
ee01cb00fe
commit
cdd4fc01a7
36 files changed
+795
-417
No files matched your search
+48
-5
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# logwisp acl test: each listener kind admits or refuses peers by address
|
||||
# before TLS, or by the client a PROXY header names (stub proxy, nginx if
|
||||
# installed), counts and warns of refusals, and throttles logins per client.
|
||||
# before TLS, or by the client a PROXY header or a proxy's X-Forwarded-For
|
||||
# names (stub proxy, nginx if installed), counts and warns of refusals, and
|
||||
# caps connections, requests and logins per client.
|
||||
# One-shot. Ports: 15881-15889 on 127.0.0.1, the proxies' on 127.0.0.11-14.
|
||||
# Requires: bash 5+, curl, nc, go; Linux.
|
||||
|
||||
@@ -102,6 +103,10 @@ check "a deny list alone on a wildcard listener warns" "$(warns 'tcp,port=15881,
|
||||
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')"
|
||||
check "a proxy_from entry reaching public addresses warns" \
|
||||
"$(warns 'tcp,host=127.0.0.1,port=15881,acl.proxy_protocol=optional,acl.proxy_from=203.0.113.0/24' 'proxy_from reaches public addresses')"
|
||||
check "a request rate on a tcp listener is refused" \
|
||||
"$(rejects 'tcp,host=127.0.0.1,port=15881,acl.requests_per_second_per_client=5' 'applies only to HTTP listeners')"
|
||||
check "a connection cap behind trusted_proxies, which would count the proxy, is refused" \
|
||||
"$(rejects "http,host=127.0.0.1,port=15881,auth.type=scram,auth.credentials_file=$RUN/users.toml,auth.trusted_proxies=127.0.0.1,acl.max_connections_per_client=2" 'behind trusted_proxies')"
|
||||
check "optional proxy_protocol warns that a headerless client passes as the proxy" \
|
||||
"$(warns 'tcp,host=127.0.0.1,port=15881,acl.proxy_protocol=optional,acl.proxy_from=127.0.0.1' 'passes as the proxy')"
|
||||
|
||||
@@ -134,6 +139,25 @@ check "a client the acl admits gets all 20000 lines of a finite input ($n)" "$((
|
||||
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 "Behind an L7 proxy"
|
||||
# proxy mode over plaintext loopback: curl is the proxy, its headers name the client
|
||||
spawn web-proxied "$BIN" --source null --sink "http,host=127.0.0.1,port=15882,auth.type=scram,auth.credentials_file=$RUN/users.toml,auth.trusted_proxies=127.0.0.1,acl.deny=198.51.100.5,acl.deny=2001:db8::/32,acl.requests_per_second_per_client=1"
|
||||
wait_port 15882 || abort "the proxy-mode http sink did not listen" web-proxied
|
||||
# as CLIENT [PATH [CURL ARGS]]: the status of a request forwarded for CLIENT,
|
||||
# GET /status by default; there 401 is past the acl, short of a login
|
||||
as() { get -o /dev/null -w '%{http_code}' -H "X-Forwarded-For: $1" -H 'X-Forwarded-Proto: https' "${@:3}" "http://127.0.0.1:15882${2:-/status}"; }
|
||||
codes="$(as 198.51.100.5) $(as 2001:db8::5)"
|
||||
wait_until 5 logged web-proxied 'Request refused by acl.* remote_addr 198.51.100.5 .*address rules'
|
||||
check "forwarded clients the acl denies, of either family, are refused per request, and logged once ($codes)" \
|
||||
"$( [[ $codes == '403 403' && $(count web-proxied 'refused by acl') == 1 ]] && echo 1 || echo 0)"
|
||||
codes="$(as 198.51.100.7) $(as 198.51.100.7)"
|
||||
check "a forwarded client past requests_per_second_per_client gets 429 ($codes)" "$(is "$codes" '401 429')"
|
||||
code=$(as 198.51.100.8)
|
||||
check "while another behind the same proxy is not ($code)" "$(is "$code" 401)"
|
||||
hello=(-H 'Content-Type: application/json' -d '{"logwisp":1,"scram":{"username":"edge-01","client_nonce":"nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"}}')
|
||||
codes="$(as 198.51.100.9 /auth "${hello[@]}") $(as 198.51.100.9 /auth "${hello[@]}")"
|
||||
check "the SCRAM exchange, which its throttling limits, is not counted in the rate ($codes)" "$(is "$codes" '200 200')"
|
||||
|
||||
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; }
|
||||
@@ -158,6 +182,7 @@ section "Behind a PROXY header"
|
||||
behind=acl.proxy_protocol=required,acl.proxy_from=127.0.0.1
|
||||
scram="tls.enabled=true,tls.self_signed=true,auth.type=scram,auth.credentials_file=$RUN/users.toml"
|
||||
spawn sink-behind "$BIN" --logging.level=info --source null --sink "http,host=127.0.0.1,port=15886,$scram,$behind,acl.deny=198.51.100.2"
|
||||
spawn tcp-capped "$BIN" --source null --sink "tcp,host=127.0.0.1,port=15889,$behind,acl.max_connections_per_client=2"
|
||||
for t in tcp http; do
|
||||
spawn "agg-$t-behind" "$BIN" --logging.level=info \
|
||||
--source "${t}_chain,host=127.0.0.1,port=$([[ $t == tcp ]] && echo 15888 || echo 15883),$scram,$behind" \
|
||||
@@ -167,11 +192,12 @@ done
|
||||
{ wait_until 30 test -e "$RUN/feed"; seq 1 100 | sed 's/^/behind /'; } |
|
||||
lw --sink "tcp,host=127.0.0.1,port=15887,$behind,acl.deny=198.51.100.2,acl.deny=127.0.0.6" 2>"$LOG/tcp-behind.out" &
|
||||
fpid=$!
|
||||
for p in 15883 15886 15887 15888; do wait_port $p || abort "a listener behind the proxy did not listen on $p" sink-behind; done
|
||||
for p in 15883 15886 15887 15888 15889; do wait_port $p || abort "a listener behind the proxy did not listen on $p" sink-behind; done
|
||||
spawn proxy "$RUN/proxy/proxy" 127.0.0.11:15886,127.0.0.1:15886,1,198.51.100.1 \
|
||||
127.0.0.12:15886,127.0.0.1:15886,2,198.51.100.2 127.0.0.13:15886,127.0.0.1:15886,2,198.51.100.3 \
|
||||
127.0.0.11:15887,127.0.0.1:15887,2,198.51.100.1 127.0.0.12:15887,127.0.0.1:15887,1,198.51.100.2 \
|
||||
127.0.0.11:15888,127.0.0.1:15888,1,198.51.100.1 127.0.0.13:15883,127.0.0.1:15883,2,198.51.100.3
|
||||
127.0.0.11:15888,127.0.0.1:15888,1,198.51.100.1 127.0.0.13:15883,127.0.0.1:15883,2,198.51.100.3 \
|
||||
127.0.0.11:15889,127.0.0.1:15889,1,198.51.100.1 127.0.0.12:15889,127.0.0.1:15889,2,198.51.100.2
|
||||
wait_until 10 logged proxy ready || abort "the stub proxy did not start" proxy
|
||||
NGINX=0
|
||||
if command -v nginx >/dev/null; then
|
||||
@@ -214,8 +240,9 @@ touch "$RUN/feed"
|
||||
wait "$fpid" "${pids[@]}"
|
||||
lines() { grep -c '^behind ' "$RUN/$1.out"; }
|
||||
check "a tcp sink streams to the client a v2 header names ($(lines admitted) lines)" "$(($(lines admitted) == 100))"
|
||||
# the WARN names whichever denied client came first, the stub's or nginx's
|
||||
check "and refuses one a v1 header names, logging both addresses ($(lines denied) lines)" \
|
||||
"$( (($(lines denied) == 0)) && grep -q 'refused by acl.* remote_addr 198.51.100.2:.*peer_addr 127.0.0.1:' "$LOG/tcp-behind.out" && echo 1 || echo 0)"
|
||||
"$( (($(lines denied) == 0)) && grep -Eq 'refused by acl.* remote_addr (198.51.100.2|127.0.0.6):.*peer_addr 127.0.0.1:' "$LOG/tcp-behind.out" && echo 1 || echo 0)"
|
||||
if ((NGINX)); then
|
||||
check "nginx stream with proxy_protocol on names its clients ($(lines nginx) lines, $(lines nginx-denied) to a denied one)" \
|
||||
"$( (($(lines nginx) == 100 && $(lines nginx-denied) == 0)) && echo 1 || echo 0)"
|
||||
@@ -223,6 +250,22 @@ else
|
||||
skip "nginx stream with proxy_protocol on (nginx with its stream module is not available)"
|
||||
fi
|
||||
|
||||
# tcp sink capped at two connections per client: two held, then a third and another client's
|
||||
held() { timeout 2 nc "$@" 15889 </dev/null >/dev/null; (($? == 124)) && echo 1 || echo 0; } # NC_ARGS... ADDR
|
||||
pids=()
|
||||
for from in 127.0.0.11 127.0.0.11 '-s 127.0.0.2 127.0.0.1' '-s 127.0.0.2 127.0.0.1'; do
|
||||
# shellcheck disable=SC2086 # NC_ARGS
|
||||
timeout 6 nc $from 15889 </dev/null >/dev/null &
|
||||
pids+=($!)
|
||||
done
|
||||
sleep 1
|
||||
third=$(held 127.0.0.11) other=$(held 127.0.0.12)
|
||||
check "a third connection from the client a header names is closed, another client's held ($third, $other)" \
|
||||
"$( ((!third && other)) && grep -q 'refused by acl.* remote_addr 198.51.100.1:.*max_connections_per_client.*peer_addr 127.0.0.1:' "$LOG/tcp-capped.out" && echo 1 || echo 0)"
|
||||
third=$(held -s 127.0.0.2 127.0.0.1) other=$(held -s 127.0.0.3 127.0.0.1)
|
||||
check "and so for direct peers, outside proxy_from ($third, $other)" "$( ((!third && other)) && echo 1 || echo 0)"
|
||||
wait "${pids[@]}"
|
||||
|
||||
# http sink: each client its own login budget behind the one proxy address
|
||||
PIN=$(pin sink-behind)
|
||||
token() { lw auth token --url "https://$1:15886" -u viewer-01 --password-file "$2" --pin-sha256 "$PIN" 2>&1; }
|
||||
|
||||
Reference in new issue
Block a user