fix: harden DWH Nginx integration gates
This commit is contained in:
@@ -55,6 +55,10 @@ location_block() {
|
||||
' "$file"
|
||||
}
|
||||
|
||||
location_declarations() {
|
||||
effective_lines "$1" | awk "/^location / { print }"
|
||||
}
|
||||
|
||||
contains_exactly_once() {
|
||||
local haystack=$1
|
||||
local needle=$2
|
||||
@@ -70,13 +74,19 @@ contains_line() {
|
||||
check_templates() {
|
||||
local http=$1
|
||||
local location=$2
|
||||
local http_lines auth_lines unavailable_lines dwh_lines
|
||||
local http_lines auth_lines unavailable_lines dwh_lines locations
|
||||
|
||||
[[ -f "$http" && -f "$location" ]] || return 1
|
||||
http_lines=$(effective_lines "$http")
|
||||
auth_lines=$(location_block "$location" '= /_check_dwh_key')
|
||||
unavailable_lines=$(location_block "$location" '@dwh_auth_unavailable')
|
||||
dwh_lines=$(location_block "$location" '/dwh/')
|
||||
locations=$(location_declarations "$location")
|
||||
[[ $(wc -l <<<"$locations") -eq 3 ]] || return 1
|
||||
[[ $(grep -Fxc -- "location = /_check_dwh_key {" <<<"$locations" || true) -eq 1 ]] || return 1
|
||||
[[ $(grep -Fxc -- "location @dwh_auth_unavailable {" <<<"$locations" || true) -eq 1 ]] || return 1
|
||||
[[ $(grep -Fxc -- "location /dwh/ {" <<<"$locations" || true) -eq 1 ]] || return 1
|
||||
|
||||
|
||||
contains_exactly_once "$auth_lines" 'internal;' || return 1
|
||||
contains_exactly_once "$auth_lines" 'proxy_method GET;' || return 1
|
||||
@@ -157,6 +167,24 @@ expect_http_rejected() {
|
||||
! check_templates "$fixture/http.conf" "$fixture/location.conf"
|
||||
}
|
||||
|
||||
expect_postgrest_regex_bypass_rejected() {
|
||||
local fixture="$temp_root/postgrest_regex_bypass"
|
||||
mkdir -- "$fixture"
|
||||
cp -- "$http_template" "$fixture/http.conf"
|
||||
cp -- "$location_template" "$fixture/location.conf"
|
||||
printf "%s\n" "location ~ ^/dwh/ {" " proxy_pass http://127.0.0.1:3001;" "}" >>"$fixture/location.conf"
|
||||
! check_templates "$fixture/http.conf" "$fixture/location.conf"
|
||||
}
|
||||
|
||||
expect_postgrest_duplicate_bypass_rejected() {
|
||||
local fixture="$temp_root/postgrest_duplicate_bypass"
|
||||
mkdir -- "$fixture"
|
||||
cp -- "$http_template" "$fixture/http.conf"
|
||||
cp -- "$location_template" "$fixture/location.conf"
|
||||
printf "%s\n" "location /dwh/ {" " proxy_pass http://127.0.0.1:3001;" "}" >>"$fixture/location.conf"
|
||||
! check_templates "$fixture/http.conf" "$fixture/location.conf"
|
||||
}
|
||||
|
||||
[[ -f "$http_template" ]] || report_fail source_http_exists
|
||||
[[ -f "$location_template" ]] || report_fail source_location_exists
|
||||
check_templates "$http_template" "$location_template" || report_fail source_contract
|
||||
@@ -208,6 +236,12 @@ expect_location_rejected postgrest_bypass 'auth_request /_check_dwh_key;' '# dir
|
||||
|| report_fail negative_postgrest_bypass
|
||||
report_pass negative_postgrest_bypass
|
||||
|
||||
expect_postgrest_regex_bypass_rejected || report_fail negative_postgrest_regex_bypass
|
||||
report_pass negative_postgrest_regex_bypass
|
||||
|
||||
expect_postgrest_duplicate_bypass_rejected || report_fail negative_postgrest_duplicate_bypass
|
||||
report_pass negative_postgrest_duplicate_bypass
|
||||
|
||||
expect_location_rejected failure_mapped_to_success 'error_page 500 =503 @dwh_auth_unavailable;' 'error_page 500 =200 @dwh_auth_unavailable;' \
|
||||
|| report_fail negative_failure_mapped_to_success
|
||||
report_pass negative_failure_mapped_to_success
|
||||
|
||||
@@ -27,15 +27,9 @@ cleanup() {
|
||||
local pid
|
||||
set +e
|
||||
for pid in "${registered_pids[@]}"; do
|
||||
if [[ "$pid" =~ ^[0-9]+$ ]] && kill -0 "$pid" 2>/dev/null; then
|
||||
kill -TERM "$pid" 2>/dev/null
|
||||
fi
|
||||
done
|
||||
for pid in "${registered_pids[@]}"; do
|
||||
if [[ "$pid" =~ ^[0-9]+$ ]]; then
|
||||
wait "$pid" 2>/dev/null
|
||||
fi
|
||||
stop_registered_pid "$pid" || true
|
||||
done
|
||||
registered_pids=()
|
||||
if [[ "$temp_root" == /tmp/thothii-dwh-auth-nginx-integration.* && -d "$temp_root" ]]; then
|
||||
rm -rf -- "$temp_root"
|
||||
fi
|
||||
@@ -119,13 +113,124 @@ expect_status() {
|
||||
[[ "$status" == "$expected" ]] || fail_case "$name"
|
||||
}
|
||||
|
||||
unregister_pid() {
|
||||
local target=$1
|
||||
local candidate
|
||||
local retained=()
|
||||
for candidate in "${registered_pids[@]}"; do
|
||||
[[ "$candidate" == "$target" ]] || retained+=("$candidate")
|
||||
done
|
||||
registered_pids=("${retained[@]}")
|
||||
}
|
||||
|
||||
pid_exited_or_zombie() {
|
||||
local pid=$1
|
||||
local state
|
||||
[[ "$pid" =~ ^[0-9]+$ ]] || return 1
|
||||
[[ ! -d "/proc/$pid" ]] && return 0
|
||||
state=$(awk "{print \$3}" "/proc/$pid/stat" 2>/dev/null) || return 0
|
||||
[[ "$state" == Z* ]]
|
||||
}
|
||||
|
||||
pid_is_direct_child() {
|
||||
local pid=$1
|
||||
local parent
|
||||
[[ -r "/proc/$pid/stat" ]] || return 1
|
||||
parent=$(awk "{print \$4}" "/proc/$pid/stat" 2>/dev/null) || return 1
|
||||
[[ "$parent" == "$$" ]]
|
||||
}
|
||||
|
||||
reap_if_direct_child() {
|
||||
local pid=$1
|
||||
if pid_is_direct_child "$pid"; then
|
||||
wait "$pid" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_exit_or_zombie() {
|
||||
local pid=$1
|
||||
local attempts=${2:-10}
|
||||
local attempt
|
||||
for ((attempt = 0; attempt < attempts; attempt++)); do
|
||||
pid_exited_or_zombie "$pid" && return 0
|
||||
sleep 0.05
|
||||
done
|
||||
pid_exited_or_zombie "$pid"
|
||||
}
|
||||
|
||||
tcp_listener_for_pid() {
|
||||
local pid=$1
|
||||
[[ "$pid" =~ ^[0-9]+$ ]] || return 1
|
||||
ss -ltnpH 2>/dev/null | grep -Eq "(^|[^0-9])pid=$pid([,)]|$)"
|
||||
}
|
||||
|
||||
wait_for_tcp_listener_pid() {
|
||||
local pid=$1
|
||||
local attempt
|
||||
for ((attempt = 0; attempt < 10; attempt++)); do
|
||||
tcp_listener_for_pid "$pid" && return 0
|
||||
sleep 0.05
|
||||
done
|
||||
tcp_listener_for_pid "$pid"
|
||||
}
|
||||
|
||||
stop_registered_pid() {
|
||||
local pid=$1
|
||||
[[ "$pid" =~ ^[0-9]+$ ]] || return 1
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
kill -TERM "$pid"
|
||||
wait "$pid"
|
||||
if pid_exited_or_zombie "$pid"; then
|
||||
reap_if_direct_child "$pid"
|
||||
unregister_pid "$pid"
|
||||
return 0
|
||||
fi
|
||||
if ! kill -TERM "$pid" 2>/dev/null; then
|
||||
wait_for_exit_or_zombie "$pid" 1 || return 1
|
||||
fi
|
||||
if ! wait_for_exit_or_zombie "$pid"; then
|
||||
kill -KILL "$pid" 2>/dev/null || return 1
|
||||
wait_for_exit_or_zombie "$pid" || return 1
|
||||
fi
|
||||
reap_if_direct_child "$pid"
|
||||
unregister_pid "$pid"
|
||||
}
|
||||
|
||||
run_term_ignored_regression() {
|
||||
local child_pid started_seconds registered_pid
|
||||
current_case=cleanup_term_ignored_bounded
|
||||
python3 -c "import signal; signal.signal(signal.SIGTERM, signal.SIG_IGN); signal.pause()" >"$temp_root/term-ignored.log" 2>&1 &
|
||||
child_pid=$!
|
||||
register_pid "$child_pid"
|
||||
started_seconds=$SECONDS
|
||||
if ! stop_registered_pid "$child_pid"; then
|
||||
fail_case cleanup_term_ignored_bounded
|
||||
fi
|
||||
if kill -0 "$child_pid" 2>/dev/null; then
|
||||
fail_case cleanup_term_ignored_bounded
|
||||
fi
|
||||
for registered_pid in "${registered_pids[@]}"; do
|
||||
[[ "$registered_pid" != "$child_pid" ]] || fail_case cleanup_term_ignored_bounded
|
||||
done
|
||||
((SECONDS - started_seconds < 3)) || fail_case cleanup_term_ignored_bounded
|
||||
report_pass cleanup_term_ignored_bounded
|
||||
}
|
||||
|
||||
run_tcp_listener_detector_positive() {
|
||||
local probe_pid
|
||||
current_case=tcp_listener_detector_positive
|
||||
python3 - >"$temp_root/tcp-listener.log" 2>&1 <<PY &
|
||||
import signal
|
||||
import socket
|
||||
|
||||
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
listener.bind(("127.0.0.1", 0))
|
||||
listener.listen()
|
||||
signal.pause()
|
||||
PY
|
||||
probe_pid=$!
|
||||
register_pid "$probe_pid"
|
||||
wait_for_tcp_listener_pid "$probe_pid" || fail_case tcp_listener_detector_positive
|
||||
report_pass tcp_listener_detector_positive
|
||||
stop_registered_pid "$probe_pid" || fail_case tcp_listener_detector_positive
|
||||
! tcp_listener_for_pid "$probe_pid" || fail_case tcp_listener_detector_positive
|
||||
}
|
||||
|
||||
for command in nginx curl python3 ss date; do
|
||||
@@ -139,6 +244,8 @@ temp_root=$(mktemp -d /tmp/thothii-dwh-auth-nginx-integration.XXXXXXXX) || fail_
|
||||
chmod 0700 "$temp_root" || fail_case fixture_root
|
||||
umask 077
|
||||
|
||||
run_term_ignored_regression
|
||||
|
||||
dwh_auth="$temp_root/dwh-auth"
|
||||
current_case=build_dwh_auth
|
||||
build_dwh_auth "$dwh_auth" >"$temp_root/build.log" 2>&1 || fail_case build_dwh_auth
|
||||
@@ -197,6 +304,8 @@ register_pid "$verifier_pid"
|
||||
wait_for_socket "$service_socket" || fail_case verifier_start
|
||||
report_pass verifier_start
|
||||
|
||||
run_tcp_listener_detector_positive
|
||||
|
||||
current_case=synthetic_upstreams
|
||||
AUTH_PROXY_SOCKET="$auth_proxy_socket" \
|
||||
VERIFIER_SOCKET="$service_socket" \
|
||||
@@ -370,6 +479,7 @@ current_case=auth_socket_unix_only
|
||||
[[ -S "$service_socket" ]] || fail_case auth_socket_unix_only
|
||||
ss -xl >"$temp_root/ss-unix.log" 2>&1 || fail_case auth_socket_unix_only
|
||||
grep -Fq -- "$service_socket" "$temp_root/ss-unix.log" || fail_case auth_socket_unix_only
|
||||
! tcp_listener_for_pid "$verifier_pid" || fail_case auth_socket_unix_only
|
||||
report_pass auth_socket_unix_only
|
||||
|
||||
probe_body="$temp_root/probe.body"
|
||||
|
||||
Reference in New Issue
Block a user