fix(deploy): 默认关闭沙箱公网访问
This commit is contained in:
+5
-5
@@ -30,6 +30,9 @@ WEB_PORT=3010
|
||||
SERVER_HTTP_PORT=3001
|
||||
# easyai-server 后端 HTTP 端口
|
||||
|
||||
SERVER_HTTP_BIND_IP=127.0.0.1
|
||||
# easyai-server 宿主机绑定地址。域名/本机部署保持 127.0.0.1;仅 IP/LAN 直连模式使用 0.0.0.0
|
||||
|
||||
NODE_MAX_OLD_SPACE_SIZE=8192
|
||||
# easyai-server 的 Node.js V8 old-space 堆上限,单位 MiB
|
||||
|
||||
@@ -66,14 +69,11 @@ MEMORY_TCP_PORT=4004
|
||||
# 记忆整理相关参数在 .env.AMS 中配置(MEMORY_MAINTENANCE_*)
|
||||
|
||||
# ========== 4. 沙箱环境 ==========
|
||||
SANDBOX_PORT=8081
|
||||
# 不建议对外暴露
|
||||
|
||||
SANDBOX_JUPYTERLAB_PORT=8888
|
||||
SANDBOX_JUPYTER_TOKEN=easyaiisbest
|
||||
# 建议设置 token
|
||||
# Sandbox API 与 JupyterLab 默认不映射宿主机端口;token 仅用于容器内 Jupyter 认证
|
||||
|
||||
SANDBOX_SERVICE_BASE_URL=
|
||||
# 留空时 easyai-server 通过 Docker 内网 http://sandbox:8000 访问
|
||||
|
||||
# ========== 5. Redis ==========
|
||||
REDIS_PORT=
|
||||
|
||||
@@ -10,6 +10,8 @@ on:
|
||||
- '.env.sample'
|
||||
- 'scripts/init-public-api-base-url.sh'
|
||||
- 'scripts/test-public-api-base-url.sh'
|
||||
- 'scripts/init-server-http-bind-ip.sh'
|
||||
- 'scripts/test-server-http-bind-ip.sh'
|
||||
- 'scripts/test-deployment-public-url.sh'
|
||||
- '.github/workflows/test-deployment-public-url.yml'
|
||||
pull_request:
|
||||
@@ -21,6 +23,8 @@ on:
|
||||
- '.env.sample'
|
||||
- 'scripts/init-public-api-base-url.sh'
|
||||
- 'scripts/test-public-api-base-url.sh'
|
||||
- 'scripts/init-server-http-bind-ip.sh'
|
||||
- 'scripts/test-server-http-bind-ip.sh'
|
||||
- 'scripts/test-deployment-public-url.sh'
|
||||
- '.github/workflows/test-deployment-public-url.yml'
|
||||
workflow_dispatch:
|
||||
@@ -33,10 +37,13 @@ jobs:
|
||||
|
||||
- name: Check shell syntax
|
||||
run: |
|
||||
bash -n start.sh update.sh scripts/init-public-api-base-url.sh scripts/test-public-api-base-url.sh scripts/test-deployment-public-url.sh
|
||||
bash -n start.sh update.sh scripts/init-public-api-base-url.sh scripts/init-server-http-bind-ip.sh scripts/test-public-api-base-url.sh scripts/test-server-http-bind-ip.sh scripts/test-deployment-public-url.sh
|
||||
|
||||
- name: Test migration helper
|
||||
run: bash scripts/test-public-api-base-url.sh
|
||||
|
||||
- name: Test server HTTP bind migration
|
||||
run: bash scripts/test-server-http-bind-ip.sh
|
||||
|
||||
- name: Test deployment dry-run matrix
|
||||
run: bash scripts/test-deployment-public-url.sh
|
||||
|
||||
@@ -8,6 +8,8 @@ on:
|
||||
- 'update.ps1'
|
||||
- 'scripts/Initialize-PublicApiBaseUrl.ps1'
|
||||
- 'scripts/Test-PublicApiBaseUrl.ps1'
|
||||
- 'scripts/Initialize-ServerHttpBindIp.ps1'
|
||||
- 'scripts/Test-ServerHttpBindIp.ps1'
|
||||
- '.env.sample'
|
||||
- '.github/workflows/test-start-ps1.yml'
|
||||
pull_request:
|
||||
@@ -16,6 +18,8 @@ on:
|
||||
- 'update.ps1'
|
||||
- 'scripts/Initialize-PublicApiBaseUrl.ps1'
|
||||
- 'scripts/Test-PublicApiBaseUrl.ps1'
|
||||
- 'scripts/Initialize-ServerHttpBindIp.ps1'
|
||||
- 'scripts/Test-ServerHttpBindIp.ps1'
|
||||
- '.env.sample'
|
||||
- '.github/workflows/test-start-ps1.yml'
|
||||
workflow_dispatch:
|
||||
@@ -40,12 +44,18 @@ 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 }
|
||||
$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 ($serverBindIp -ne 'SERVER_HTTP_BIND_IP=0.0.0.0') { exit 1 }
|
||||
Write-Host "OK: .env 配置正确"
|
||||
|
||||
- name: Test public API URL migration helper
|
||||
run: |
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\Test-PublicApiBaseUrl.ps1
|
||||
|
||||
- name: Test server HTTP bind migration helper
|
||||
run: |
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\Test-ServerHttpBindIp.ps1
|
||||
|
||||
@@ -455,10 +455,11 @@ curl https://<你的域名>/asg-api/health
|
||||
1. 增加脚本沙箱环境容器,需要更新`docker-compose.yml`,用以支持SKILL中的脚本运行
|
||||
2. 增加环境变量配置
|
||||
```dotenv
|
||||
SANDBOX_PORT=8081 #对外暴露沙箱环境的端口,不建议暴露,权限较高
|
||||
SANDBOX_SERVICE_BASE_URL= #脚本运行环境使用的服务地址,默认为通过内网直接访问http://sandbox:8000,不需要配置。当将sandbox部署在其他外部网络才需要配置
|
||||
SANDBOX_SERVICE_BASE_URL= #脚本运行环境使用的服务地址,默认通过 Docker 内网访问 http://sandbox:8000,不需要配置
|
||||
```
|
||||
|
||||
> 安全默认值:Sandbox API 与 JupyterLab 不映射宿主机端口,Nginx 模板对 `/api/sandbox`、`/sandbox`、`/jupyterlab` 返回 404。不要在公网重新开放这些高权限入口。
|
||||
|
||||
|
||||
### 2025.1.29
|
||||
|
||||
@@ -582,6 +583,7 @@ cd easyai
|
||||
```bash
|
||||
NUXT_PUBLIC_BASE_APIURL=http://<你的服务器公网IP或者局域网地址>:3001
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=ws://<你的服务器公网IP或者局域网地址>:3002
|
||||
SERVER_HTTP_BIND_IP=0.0.0.0
|
||||
```
|
||||
3. 开放服务器的3001、3002和3010端口
|
||||
4. [首次执行,后续无需重复执行]添加脚本的执行权限,命令:
|
||||
@@ -619,6 +621,7 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File ".\start.ps1"
|
||||
```bash
|
||||
NUXT_PUBLIC_BASE_APIURL=/api
|
||||
NUXT_PUBLIC_BASE_SOCKETURL=wss://<你的域名>/socket.io
|
||||
SERVER_HTTP_BIND_IP=127.0.0.1
|
||||
```
|
||||
3. [首次执行,后续无需重复执行]添加执行权限,命令:
|
||||
```bash
|
||||
|
||||
+2
-5
@@ -55,7 +55,7 @@ services:
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
ports:
|
||||
- "${SERVER_HTTP_PORT}:3001" #http端口
|
||||
- "${SERVER_HTTP_BIND_IP:-127.0.0.1}:${SERVER_HTTP_PORT}:3001" # 域名/本机部署默认仅回环;IP/LAN 模式由启动脚本显式设为 0.0.0.0
|
||||
# read_only: true
|
||||
networks:
|
||||
easyai:
|
||||
@@ -341,10 +341,7 @@ services:
|
||||
container_name: sandbox
|
||||
networks:
|
||||
- easyai
|
||||
#沙箱环境默认不对外暴露
|
||||
ports:
|
||||
# - "${SANDBOX_PORT}:8000"
|
||||
- "${SANDBOX_JUPYTERLAB_PORT:-8888}:8888"
|
||||
# 不映射宿主机端口,仅允许 Docker 内网中的 easyai-server 访问
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
volumes:
|
||||
|
||||
+21
-19
@@ -95,6 +95,15 @@ server {
|
||||
return 308 /api/;
|
||||
}
|
||||
|
||||
# Sandbox 只能由 easyai-server 通过 Docker 内网访问,禁止从公网 API 入口绕过。
|
||||
location = /api/sandbox {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location ^~ /api/sandbox/ {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3001/;
|
||||
proxy_read_timeout 1200s; # 您已设置,适合长连接
|
||||
@@ -160,14 +169,12 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
location /jupyterlab/ {
|
||||
proxy_pass http://127.0.0.1:8888/lab/;
|
||||
proxy_redirect off;
|
||||
proxy_set_header X-Original-Prefix '/lab';
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
location = /jupyterlab {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location ^~ /jupyterlab/ {
|
||||
return 404;
|
||||
}
|
||||
location /socket.io {
|
||||
proxy_pass http://127.0.0.1:3002;
|
||||
@@ -197,17 +204,12 @@ server {
|
||||
proxy_buffering off; # 对于 WebSocket 连接禁用缓冲
|
||||
}
|
||||
|
||||
# 沙箱环境 API(脚本执行、下载、安装依赖等),需在 docker-compose 中取消 SANDBOX_PORT 映射
|
||||
location /sandbox/ {
|
||||
proxy_pass http://127.0.0.1:8081/;
|
||||
proxy_read_timeout 300s;
|
||||
client_max_body_size 50M;
|
||||
proxy_redirect off;
|
||||
proxy_set_header X-Original-Prefix '/sandbox';
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
location = /sandbox {
|
||||
return 404;
|
||||
}
|
||||
|
||||
location ^~ /sandbox/ {
|
||||
return 404;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
function Get-ServerHttpBindEnvValue {
|
||||
param([string]$Content, [string]$Key)
|
||||
$match = [regex]::Match($Content, "(?m)^$([regex]::Escape($Key))=([^\r\n]*)$")
|
||||
if ($match.Success) { return $match.Groups[1].Value.Trim() }
|
||||
return ""
|
||||
}
|
||||
|
||||
function Set-ServerHttpBindEnvValue {
|
||||
param([string]$Content, [string]$Key, [string]$Value)
|
||||
$line = "$Key=$Value"
|
||||
$pattern = "(?m)^$([regex]::Escape($Key))=.*$"
|
||||
if ($Content -match $pattern) { return ($Content -replace $pattern, $line) }
|
||||
if ($Content -and -not $Content.EndsWith("`n")) { $Content += "`n" }
|
||||
return ($Content + $line + "`n")
|
||||
}
|
||||
|
||||
function Initialize-ServerHttpBindIp {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Path,
|
||||
[string]$Override = ""
|
||||
)
|
||||
|
||||
if (-not (Test-Path $Path)) { throw "Environment file not found: $Path" }
|
||||
$content = Get-Content $Path -Raw -Encoding UTF8
|
||||
if (-not $content) { $content = "" }
|
||||
|
||||
$current = Get-ServerHttpBindEnvValue $content "SERVER_HTTP_BIND_IP"
|
||||
$target = if (-not [string]::IsNullOrWhiteSpace($Override)) { $Override.Trim() } else { $current }
|
||||
if ([string]::IsNullOrWhiteSpace($target)) {
|
||||
$publicApiUrl = Get-ServerHttpBindEnvValue $content "NUXT_PUBLIC_BASE_APIURL"
|
||||
$target = if (
|
||||
$publicApiUrl -eq "/api" -or
|
||||
$publicApiUrl.StartsWith("/api/") -or
|
||||
$publicApiUrl -match '^https?://(127\.0\.0\.1|localhost)(:\d+)?(?:/|$)'
|
||||
) { "127.0.0.1" } elseif ($publicApiUrl -match '^https?://') { "0.0.0.0" } else { "127.0.0.1" }
|
||||
}
|
||||
|
||||
if ($target -notin @("127.0.0.1", "0.0.0.0")) {
|
||||
throw "SERVER_HTTP_BIND_IP only supports 127.0.0.1 or 0.0.0.0, current value: $target"
|
||||
}
|
||||
|
||||
if ($current -ne $target) {
|
||||
$content = Set-ServerHttpBindEnvValue $content "SERVER_HTTP_BIND_IP" $target
|
||||
[System.IO.File]::WriteAllText($Path, $content, [System.Text.UTF8Encoding]::new($false))
|
||||
Write-Host " ✓ SERVER_HTTP_BIND_IP=$target"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
. (Join-Path $PSScriptRoot "Initialize-ServerHttpBindIp.ps1")
|
||||
|
||||
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("easyai-bind-test-" + [guid]::NewGuid().ToString("N"))
|
||||
New-Item -ItemType Directory -Path $tempDir | Out-Null
|
||||
$envPath = Join-Path $tempDir ".env"
|
||||
|
||||
function Assert-BindIp {
|
||||
param([string]$PublicApiUrl, [string]$Expected)
|
||||
[System.IO.File]::WriteAllText($envPath, "NUXT_PUBLIC_BASE_APIURL=$PublicApiUrl`n", [System.Text.UTF8Encoding]::new($false))
|
||||
Initialize-ServerHttpBindIp -Path $envPath
|
||||
$content = Get-Content $envPath -Raw -Encoding UTF8
|
||||
if ($content -notmatch "(?m)^SERVER_HTTP_BIND_IP=$([regex]::Escape($Expected))$") {
|
||||
throw "Expected SERVER_HTTP_BIND_IP=$Expected for $PublicApiUrl"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Assert-BindIp "/api" "127.0.0.1"
|
||||
Assert-BindIp "http://10.0.0.8:3001" "0.0.0.0"
|
||||
Assert-BindIp "http://127.0.0.1:3001" "127.0.0.1"
|
||||
|
||||
[System.IO.File]::WriteAllText($envPath, "NUXT_PUBLIC_BASE_APIURL=/api`nSERVER_HTTP_BIND_IP=0.0.0.0`n", [System.Text.UTF8Encoding]::new($false))
|
||||
Initialize-ServerHttpBindIp -Path $envPath
|
||||
if ((Get-Content $envPath -Raw -Encoding UTF8) -notmatch '(?m)^SERVER_HTTP_BIND_IP=0\.0\.0\.0$') {
|
||||
throw "Existing explicit bind value was not preserved"
|
||||
}
|
||||
|
||||
$invalidAccepted = $false
|
||||
try {
|
||||
Initialize-ServerHttpBindIp -Path $envPath -Override "192.168.1.8"
|
||||
$invalidAccepted = $true
|
||||
} catch { }
|
||||
if ($invalidAccepted) { throw "Invalid SERVER_HTTP_BIND_IP unexpectedly accepted" }
|
||||
|
||||
Write-Host "Server HTTP bind IP PowerShell tests passed"
|
||||
} finally {
|
||||
Remove-Item -Recurse -Force $tempDir -ErrorAction SilentlyContinue
|
||||
}
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
|
||||
server_bind_read_env_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
awk -F= -v key="$key" '$1 == key { sub(/^[^=]*=/, ""); print; exit }' "$file" | tr -d '\r'
|
||||
}
|
||||
|
||||
server_bind_write_env_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local value="$3"
|
||||
local tmp_file
|
||||
tmp_file="$(mktemp "${file}.XXXXXX")"
|
||||
awk -v key="$key" -v value="$value" '
|
||||
BEGIN { replaced = 0 }
|
||||
$0 ~ "^" key "=" {
|
||||
if (!replaced) {
|
||||
print key "=" value
|
||||
replaced = 1
|
||||
}
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
END {
|
||||
if (!replaced) print key "=" value
|
||||
}
|
||||
' "$file" > "$tmp_file"
|
||||
chmod --reference="$file" "$tmp_file" 2>/dev/null || true
|
||||
mv "$tmp_file" "$file"
|
||||
}
|
||||
|
||||
infer_server_http_bind_ip() {
|
||||
local file="$1"
|
||||
local public_api_url
|
||||
public_api_url="$(server_bind_read_env_value "$file" "NUXT_PUBLIC_BASE_APIURL")"
|
||||
|
||||
case "$public_api_url" in
|
||||
/api|/api/*|http://127.0.0.1:*|https://127.0.0.1:*|http://localhost:*|https://localhost:*)
|
||||
printf '%s\n' "127.0.0.1"
|
||||
;;
|
||||
http://*|https://*)
|
||||
printf '%s\n' "0.0.0.0"
|
||||
;;
|
||||
*)
|
||||
printf '%s\n' "127.0.0.1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
init_server_http_bind_ip() {
|
||||
local file="${1:-.env}"
|
||||
local override="${2:-}"
|
||||
local current target
|
||||
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "❌ 未找到环境文件: $file" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
current="$(server_bind_read_env_value "$file" "SERVER_HTTP_BIND_IP")"
|
||||
target="${override:-$current}"
|
||||
if [ -z "$target" ]; then
|
||||
target="$(infer_server_http_bind_ip "$file")"
|
||||
fi
|
||||
|
||||
case "$target" in
|
||||
127.0.0.1|0.0.0.0) ;;
|
||||
*)
|
||||
echo "❌ SERVER_HTTP_BIND_IP 仅支持 127.0.0.1 或 0.0.0.0,当前为: $target" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$current" != "$target" ]; then
|
||||
server_bind_write_env_value "$file" "SERVER_HTTP_BIND_IP" "$target"
|
||||
echo " ✓ SERVER_HTTP_BIND_IP=$target"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
||||
init_server_http_bind_ip "${1:-.env}" "${2:-}"
|
||||
fi
|
||||
@@ -20,6 +20,7 @@ mkdir -p "$TMP_DIR/scripts"
|
||||
cp \
|
||||
"$REPO_ROOT/scripts/init-security-env.sh" \
|
||||
"$REPO_ROOT/scripts/init-public-api-base-url.sh" \
|
||||
"$REPO_ROOT/scripts/init-server-http-bind-ip.sh" \
|
||||
"$TMP_DIR/scripts/"
|
||||
|
||||
cd "$TMP_DIR"
|
||||
@@ -48,6 +49,34 @@ assert_compression_config() {
|
||||
fi
|
||||
}
|
||||
|
||||
assert_sandbox_public_access_disabled() {
|
||||
local config_file="$1"
|
||||
grep -q '^ location = /api/sandbox {' "$config_file"
|
||||
grep -q '^ location \^~ /api/sandbox/ {' "$config_file"
|
||||
grep -q '^ location = /jupyterlab {' "$config_file"
|
||||
grep -q '^ location \^~ /jupyterlab/ {' "$config_file"
|
||||
grep -q '^ location = /sandbox {' "$config_file"
|
||||
grep -q '^ location \^~ /sandbox/ {' "$config_file"
|
||||
if grep -Eq 'proxy_pass http://127\.0\.0\.1:(8081|8888)' "$config_file"; then
|
||||
echo "Unexpected public Sandbox/Jupyter proxy in $config_file" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_compose_exposure() {
|
||||
local expected_server_host_ip="$1"
|
||||
docker compose config --format json | python3 -c '
|
||||
import json, sys
|
||||
expected = sys.argv[1]
|
||||
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"
|
||||
}
|
||||
|
||||
sed -i.bak 's/^SERVER_HTTP_PORT=.*/SERVER_HTTP_PORT=4100/' .env.sample
|
||||
rm -f .env.sample.bak
|
||||
DEPLOY_NON_INTERACTIVE=1 \
|
||||
@@ -57,8 +86,9 @@ DEPLOY_NON_INTERACTIVE=1 \
|
||||
bash start.sh >/dev/null
|
||||
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
|
||||
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
||||
docker compose config --quiet
|
||||
assert_compose_exposure 0.0.0.0
|
||||
fi
|
||||
|
||||
reset_case
|
||||
@@ -75,6 +105,11 @@ grep -q 'proxy_set_header X-Forwarded-Port $server_port;' demo.example.com.conf
|
||||
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
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=127.0.0.1' .env
|
||||
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
|
||||
assert_compose_exposure 127.0.0.1
|
||||
fi
|
||||
|
||||
# 直接验证 https.sh 的缺省配置生成函数会采用完整模板,而不是只代理 3010。
|
||||
eval "$(awk '
|
||||
@@ -90,6 +125,7 @@ grep -q 'proxy_pass http://127.0.0.1:3001/;' easyai-proxy.conf
|
||||
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
|
||||
|
||||
reset_case
|
||||
DEPLOY_NON_INTERACTIVE=1 \
|
||||
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
# shellcheck source=init-server-http-bind-ip.sh
|
||||
. "$REPO_ROOT/scripts/init-server-http-bind-ip.sh"
|
||||
|
||||
write_env() {
|
||||
printf '%s\n' "$1" > "$TMP_DIR/.env"
|
||||
}
|
||||
|
||||
write_env 'NUXT_PUBLIC_BASE_APIURL=/api'
|
||||
init_server_http_bind_ip "$TMP_DIR/.env" >/dev/null
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=127.0.0.1' "$TMP_DIR/.env"
|
||||
|
||||
write_env 'NUXT_PUBLIC_BASE_APIURL=http://10.0.0.8:3001'
|
||||
init_server_http_bind_ip "$TMP_DIR/.env" >/dev/null
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=0.0.0.0' "$TMP_DIR/.env"
|
||||
|
||||
write_env 'NUXT_PUBLIC_BASE_APIURL=http://127.0.0.1:3001'
|
||||
init_server_http_bind_ip "$TMP_DIR/.env" >/dev/null
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=127.0.0.1' "$TMP_DIR/.env"
|
||||
|
||||
write_env $'NUXT_PUBLIC_BASE_APIURL=/api\nSERVER_HTTP_BIND_IP=0.0.0.0'
|
||||
init_server_http_bind_ip "$TMP_DIR/.env" >/dev/null
|
||||
grep -qx 'SERVER_HTTP_BIND_IP=0.0.0.0' "$TMP_DIR/.env"
|
||||
|
||||
if init_server_http_bind_ip "$TMP_DIR/.env" '192.168.1.8' >/dev/null 2>&1; then
|
||||
echo 'Invalid SERVER_HTTP_BIND_IP unexpectedly accepted' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 'Server HTTP bind IP shell tests passed'
|
||||
@@ -173,6 +173,8 @@ function Setup-EnvFiles {
|
||||
$content = Upsert-Env $content "NUXT_PUBLIC_BASE_APIURL" $publicApiBaseUrl
|
||||
$content = Upsert-Env $content "NUXT_PUBLIC_BASE_SOCKETURL" "ws://$($script:DeployIP):3002"
|
||||
$content = Upsert-Env $content "NUXT_PUBLIC_SG_APIURL" "http://$($script:DeployIP):3003"
|
||||
$serverBindIp = if ($script:DeployIP -eq "127.0.0.1" -or $script:DeployIP -eq "localhost") { "127.0.0.1" } else { "0.0.0.0" }
|
||||
$content = Upsert-Env $content "SERVER_HTTP_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"
|
||||
@@ -386,6 +388,18 @@ function Main {
|
||||
Ensure-FileFromSample ".env.AMS" ".env.AMS.sample"
|
||||
}
|
||||
|
||||
. (Join-Path $script:Root "scripts\Initialize-ServerHttpBindIp.ps1")
|
||||
$serverBindOverride = if ($script:SkipDeployQuestions) {
|
||||
""
|
||||
} elseif ($script:DeployIP -eq "127.0.0.1" -or $script:DeployIP -eq "localhost") {
|
||||
"127.0.0.1"
|
||||
} else {
|
||||
"0.0.0.0"
|
||||
}
|
||||
Initialize-ServerHttpBindIp `
|
||||
-Path (Join-Path $script:Root ".env") `
|
||||
-Override $serverBindOverride
|
||||
|
||||
. (Join-Path $script:Root "scripts\Initialize-SecurityEnv.ps1")
|
||||
Initialize-SecurityEnv -Path (Join-Path $script:Root ".env") -Mode $script:SecurityEnvMode
|
||||
|
||||
|
||||
@@ -495,6 +495,20 @@ main() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# 域名和本机部署默认仅绑定回环;只有明确的 IP/LAN 直连模式才开放后端宿主机端口。
|
||||
# shellcheck source=scripts/init-server-http-bind-ip.sh
|
||||
. ./scripts/init-server-http-bind-ip.sh
|
||||
local server_bind_override=""
|
||||
if [ "$DEPLOY_MODE" = "domain" ]; then
|
||||
server_bind_override="127.0.0.1"
|
||||
elif [ "$DEPLOY_MODE" = "ip" ]; then
|
||||
case "$DEPLOY_IP" in
|
||||
127.0.0.1|localhost) server_bind_override="127.0.0.1" ;;
|
||||
*) server_bind_override="0.0.0.0" ;;
|
||||
esac
|
||||
fi
|
||||
init_server_http_bind_ip .env "$server_bind_override"
|
||||
|
||||
# 空值或历史默认值只在首次发现时生成并写回 .env,后续启动保持稳定。
|
||||
# 数据库中的后台 JWT 轮转配置由后端优先使用,不受此处影响。
|
||||
# shellcheck source=scripts/init-security-env.sh
|
||||
|
||||
@@ -174,6 +174,9 @@ if (-not $skipRepoUpdate) {
|
||||
|
||||
# 空值或历史默认值只初始化一次并写回 .env;已有自定义值及数据库轮转配置不受影响。
|
||||
if (-not (Test-Path ".env")) { Write-Err "未找到 .env,请先执行 start.ps1 完成初始化" }
|
||||
. (Join-Path $scriptDir "scripts\Initialize-ServerHttpBindIp.ps1")
|
||||
Initialize-ServerHttpBindIp -Path (Join-Path $scriptDir ".env")
|
||||
|
||||
. (Join-Path $scriptDir "scripts\Initialize-SecurityEnv.ps1")
|
||||
Initialize-SecurityEnv -Path (Join-Path $scriptDir ".env") -Mode "upgrade"
|
||||
|
||||
|
||||
@@ -128,6 +128,12 @@ if [ ! -f .env ]; then
|
||||
echo "❌ 未找到 .env,请先执行 start.sh 完成初始化"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 老部署缺少绑定配置时,根据现有公开 API 地址迁移:域名/本机保持回环,IP 直连保持可用。
|
||||
# shellcheck source=scripts/init-server-http-bind-ip.sh
|
||||
. ./scripts/init-server-http-bind-ip.sh
|
||||
init_server_http_bind_ip .env
|
||||
|
||||
# shellcheck source=scripts/init-security-env.sh
|
||||
. ./scripts/init-security-env.sh
|
||||
init_security_env .env upgrade
|
||||
|
||||
Reference in New Issue
Block a user