dify/docker/ssrf_proxy/test_ssrf_proxy_config.sh

144 lines
4.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
IMAGE="${SSRF_PROXY_TEST_IMAGE:-ubuntu/squid:latest}"
CLIENT_IMAGE="${SSRF_PROXY_TEST_CLIENT_IMAGE:-busybox:latest}"
CONTAINER_NAME="${SSRF_PROXY_TEST_CONTAINER:-dify-ssrf-proxy-test-$$}"
SANDBOX_CONTAINER_NAME="${CONTAINER_NAME}-sandbox"
NETWORK_NAME="${SSRF_PROXY_TEST_NETWORK:-dify-ssrf-proxy-test-$$}"
RUN_PUBLIC_CHECK="${SSRF_PROXY_TEST_PUBLIC_CHECK:-true}"
cleanup() {
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
docker rm -f "$SANDBOX_CONTAINER_NAME" >/dev/null 2>&1 || true
docker network rm "$NETWORK_NAME" >/dev/null 2>&1 || true
}
http_code_for() {
local proxy_url="$1"
local target_url="$2"
local output
output="$(
docker run \
--rm \
--network "$NETWORK_NAME" \
--env "http_proxy=$proxy_url" \
--env "https_proxy=$proxy_url" \
"$CLIENT_IMAGE" \
wget -S -O /dev/null -T 10 "$target_url" 2>&1 || true
)"
printf '%s\n' "$output" | awk '$1 ~ /^HTTP\// { code = $2 } END { print code }'
}
direct_http_code_for() {
local target_url="$1"
local output
output="$(
docker run \
--rm \
--network "$NETWORK_NAME" \
"$CLIENT_IMAGE" \
wget -S -O /dev/null -T 10 "$target_url" 2>&1 || true
)"
printf '%s\n' "$output" | awk '$1 ~ /^HTTP\// { code = $2 } END { print code }'
}
assert_private_target_blocked() {
local proxy_url="$1"
local target_url="$2"
local status_code
status_code="$(http_code_for "$proxy_url" "$target_url")"
if [[ "$status_code" != "403" ]]; then
echo "Expected $target_url to be blocked with HTTP 403, got ${status_code:-no response}."
docker logs "$CONTAINER_NAME" >&2 || true
exit 1
fi
}
assert_public_target_allowed() {
local proxy_url="$1"
local target_url="$2"
local status_code
status_code="$(http_code_for "$proxy_url" "$target_url")"
if [[ ! "$status_code" =~ ^[234][0-9][0-9]$ || "$status_code" == "403" ]]; then
echo "Expected $target_url to remain reachable, got ${status_code:-no response}."
docker logs "$CONTAINER_NAME" >&2 || true
exit 1
fi
}
assert_sandbox_bridge_allowed() {
local target_url="$1"
local status_code
status_code="$(direct_http_code_for "$target_url")"
if [[ ! "$status_code" =~ ^2[0-9][0-9]$ ]]; then
echo "Expected sandbox host bridge $target_url to remain reachable, got ${status_code:-no response}."
docker logs "$CONTAINER_NAME" >&2 || true
docker logs "$SANDBOX_CONTAINER_NAME" >&2 || true
exit 1
fi
}
trap cleanup EXIT
cleanup
docker network create "$NETWORK_NAME" >/dev/null
docker run \
--detach \
--name "$SANDBOX_CONTAINER_NAME" \
--network "$NETWORK_NAME" \
--network-alias sandbox \
"$CLIENT_IMAGE" \
sh -c "mkdir -p /www && echo ok > /www/health && httpd -f -p 8194 -h /www" \
>/dev/null
docker run \
--detach \
--name "$CONTAINER_NAME" \
--entrypoint sh \
--network "$NETWORK_NAME" \
--volume "$ROOT_DIR/docker/ssrf_proxy/squid.conf.template:/etc/squid/squid.conf.template:ro" \
--volume "$ROOT_DIR/docker/ssrf_proxy/docker-entrypoint.sh:/docker-entrypoint-mount.sh:ro" \
--env HTTP_PORT=3128 \
--env COREDUMP_DIR=/var/spool/squid \
--env SSRF_SANDBOX_PROXY_PORT=8194 \
--env SSRF_SANDBOX_PROXY_HOST=sandbox \
--env "SSRF_PROXY_ALLOW_PRIVATE_IPS=${SSRF_PROXY_ALLOW_PRIVATE_IPS:-}" \
--env "SSRF_PROXY_ALLOW_PRIVATE_DOMAINS=${SSRF_PROXY_ALLOW_PRIVATE_DOMAINS:-}" \
"$IMAGE" \
-c "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh" \
>/dev/null
proxy_url="http://$CONTAINER_NAME:3128"
for _ in {1..30}; do
probe_status="$(http_code_for "$proxy_url" "http://127.0.0.1:80/")"
if [[ -n "$probe_status" ]]; then
break
fi
sleep 1
done
if [[ -z "${probe_status:-}" ]]; then
echo "Squid proxy did not respond to probes."
docker logs "$CONTAINER_NAME" >&2 || true
exit 1
fi
assert_private_target_blocked "$proxy_url" "http://127.0.0.1:80/"
assert_private_target_blocked "$proxy_url" "http://0.1.2.3:80/"
assert_private_target_blocked "$proxy_url" "http://169.254.169.254/latest/meta-data/"
if [[ "$RUN_PUBLIC_CHECK" == "true" ]]; then
assert_public_target_allowed "$proxy_url" "http://example.com/"
fi
assert_sandbox_bridge_allowed "http://$CONTAINER_NAME:8194/health"