From 89cc554c04492e1296fe498bc39fb9166ac6f043 Mon Sep 17 00:00:00 2001 From: wangbo Date: Sun, 27 Sep 2026 18:00:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20=E8=B7=A8=E5=9F=9F=E6=9D=A5?= =?UTF-8?q?=E6=BA=90=E6=9C=AA=E9=85=8D=E7=BD=AE=E6=97=B6=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=94=BE=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.sample | 6 ++--- .github/workflows/test-start-ps1.yml | 2 ++ README.md | 2 ++ scripts/Initialize-SecurityOrigin.ps1 | 38 ++++++++++++++++----------- scripts/Test-SecurityOrigin.ps1 | 6 +++-- scripts/init-security-origin.sh | 37 +++++++++++++------------- scripts/test-deployment-public-url.sh | 2 +- scripts/test-security-origin.sh | 6 +++-- start.ps1 | 4 +-- start.sh | 6 ++--- update.ps1 | 1 + update.sh | 2 +- 12 files changed, 62 insertions(+), 50 deletions(-) diff --git a/.env.sample b/.env.sample index e654483..e9e9017 100644 --- a/.env.sample +++ b/.env.sample @@ -137,10 +137,10 @@ CONFIG_OTP_HASH_SECRET= CONFIG_AUDIT_HASH_PEPPER= CONFIG_AUDIT_INTEGRITY_KEY= -# 允许访问后端的浏览器 Origin,多个值用英文逗号分隔,生产环境禁止使用 *。 -# 启动/升级脚本会从已校验的公开 API 地址补齐缺失值或下方样例值;自定义值会保留并校验。 +# 可选:限制允许访问后端的浏览器 Origin,多个值用英文逗号分隔,生产环境禁止使用 *。 +# 留空时后端默认允许所有来源;启动/升级脚本不会自动补齐,显式配置时才会校验。 # 域名部署示例:https://yourwebsite.com,https://www.yourwebsite.com -CONFIG_SECURITY_ORIGIN=http://127.0.0.1,http://localhost +CONFIG_SECURITY_ORIGIN= # 可信反向代理 IP/CIDR,多个值用英文逗号分隔;未使用反向代理时保持为空。 # 仅填写实际 Nginx/负载均衡地址,不要使用 0.0.0.0/0。 diff --git a/.github/workflows/test-start-ps1.yml b/.github/workflows/test-start-ps1.yml index 7ced14f..4a76904 100644 --- a/.github/workflows/test-start-ps1.yml +++ b/.github/workflows/test-start-ps1.yml @@ -62,11 +62,13 @@ jobs: $socket = Select-String -Path .env -Pattern '^NUXT_PUBLIC_BASE_SOCKETURL=' | ForEach-Object { $_.Line } $sg = Select-String -Path .env -Pattern '^NUXT_PUBLIC_SG_APIURL=' | ForEach-Object { $_.Line } $publicApi = Select-String -Path .env -Pattern '^CONFIG_PUBLIC_API_BASE_URL=' | ForEach-Object { $_.Line } + $securityOrigin = Select-String -Path .env -Pattern '^CONFIG_SECURITY_ORIGIN=' | ForEach-Object { $_.Line } $serverBindIp = Select-String -Path .env -Pattern '^SERVER_HTTP_BIND_IP=' | ForEach-Object { $_.Line } if ($api -ne 'NUXT_PUBLIC_BASE_APIURL=http://192.168.1.100:4100') { exit 1 } if ($socket -ne 'NUXT_PUBLIC_BASE_SOCKETURL=ws://192.168.1.100:3002') { exit 1 } if ($sg -ne 'NUXT_PUBLIC_SG_APIURL=http://192.168.1.100:3003') { exit 1 } if ($publicApi -ne 'CONFIG_PUBLIC_API_BASE_URL=http://192.168.1.100:4100') { exit 1 } + if ($securityOrigin -ne 'CONFIG_SECURITY_ORIGIN=') { exit 1 } if ($serverBindIp -ne 'SERVER_HTTP_BIND_IP=0.0.0.0') { exit 1 } Write-Host "OK: .env 配置正确" diff --git a/README.md b/README.md index 96bb11d..f55787e 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ Linux IP 模式会自动安装并配置 Nginx,浏览器统一访问 `http://IP 启动和升级脚本会在拉取镜像、重建服务之前校验并持久化 `CONFIG_PUBLIC_API_BASE_URL`:新 IP 部署使用 `http://IP/api`;域名部署中 `DEPLOY_HTTPS=false` 使用 `http/ws`,`DEPLOY_HTTPS=true` 使用 `https/wss`。IP 与域名模式的浏览器接口路径统一为 `/api`、`/socket.io`、`/asg-api`,只由 Nginx 监听地址和公开协议决定 origin。旧环境缺少该配置时,会依次从绝对 `NUXT_PUBLIC_BASE_APIURL`、WebSocket origin、首个 `CONFIG_SECURITY_ORIGIN` 推导。已有合法值保持不变,非法或无法推导时脚本会终止并提示修复。 +`CONFIG_SECURITY_ORIGIN` 是可选的浏览器来源白名单:留空或不配置时后端默认允许所有来源,启动和升级脚本不会自动生成;显式配置一个或多个 http(s) Origin 时,脚本会校验格式并由后端按白名单限制访问。 + 已有外部 TLS、手工代理、自定义公开端口或特殊路径时,可在执行脚本前设置高级覆盖: ```bash diff --git a/scripts/Initialize-SecurityOrigin.ps1 b/scripts/Initialize-SecurityOrigin.ps1 index f1ab7a6..66f1af1 100644 --- a/scripts/Initialize-SecurityOrigin.ps1 +++ b/scripts/Initialize-SecurityOrigin.ps1 @@ -1,4 +1,4 @@ -# 升级旧部署时从公开 API 地址补齐浏览器来源,并校验显式配置。 +# 浏览器来源为可选项:缺失或留空时保持宽松策略,显式配置时校验。 . (Join-Path $PSScriptRoot "Initialize-PublicApiBaseUrl.ps1") function Initialize-SecurityOrigin { @@ -7,26 +7,32 @@ function Initialize-SecurityOrigin { $content = Get-Content $Path -Raw -Encoding UTF8 if ($null -eq $content) { $content = "" } - $apiUrl = Get-PublicEnvValue $content "CONFIG_PUBLIC_API_BASE_URL" - if (-not (ConvertTo-PublicApiBaseUrl $apiUrl)) { - throw "Configure a valid CONFIG_PUBLIC_API_BASE_URL first" - } - $apiUri = [Uri]$apiUrl - $derived = $apiUri.GetLeftPart([UriPartial]::Authority) $current = Get-PublicEnvValue $content "CONFIG_SECURITY_ORIGIN" - if (-not $current -or - ($current -eq "http://127.0.0.1,http://localhost" -and $derived -ne "http://127.0.0.1")) { - $content = Set-PublicEnvValue $content "CONFIG_SECURITY_ORIGIN" $derived - [System.IO.File]::WriteAllText( - (Resolve-Path $Path), - $content, - [System.Text.UTF8Encoding]::new($false) - ) - Write-Host " [OK] Browser origin: $derived" -ForegroundColor Green + if (-not $current) { + Write-Host " [OK] Browser origin is not configured; backend allows all origins" -ForegroundColor Green return } + # The legacy sample value was not a user-selected allowlist. Clear it on non-local upgrades. + if ($current -eq "http://127.0.0.1,http://localhost") { + $apiUrl = Get-PublicEnvValue $content "CONFIG_PUBLIC_API_BASE_URL" + $normalizedApiUrl = ConvertTo-PublicApiBaseUrl $apiUrl + if ($normalizedApiUrl) { + $derived = ([Uri]$normalizedApiUrl).GetLeftPart([UriPartial]::Authority) + if ($derived -notin @('http://127.0.0.1', 'http://localhost')) { + $content = Set-PublicEnvValue $content "CONFIG_SECURITY_ORIGIN" "" + [System.IO.File]::WriteAllText( + (Resolve-Path $Path), + $content, + [System.Text.UTF8Encoding]::new($false) + ) + Write-Host " [OK] Cleared legacy browser-origin sample; backend allows all origins" -ForegroundColor Green + return + } + } + } + if ($current.StartsWith(',') -or $current.EndsWith(',') -or $current.Contains(',,')) { throw "CONFIG_SECURITY_ORIGIN contains an empty origin" } diff --git a/scripts/Test-SecurityOrigin.ps1 b/scripts/Test-SecurityOrigin.ps1 index ea9e997..751fe7f 100644 --- a/scripts/Test-SecurityOrigin.ps1 +++ b/scripts/Test-SecurityOrigin.ps1 @@ -6,16 +6,18 @@ New-Item -ItemType Directory -Path $tempDir | Out-Null try { $path = Join-Path $tempDir 'test.env' [System.IO.File]::WriteAllText($path, "CONFIG_PUBLIC_API_BASE_URL=https://zaowua.com/api`n") + $before = Get-Content $path -Raw Initialize-SecurityOrigin -Path $path $content = Get-Content $path -Raw - if ((Get-PublicEnvValue $content 'CONFIG_SECURITY_ORIGIN') -ne 'https://zaowua.com') { throw 'Missing origin was not initialized' } + if ((Get-PublicEnvValue $content 'CONFIG_SECURITY_ORIGIN')) { throw 'Missing origin was unexpectedly initialized' } + if ($content -ne $before) { throw 'Missing origin changed the environment' } $before = $content Initialize-SecurityOrigin -Path $path if ((Get-Content $path -Raw) -ne $before) { throw 'Repeated initialization changed the environment' } [System.IO.File]::WriteAllText($path, "CONFIG_PUBLIC_API_BASE_URL=https://zaowua.com/api`nCONFIG_SECURITY_ORIGIN=http://127.0.0.1,http://localhost`n") Initialize-SecurityOrigin -Path $path - if ((Get-PublicEnvValue (Get-Content $path -Raw) 'CONFIG_SECURITY_ORIGIN') -ne 'https://zaowua.com') { throw 'Sample origin was not migrated' } + if ((Get-PublicEnvValue (Get-Content $path -Raw) 'CONFIG_SECURITY_ORIGIN')) { throw 'Sample origin was not cleared' } [System.IO.File]::WriteAllText($path, "CONFIG_PUBLIC_API_BASE_URL=https://zaowua.com/api`nCONFIG_SECURITY_ORIGIN=https://zaowua.com,https://www.zaowua.com`n") $before = Get-Content $path -Raw diff --git a/scripts/init-security-origin.sh b/scripts/init-security-origin.sh index 773d09d..f6bfba7 100755 --- a/scripts/init-security-origin.sh +++ b/scripts/init-security-origin.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# 升级旧部署时补齐浏览器来源;显式配置始终保留并校验。 +# 浏览器来源为可选项:缺失或留空时保持宽松策略,显式配置时校验。 # 不 source .env,避免执行环境文件中的内容。 security_origin_script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -23,27 +23,28 @@ init_security_origin() { return 1 } - api_url="$(public_url_read_env_value "$file" "CONFIG_PUBLIC_API_BASE_URL")" - public_url_validate "$api_url" || { - echo "❌ 请先配置有效的 CONFIG_PUBLIC_API_BASE_URL" >&2 - return 1 - } - scheme="${api_url%%://*}" - rest="${api_url#*://}" - authority="${rest%%/*}" - origin="${scheme}://${authority}" - security_origin_valid "$origin" || return 1 - current="$(public_url_read_env_value "$file" "CONFIG_SECURITY_ORIGIN")" - if [ -z "$current" ] || { - [ "$current" = 'http://127.0.0.1,http://localhost' ] && - [ "$origin" != 'http://127.0.0.1' ]; - }; then - public_url_write_env_value "$file" "CONFIG_SECURITY_ORIGIN" "$origin" - echo " ✓ 已配置浏览器来源: $origin" + if [ -z "$current" ]; then + echo " ✓ 未配置浏览器来源,后端将默认允许所有来源" return 0 fi + # 旧版样例值不是用户选择的白名单;非本机部署升级时清空,恢复缺省策略。 + if [ "$current" = 'http://127.0.0.1,http://localhost' ]; then + api_url="$(public_url_read_env_value "$file" "CONFIG_PUBLIC_API_BASE_URL")" + if public_url_validate "$api_url"; then + scheme="${api_url%%://*}" + rest="${api_url#*://}" + authority="${rest%%/*}" + origin="${scheme}://${authority}" + if [ "$origin" != 'http://127.0.0.1' ] && [ "$origin" != 'http://localhost' ]; then + public_url_write_env_value "$file" "CONFIG_SECURITY_ORIGIN" "" + echo " ✓ 已清理历史浏览器来源样例值,后端将默认允许所有来源" + return 0 + fi + fi + fi + local -a origins case "$current" in ,*|*,|*,,*) diff --git a/scripts/test-deployment-public-url.sh b/scripts/test-deployment-public-url.sh index c699f62..02e0cff 100755 --- a/scripts/test-deployment-public-url.sh +++ b/scripts/test-deployment-public-url.sh @@ -246,7 +246,7 @@ 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 'CONFIG_SECURITY_ORIGIN=' .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 diff --git a/scripts/test-security-origin.sh b/scripts/test-security-origin.sh index d191bfb..17e3870 100755 --- a/scripts/test-security-origin.sh +++ b/scripts/test-security-origin.sh @@ -16,8 +16,10 @@ assert_value() { cat > "$tmp_dir/missing.env" <<'ENV' CONFIG_PUBLIC_API_BASE_URL=https://zaowua.com/api ENV +before="$(shasum -a 256 "$tmp_dir/missing.env" | cut -d ' ' -f 1)" init_security_origin "$tmp_dir/missing.env" > /dev/null -assert_value "$tmp_dir/missing.env" 'https://zaowua.com' +assert_value "$tmp_dir/missing.env" '' +[ "$before" = "$(shasum -a 256 "$tmp_dir/missing.env" | cut -d ' ' -f 1)" ] first_hash="$(shasum -a 256 "$tmp_dir/missing.env" | cut -d ' ' -f 1)" init_security_origin "$tmp_dir/missing.env" > /dev/null [ "$first_hash" = "$(shasum -a 256 "$tmp_dir/missing.env" | cut -d ' ' -f 1)" ] @@ -27,7 +29,7 @@ CONFIG_PUBLIC_API_BASE_URL=https://zaowua.com/api CONFIG_SECURITY_ORIGIN=http://127.0.0.1,http://localhost ENV init_security_origin "$tmp_dir/sample.env" > /dev/null -assert_value "$tmp_dir/sample.env" 'https://zaowua.com' +assert_value "$tmp_dir/sample.env" '' cat > "$tmp_dir/preserved.env" <<'ENV' CONFIG_PUBLIC_API_BASE_URL=https://zaowua.com/api diff --git a/start.ps1 b/start.ps1 index c35acf4..92eb5bc 100644 --- a/start.ps1 +++ b/start.ps1 @@ -177,9 +177,6 @@ function Setup-EnvFiles { $content = Upsert-Env $content "SERVER_HTTP_BIND_IP" $serverBindIp # Windows 当前仍使用 Docker Desktop 直连端口;显式覆盖 Linux Nginx 模式的回环默认值。 $content = Upsert-Env $content "EASYAI_PROXY_BIND_IP" $serverBindIp - $webPortMatch = [regex]::Match($content, '(?m)^WEB_PORT=(\d+)$') - $webPort = if ($webPortMatch.Success) { $webPortMatch.Groups[1].Value } else { "3010" } - $content = Upsert-Env $content "CONFIG_SECURITY_ORIGIN" "http://$($script:DeployIP):$webPort" $script:PublicApiBaseUrlCandidate = if ($env:DEPLOY_PUBLIC_API_BASE_URL) { $env:DEPLOY_PUBLIC_API_BASE_URL } else { @@ -439,6 +436,7 @@ function Main { -Path (Join-Path $script:Root ".env") ` -Mode $publicUrlMode ` -Override $script:PublicApiBaseUrlCandidate + # CONFIG_SECURITY_ORIGIN is optional. Blank means allow all; explicit values are validated. . (Join-Path $script:Root "scripts\Initialize-SecurityOrigin.ps1") Initialize-SecurityOrigin -Path (Join-Path $script:Root ".env") diff --git a/start.sh b/start.sh index c0658a6..0babdd5 100755 --- a/start.sh +++ b/start.sh @@ -250,7 +250,7 @@ setup_env_files() { SECURITY_ENV_MODE="new" fi - local web_port server_port ws_port asg_port public_scheme socket_scheme public_host security_origins + local web_port server_port ws_port asg_port public_scheme socket_scheme public_host web_port="$(awk -F= '$1 == "WEB_PORT" { print $2; exit }' .env | tr -d '[:space:]')" web_port="${web_port:-3010}" server_port="$(awk -F= '$1 == "SERVER_HTTP_PORT" { print $2; exit }' .env | tr -d '[:space:]')" @@ -264,10 +264,8 @@ setup_env_files() { socket_scheme="ws" if [ "$DEPLOY_MODE" = "ip" ]; then public_host="$DEPLOY_IP" - security_origins="http://${DEPLOY_IP}" else public_host="$DEPLOY_DOMAIN" - security_origins="https://${DEPLOY_DOMAIN},http://${DEPLOY_DOMAIN}" if [ "$DEPLOY_HTTPS" = true ]; then public_scheme="https" socket_scheme="wss" @@ -278,7 +276,6 @@ setup_env_files() { upsert_env_value .env NUXT_PUBLIC_BASE_APIURL "/api" upsert_env_value .env NUXT_PUBLIC_BASE_SOCKETURL "${socket_scheme}://${public_host}/socket.io" upsert_env_value .env NUXT_PUBLIC_SG_APIURL "/asg-api" - upsert_env_value .env CONFIG_SECURITY_ORIGIN "$security_origins" upsert_env_value .env EASYAI_PROXY_BIND_IP "127.0.0.1" upsert_env_value .env EASYAI_INFRA_BIND_IP "127.0.0.1" upsert_env_value .env GATEWAY_INBOUND_TCP_LISTEN_HOST "172.21.0.6" @@ -660,6 +657,7 @@ main() { else init_public_api_base_url .env configure "$PUBLIC_API_BASE_URL_CANDIDATE" fi + # CONFIG_SECURITY_ORIGIN 为可选项:留空时后端默认允许所有来源,显式配置时才校验。 # shellcheck source=scripts/init-security-origin.sh . ./scripts/init-security-origin.sh init_security_origin .env diff --git a/update.ps1 b/update.ps1 index 4914c2e..ffd7903 100644 --- a/update.ps1 +++ b/update.ps1 @@ -207,6 +207,7 @@ Initialize-PublicApiBaseUrl ` -Path (Join-Path $scriptDir ".env") ` -Mode "upgrade" ` -Override $env:DEPLOY_PUBLIC_API_BASE_URL +# Browser origin is optional. Blank means allow all; explicit values are validated. . (Join-Path $scriptDir "scripts\Initialize-SecurityOrigin.ps1") Initialize-SecurityOrigin -Path (Join-Path $scriptDir ".env") diff --git a/update.sh b/update.sh index 1abb7f1..e8d10fa 100755 --- a/update.sh +++ b/update.sh @@ -158,7 +158,7 @@ init_security_env .env upgrade . ./scripts/init-public-api-base-url.sh init_public_api_base_url .env upgrade "${DEPLOY_PUBLIC_API_BASE_URL:-}" -# 旧部署可能缺少浏览器 Origin;在重建主服务前从已校验的公开 API 地址补齐。 +# 浏览器 Origin 为可选项;留空时后端默认允许所有来源,显式配置时才校验。 # shellcheck source=scripts/init-security-origin.sh . ./scripts/init-security-origin.sh init_security_origin .env