forked from wangbo/easyai
fix(deploy): 跨域来源未配置时默认放行
This commit is contained in:
+3
-3
@@ -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。
|
||||
|
||||
@@ -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 配置正确"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 升级旧部署时从公开 API 地址补齐浏览器来源,并校验显式配置。
|
||||
# 浏览器来源为可选项:缺失或留空时保持宽松策略,显式配置时校验。
|
||||
. (Join-Path $PSScriptRoot "Initialize-PublicApiBaseUrl.ps1")
|
||||
|
||||
function Initialize-SecurityOrigin {
|
||||
@@ -7,25 +7,31 @@ 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
|
||||
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] Browser origin: $derived" -ForegroundColor Green
|
||||
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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 升级旧部署时补齐浏览器来源;显式配置始终保留并校验。
|
||||
# 浏览器来源为可选项:缺失或留空时保持宽松策略,显式配置时校验。
|
||||
# 不 source .env,避免执行环境文件中的内容。
|
||||
|
||||
security_origin_script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -23,26 +23,27 @@ init_security_origin() {
|
||||
return 1
|
||||
}
|
||||
|
||||
current="$(public_url_read_env_value "$file" "CONFIG_SECURITY_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")"
|
||||
public_url_validate "$api_url" || {
|
||||
echo "❌ 请先配置有效的 CONFIG_PUBLIC_API_BASE_URL" >&2
|
||||
return 1
|
||||
}
|
||||
if public_url_validate "$api_url"; then
|
||||
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 [ "$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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user