fix(security): 收紧默认部署暴露面
This commit is contained in:
@@ -32,6 +32,7 @@ reset_case() {
|
||||
.env.ASG \
|
||||
.env.AMS \
|
||||
easyai-proxy.conf \
|
||||
10.0.0.8.conf \
|
||||
demo.example.com.conf
|
||||
}
|
||||
|
||||
@@ -63,18 +64,37 @@ assert_sandbox_public_access_disabled() {
|
||||
fi
|
||||
}
|
||||
|
||||
assert_high_risk_public_access_disabled() {
|
||||
local config_file="$1"
|
||||
grep -q '^ location = /api/integration/platform-api/execute {' "$config_file"
|
||||
grep -q '^ location = /api/auth/sign/token {' "$config_file"
|
||||
grep -q '^ location = /api/auth/verify/token {' "$config_file"
|
||||
grep -q '^ location = /logs-web {' "$config_file"
|
||||
grep -q '^ location \^~ /logs-web/ {' "$config_file"
|
||||
if grep -Eq 'proxy_pass .*(:8080|platform-api/execute|auth/(sign|verify)/token)' "$config_file"; then
|
||||
echo "Unexpected high-risk public proxy in $config_file" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_compose_exposure() {
|
||||
local expected_server_host_ip="$1"
|
||||
local expected_proxy_host_ip="$2"
|
||||
docker compose config --format json | python3 -c '
|
||||
import json, sys
|
||||
expected = sys.argv[1]
|
||||
expected_server = sys.argv[1]
|
||||
expected_proxy = sys.argv[2]
|
||||
config = json.load(sys.stdin)
|
||||
sandbox_ports = config["services"]["sandbox"].get("ports") or []
|
||||
assert sandbox_ports == [], f"sandbox ports published: {sandbox_ports}"
|
||||
server_ports = config["services"]["easyai-server"].get("ports") or []
|
||||
assert len(server_ports) == 1, server_ports
|
||||
assert server_ports[0].get("host_ip") == expected, server_ports
|
||||
' "$expected_server_host_ip"
|
||||
assert server_ports[0].get("host_ip") == expected_server, server_ports
|
||||
for service in ("easyai-web", "ws-gateway", "easyai-asg"):
|
||||
ports = config["services"][service].get("ports") or []
|
||||
assert len(ports) == 1, (service, ports)
|
||||
assert ports[0].get("host_ip") == expected_proxy, (service, ports)
|
||||
' "$expected_server_host_ip" "$expected_proxy_host_ip"
|
||||
}
|
||||
|
||||
assert_canvas_ws_auth_config() {
|
||||
@@ -92,6 +112,29 @@ assert "ws_ticket" in methods, f"ws-gateway does not advertise ws_ticket: {sorte
|
||||
'
|
||||
}
|
||||
|
||||
assert_compose_security() {
|
||||
docker compose config --format json | python3 -c '
|
||||
import json, sys
|
||||
config = json.load(sys.stdin)
|
||||
services = config["services"]
|
||||
|
||||
assert set(services["sandbox"]["networks"]) == {"sandbox-network"}, services["sandbox"]["networks"]
|
||||
assert {"easyai", "sandbox-network"}.issubset(services["easyai-server"]["networks"]), services["easyai-server"]["networks"]
|
||||
assert services["easyai-server"]["environment"]["GATEWAY_INBOUND_TCP_LISTEN_HOST"] == "172.21.0.6", services["easyai-server"]["environment"]["GATEWAY_INBOUND_TCP_LISTEN_HOST"]
|
||||
assert services["mongo"].get("privileged") is not True, services["mongo"].get("privileged")
|
||||
|
||||
for service in ("mongo", "rabbitmq", "video-edit", "dozzle", "agent-memory"):
|
||||
for port in services[service].get("ports") or []:
|
||||
assert port.get("host_ip") == "127.0.0.1", (service, port)
|
||||
|
||||
watchtower = services["watchtower"]
|
||||
command = " ".join(watchtower.get("command") or [])
|
||||
assert "http-api-update" not in command, command
|
||||
assert "WATCHTOWER_HTTP_API_TOKEN" not in (watchtower.get("environment") or {}), watchtower.get("environment")
|
||||
assert services["dozzle"]["environment"]["DOZZLE_ENABLE_DOWNLOAD"] == "false", services["dozzle"]["environment"]
|
||||
'
|
||||
}
|
||||
|
||||
sed -i.bak 's/^SERVER_HTTP_PORT=.*/SERVER_HTTP_PORT=4100/' .env.sample
|
||||
rm -f .env.sample.bak
|
||||
DEPLOY_NON_INTERACTIVE=1 \
|
||||
@@ -99,9 +142,29 @@ DEPLOY_NON_INTERACTIVE=1 \
|
||||
DEPLOY_ACCESS=ip \
|
||||
DEPLOY_IP=10.0.0.8 \
|
||||
bash start.sh > "$TMP_DIR/first-install.log"
|
||||
grep -qx 'NUXT_PUBLIC_BASE_APIURL=http://10.0.0.8:4100' .env
|
||||
grep -qx 'CONFIG_PUBLIC_API_BASE_URL=http://10.0.0.8:4100' .env
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=0.0.0.0' .env
|
||||
grep -qx 'NUXT_PUBLIC_BASE_APIURL=/api' .env
|
||||
grep -qx 'NUXT_PUBLIC_BASE_SOCKETURL=ws://10.0.0.8/socket.io' .env
|
||||
grep -qx 'NUXT_PUBLIC_SG_APIURL=/asg-api' .env
|
||||
grep -qx 'CONFIG_PUBLIC_API_BASE_URL=http://10.0.0.8/api' .env
|
||||
grep -qx 'CONFIG_SECURITY_ORIGIN=http://10.0.0.8' .env
|
||||
grep -qx 'EASYAI_PROXY_BIND_IP=127.0.0.1' .env
|
||||
grep -qx 'EASYAI_INFRA_BIND_IP=127.0.0.1' .env
|
||||
grep -qx 'GATEWAY_INBOUND_TCP_LISTEN_HOST=172.21.0.6' .env
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=127.0.0.1' .env
|
||||
grep -q 'server_name 10.0.0.8;' 10.0.0.8.conf
|
||||
if grep -q 'www.10.0.0.8' 10.0.0.8.conf; then
|
||||
echo "IP proxy config must not contain the domain-only www redirect" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'proxy_pass http://127.0.0.1:3010/;' 10.0.0.8.conf
|
||||
grep -q 'proxy_pass http://127.0.0.1:4100/;' 10.0.0.8.conf
|
||||
grep -q 'proxy_pass http://127.0.0.1:3002;' 10.0.0.8.conf
|
||||
grep -q 'proxy_pass http://127.0.0.1:3003/;' 10.0.0.8.conf
|
||||
grep -q 'location /socket.io {' 10.0.0.8.conf
|
||||
assert_compression_config 10.0.0.8.conf
|
||||
assert_sandbox_public_access_disabled 10.0.0.8.conf
|
||||
assert_high_risk_public_access_disabled 10.0.0.8.conf
|
||||
grep -Fq '访问地址: http://10.0.0.8' "$TMP_DIR/first-install.log"
|
||||
initial_admin_password="$(awk -F= '$1 == "CONFIG_INITIAL_ADMIN_PASSWORD" { print $2; exit }' .env)"
|
||||
[ "${#initial_admin_password}" -ge 12 ] || {
|
||||
echo "Initial admin password was not generated" >&2
|
||||
@@ -122,8 +185,9 @@ if grep -Fq "登录密码: ${initial_admin_password}" "$TMP_DIR/existing-env.log
|
||||
exit 1
|
||||
fi
|
||||
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
||||
assert_compose_exposure 0.0.0.0
|
||||
assert_compose_exposure 127.0.0.1 127.0.0.1
|
||||
assert_canvas_ws_auth_config
|
||||
assert_compose_security
|
||||
fi
|
||||
|
||||
reset_case
|
||||
@@ -133,7 +197,9 @@ DEPLOY_NON_INTERACTIVE=1 \
|
||||
DEPLOY_DOMAIN=demo.example.com \
|
||||
DEPLOY_HTTPS=false \
|
||||
bash start.sh >/dev/null
|
||||
grep -qx 'NUXT_PUBLIC_BASE_APIURL=/api' .env
|
||||
grep -qx 'NUXT_PUBLIC_BASE_SOCKETURL=ws://demo.example.com/socket.io' .env
|
||||
grep -qx 'NUXT_PUBLIC_SG_APIURL=/asg-api' .env
|
||||
grep -qx 'CONFIG_PUBLIC_API_BASE_URL=http://demo.example.com/api' .env
|
||||
grep -q 'proxy_set_header X-Forwarded-Host $easyai_forwarded_host;' demo.example.com.conf
|
||||
grep -q 'proxy_set_header X-Forwarded-Port $server_port;' demo.example.com.conf
|
||||
@@ -141,9 +207,14 @@ grep -q "proxy_set_header X-Original-Prefix '/api';" demo.example.com.conf
|
||||
grep -q 'location = /api {' demo.example.com.conf
|
||||
assert_compression_config demo.example.com.conf
|
||||
assert_sandbox_public_access_disabled demo.example.com.conf
|
||||
assert_high_risk_public_access_disabled demo.example.com.conf
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=127.0.0.1' .env
|
||||
grep -qx 'EASYAI_PROXY_BIND_IP=127.0.0.1' .env
|
||||
grep -qx 'EASYAI_INFRA_BIND_IP=127.0.0.1' .env
|
||||
grep -qx 'GATEWAY_INBOUND_TCP_LISTEN_HOST=172.21.0.6' .env
|
||||
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
||||
assert_compose_exposure 127.0.0.1
|
||||
assert_compose_exposure 127.0.0.1 127.0.0.1
|
||||
assert_compose_security
|
||||
fi
|
||||
|
||||
# 直接验证 https.sh 的缺省配置生成函数会采用完整模板,而不是只代理 3010。
|
||||
@@ -161,6 +232,7 @@ grep -q 'location /socket.io {' easyai-proxy.conf
|
||||
grep -q 'proxy_set_header X-Forwarded-Host $easyai_forwarded_host;' easyai-proxy.conf
|
||||
assert_compression_config easyai-proxy.conf
|
||||
assert_sandbox_public_access_disabled easyai-proxy.conf
|
||||
assert_high_risk_public_access_disabled easyai-proxy.conf
|
||||
|
||||
reset_case
|
||||
DEPLOY_NON_INTERACTIVE=1 \
|
||||
@@ -169,7 +241,9 @@ DEPLOY_NON_INTERACTIVE=1 \
|
||||
DEPLOY_DOMAIN=demo.example.com \
|
||||
DEPLOY_HTTPS=true \
|
||||
bash start.sh >/dev/null
|
||||
grep -qx 'NUXT_PUBLIC_BASE_APIURL=/api' .env
|
||||
grep -qx 'NUXT_PUBLIC_BASE_SOCKETURL=wss://demo.example.com/socket.io' .env
|
||||
grep -qx 'NUXT_PUBLIC_SG_APIURL=/asg-api' .env
|
||||
grep -qx 'CONFIG_PUBLIC_API_BASE_URL=https://demo.example.com/api' .env
|
||||
|
||||
reset_case
|
||||
|
||||
Reference in New Issue
Block a user