update 26.7.26
This commit is contained in:
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Modified based on Squid OCI image entrypoint
|
||||
|
||||
# This entrypoint aims to forward the squid logs to stdout to assist users of
|
||||
# common container related tooling (e.g., kubernetes, docker-compose, etc) to
|
||||
# access the service logs.
|
||||
|
||||
# Moreover, it invokes the squid binary, leaving all the desired parameters to
|
||||
# be provided by the "command" passed to the spawned container. If no command
|
||||
# is provided by the user, the default behavior (as per the CMD statement in
|
||||
# the Dockerfile) will be to use Ubuntu's default configuration [1] and run
|
||||
# squid with the "-NYC" options to mimic the behavior of the Ubuntu provided
|
||||
# systemd unit.
|
||||
|
||||
# [1] The default configuration is changed in the Dockerfile to allow local
|
||||
# network connections. See the Dockerfile for further information.
|
||||
|
||||
echo "[ENTRYPOINT] re-create snakeoil self-signed certificate removed in the build process"
|
||||
if [ ! -f /etc/ssl/private/ssl-cert-snakeoil.key ]; then
|
||||
/usr/sbin/make-ssl-cert generate-default-snakeoil --force-overwrite > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
tail -F /var/log/squid/access.log 2>/dev/null &
|
||||
tail -F /var/log/squid/error.log 2>/dev/null &
|
||||
tail -F /var/log/squid/store.log 2>/dev/null &
|
||||
tail -F /var/log/squid/cache.log 2>/dev/null &
|
||||
|
||||
ALLOW_PRIVATE_CONF=/etc/squid/dify_allow_private.conf
|
||||
SANDBOX_PROXY_CONF=/etc/squid/dify_sandbox_proxy.conf
|
||||
|
||||
write_optional_private_allowlist() {
|
||||
local env_name="$1"
|
||||
local acl_name="$2"
|
||||
local acl_type="$3"
|
||||
local raw_values="${!env_name:-}"
|
||||
|
||||
raw_values="${raw_values//,/ }"
|
||||
|
||||
if [ -z "${raw_values//[[:space:]]/}" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
printf 'acl %s %s' "$acl_name" "$acl_type" >> "$ALLOW_PRIVATE_CONF"
|
||||
for value in $raw_values; do
|
||||
printf ' %s' "$value" >> "$ALLOW_PRIVATE_CONF"
|
||||
done
|
||||
printf '\nhttp_access allow client_localnet %s\n' "$acl_name" >> "$ALLOW_PRIVATE_CONF"
|
||||
}
|
||||
|
||||
{
|
||||
echo "# Generated by docker-entrypoint.sh."
|
||||
echo "# Allows selected private targets before the default private-network deny rule."
|
||||
} > "$ALLOW_PRIVATE_CONF"
|
||||
write_optional_private_allowlist "SSRF_PROXY_ALLOW_PRIVATE_IPS" "dify_allowed_private_networks" "dst"
|
||||
write_optional_private_allowlist "SSRF_PROXY_ALLOW_PRIVATE_DOMAINS" "dify_allowed_private_domains" "dstdomain"
|
||||
|
||||
{
|
||||
echo "# Generated by docker-entrypoint.sh."
|
||||
echo "# Enables the middleware-only sandbox host bridge when configured."
|
||||
} > "$SANDBOX_PROXY_CONF"
|
||||
|
||||
if [ -n "${SSRF_SANDBOX_PROXY_PORT:-}" ]; then
|
||||
sandbox_proxy_host="${SSRF_SANDBOX_PROXY_HOST:-sandbox}"
|
||||
sandbox_proxy_target_port="${SANDBOX_PORT:-8194}"
|
||||
|
||||
{
|
||||
printf 'http_port %s accel vhost\n' "$SSRF_SANDBOX_PROXY_PORT"
|
||||
printf 'cache_peer %s parent %s 0 no-query originserver name=dify_sandbox\n' \
|
||||
"$sandbox_proxy_host" \
|
||||
"$sandbox_proxy_target_port"
|
||||
printf 'acl dify_sandbox_proxy_port localport %s\n' "$SSRF_SANDBOX_PROXY_PORT"
|
||||
printf 'http_access allow dify_sandbox_proxy_port\n'
|
||||
} >> "$SANDBOX_PROXY_CONF"
|
||||
fi
|
||||
|
||||
# Replace environment variables in the template and output to the squid.conf
|
||||
echo "[ENTRYPOINT] replacing environment variables in the template"
|
||||
awk '{
|
||||
while(match($0, /\${[A-Za-z_][A-Za-z_0-9]*}/)) {
|
||||
var = substr($0, RSTART+2, RLENGTH-3)
|
||||
val = ENVIRON[var]
|
||||
$0 = substr($0, 1, RSTART-1) val substr($0, RSTART+RLENGTH)
|
||||
}
|
||||
print
|
||||
}' /etc/squid/squid.conf.template > /etc/squid/squid.conf
|
||||
|
||||
/usr/sbin/squid -Nz
|
||||
echo "[ENTRYPOINT] starting squid"
|
||||
/usr/sbin/squid -f /etc/squid/squid.conf -NYC 1
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
acl client_localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
|
||||
acl client_localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
|
||||
acl client_localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
|
||||
acl client_localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
|
||||
acl client_localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN)
|
||||
acl client_localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
|
||||
acl client_localnet src fc00::/7 # RFC 4193 local private network range
|
||||
acl client_localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
|
||||
acl to_private_networks dst 0.0.0.0/8
|
||||
acl to_private_networks dst 10.0.0.0/8
|
||||
acl to_private_networks dst 100.64.0.0/10
|
||||
acl to_private_networks dst 127.0.0.0/8
|
||||
acl to_private_networks dst 169.254.0.0/16
|
||||
acl to_private_networks dst 172.16.0.0/12
|
||||
acl to_private_networks dst 192.168.0.0/16
|
||||
acl to_private_networks dst 224.0.0.0/4
|
||||
acl to_private_networks dst 240.0.0.0/4
|
||||
acl to_private_networks dst ::/128
|
||||
acl to_private_networks dst ::1/128
|
||||
acl to_private_networks dst ::ffff:0:0/96 # IPv4-mapped
|
||||
acl to_private_networks dst ::/96 # deprecated IPv4-compatible
|
||||
acl to_private_networks dst fc00::/7
|
||||
acl to_private_networks dst fe80::/10
|
||||
acl SSL_ports port 443
|
||||
# acl SSL_ports port 1025-65535 # Enable the configuration to resolve this issue: https://github.com/langgenius/dify/issues/12792
|
||||
acl Safe_ports port 80 # http
|
||||
acl Safe_ports port 21 # ftp
|
||||
acl Safe_ports port 443 # https
|
||||
acl Safe_ports port 70 # gopher
|
||||
acl Safe_ports port 210 # wais
|
||||
acl Safe_ports port 1025-65535 # unregistered ports
|
||||
acl Safe_ports port 280 # http-mgmt
|
||||
acl Safe_ports port 488 # gss-http
|
||||
acl Safe_ports port 591 # filemaker
|
||||
acl Safe_ports port 777 # multiling http
|
||||
acl CONNECT method CONNECT
|
||||
acl allowed_domains dstdomain .marketplace.dify.ai
|
||||
|
||||
http_port ${HTTP_PORT}
|
||||
|
||||
http_access deny !Safe_ports
|
||||
http_access deny CONNECT !SSL_ports
|
||||
http_access allow localhost manager
|
||||
http_access deny manager
|
||||
include /etc/squid/dify_sandbox_proxy.conf
|
||||
include /etc/squid/dify_allow_private.conf
|
||||
http_access deny to_private_networks
|
||||
http_access allow allowed_domains
|
||||
http_access allow client_localnet
|
||||
http_access allow localhost
|
||||
http_access deny all
|
||||
tcp_outgoing_address 0.0.0.0
|
||||
|
||||
################################## Proxy Server ################################
|
||||
coredump_dir ${COREDUMP_DIR}
|
||||
refresh_pattern ^ftp: 1440 20% 10080
|
||||
refresh_pattern ^gopher: 1440 0% 1440
|
||||
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
|
||||
refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
|
||||
refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
|
||||
refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
|
||||
refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
|
||||
refresh_pattern . 0 20% 4320
|
||||
|
||||
|
||||
# cache_dir ufs /var/spool/squid 100 16 256
|
||||
# upstream proxy, set to your own upstream proxy IP to avoid SSRF attacks
|
||||
# cache_peer 172.1.1.1 parent 3128 0 no-query no-digest no-netdb-exchange default
|
||||
|
||||
################################## Request Buffer ################################
|
||||
|
||||
# Unless the option's size is increased, an error will occur when uploading more than two files.
|
||||
client_request_buffer_max_size 100 MB
|
||||
|
||||
################################## Performance & Concurrency ###############################
|
||||
# Increase file descriptor limit for high concurrency
|
||||
max_filedescriptors 65536
|
||||
|
||||
# Timeout configurations for image requests
|
||||
connect_timeout 30 seconds
|
||||
request_timeout 2 minutes
|
||||
read_timeout 2 minutes
|
||||
client_lifetime 5 minutes
|
||||
shutdown_lifetime 30 seconds
|
||||
|
||||
# Persistent connections - improve performance for multiple requests
|
||||
server_persistent_connections on
|
||||
client_persistent_connections on
|
||||
persistent_request_timeout 30 seconds
|
||||
pconn_timeout 1 minute
|
||||
|
||||
# Connection pool and concurrency limits
|
||||
client_db on
|
||||
server_idle_pconn_timeout 2 minutes
|
||||
client_idle_pconn_timeout 2 minutes
|
||||
|
||||
# Quick abort settings - don't abort requests that are mostly done
|
||||
quick_abort_min 16 KB
|
||||
quick_abort_max 16 MB
|
||||
quick_abort_pct 95
|
||||
|
||||
# Memory and cache optimization
|
||||
memory_cache_mode disk
|
||||
cache_mem 256 MB
|
||||
maximum_object_size_in_memory 512 KB
|
||||
|
||||
# DNS resolver settings for better performance
|
||||
dns_timeout 30 seconds
|
||||
dns_retransmit_interval 5 seconds
|
||||
# By default, Squid uses the system's configured DNS resolvers.
|
||||
# If you need to override them, set dns_nameservers to appropriate servers
|
||||
# for your environment (for example, internal/corporate DNS). The following
|
||||
# is an example using public DNS and SHOULD be customized before use:
|
||||
# dns_nameservers 8.8.8.8 8.8.4.4
|
||||
|
||||
# Logging format for better debugging
|
||||
logformat dify_log %ts.%03tu %6tr %>a %Ss/%03>Hs %<st %rm %ru %[un %Sh/%<a %mt
|
||||
access_log daemon:/var/log/squid/access.log dify_log
|
||||
|
||||
# Access log to track concurrent requests and timeouts
|
||||
logfile_rotate 10
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user