From d380f35db9586635af2f1075099db502b9c60c3e Mon Sep 17 00:00:00 2001 From: eddy Date: Sat, 18 Jul 2026 04:10:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=9D=E7=95=99=20Compose=20=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8F=98=E9=87=8F=E4=B8=AD=E7=9A=84=E7=89=B9=E6=AE=8A?= =?UTF-8?q?=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引用并转义生成的 .env 值,防止 Compose 意外执行变量插值 - 为兼容旧版 Compose,拒绝管理员凭证中不受支持的单引号 - 从运行中的容器显示实际生效的管理员凭证 --- install/23-2-Sub2API.sh | 58 ++++++++++++++++++++++++++++++++++------- install/23-Sub2API.sh | 58 ++++++++++++++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 20 deletions(-) diff --git a/install/23-2-Sub2API.sh b/install/23-2-Sub2API.sh index 3819cab..64c145a 100644 --- a/install/23-2-Sub2API.sh +++ b/install/23-2-Sub2API.sh @@ -170,9 +170,27 @@ gen_secret() { openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' } +# Compose 对 .env 中未加引号和双引号的值执行变量插值。 +# 不含单引号的值用单引号包裹,按字面处理($、#、空格、\ 均不被改写); +# 含单引号的值改用双引号包裹,并转义 \、" 和 $。 +compose_env_literal() { + local value=$1 + if [[ $value != *"'"* ]]; then + printf "'%s'" "$value" + return + fi + local escaped=${value//\\/\\\\} + escaped=${escaped//\"/\\\"} + escaped=${escaped//\$/\\\$} + printf '"%s"' "$escaped" +} + if [ ! -f "$env_file" ]; then # 兼容旧版安装:尝试从旧 docker-compose.yml 中提取已在用的数据库密码, # 避免生成新密码后与已有 postgres 数据目录不一致导致连不上库 + # 注意:这里回读的是旧版明文 docker-compose.yml(历史格式),与本脚本新生成的 + # 引号化 .env 表示法不同。切勿改成从 .env 回读——.env 里的值可能带引号/转义, + # 下面的 [^ #]* 会在引号或 # 处截断,取到错误密码。 old_pg_password="" old_pg_user="" if [ -f docker-compose.yml ]; then @@ -192,23 +210,40 @@ if [ ! -f "$env_file" ]; then admin_email="${admin_email:-$default_admin_email}" read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password admin_password="${admin_password:-$default_admin_password}" + # 单引号值无法在旧版 docker-compose (v1) 的 .env 解析中可靠转义,直接拒绝 + while [[ $admin_email == *"'"* ]] || [[ $admin_password == *"'"* ]]; do + echo "邮箱或密码不能包含单引号('),请重新输入。" + read -r -p "管理员邮箱(默认 $default_admin_email):" admin_email + admin_email="${admin_email:-$default_admin_email}" + read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password + admin_password="${admin_password:-$default_admin_password}" + done + + pg_user_env=$(compose_env_literal "$pg_user") + pg_password_env=$(compose_env_literal "$pg_password") + pg_db_env=$(compose_env_literal "sub2api") + jwt_secret_env=$(compose_env_literal "$(gen_secret)") + totp_key_env=$(compose_env_literal "$(gen_secret)") + admin_email_env=$(compose_env_literal "$admin_email") + admin_password_env=$(compose_env_literal "$admin_password") + tz_env=$(compose_env_literal "Asia/Shanghai") cat < "$env_file" # Sub2API 环境配置(由安装脚本生成,请勿删除,否则需重新配置) # 数据库 -POSTGRES_USER=$pg_user -POSTGRES_PASSWORD=$pg_password -POSTGRES_DB=sub2api +POSTGRES_USER=$pg_user_env +POSTGRES_PASSWORD=$pg_password_env +POSTGRES_DB=$pg_db_env # 固定密钥:保证容器重建/更新后登录态和 2FA 不失效 -JWT_SECRET=$(gen_secret) -TOTP_ENCRYPTION_KEY=$(gen_secret) +JWT_SECRET=$jwt_secret_env +TOTP_ENCRYPTION_KEY=$totp_key_env # 管理员账户(仅首次启动时创建,之后修改无效) -ADMIN_EMAIL=$admin_email -ADMIN_PASSWORD=$admin_password +ADMIN_EMAIL=$admin_email_env +ADMIN_PASSWORD=$admin_password_env -TZ=Asia/Shanghai +TZ=$tz_env EOF chmod 600 "$env_file" echo "已生成 $env_file(包含数据库密码、密钥和管理员初始密码,请妥善保管)。" @@ -346,8 +381,11 @@ fi echo "------------------------" echo "访问链接:" echo "http://服务器IP:$port80" -echo "管理员账户(首次安装时自动创建,凭证保存在 $env_file):" -grep -E '^(ADMIN_EMAIL|ADMIN_PASSWORD)=' "$env_file" || true +echo "管理员账户(首次安装时自动创建,以下为容器实际收到的凭证):" +if ! docker inspect sub2api-2-app --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null \ + | grep -E '^(ADMIN_EMAIL|ADMIN_PASSWORD)='; then + echo "(容器尚未就绪,凭证可稍后执行 docker inspect sub2api-2-app 查看;如查看 $env_file,注意值外层的引号不属于密码本身)" +fi echo "------------------------" echo "已启用 AUTO_SETUP 自动初始化:更新/重建容器后无需重新走网页安装向导。" echo "已开启定时自动更新:每天 04:10 仅拉取最新 app 镜像并重启(db / redis 不动)。" diff --git a/install/23-Sub2API.sh b/install/23-Sub2API.sh index 36aaecb..227cf03 100644 --- a/install/23-Sub2API.sh +++ b/install/23-Sub2API.sh @@ -107,9 +107,27 @@ gen_secret() { openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' } +# Compose 对 .env 中未加引号和双引号的值执行变量插值。 +# 不含单引号的值用单引号包裹,按字面处理($、#、空格、\ 均不被改写); +# 含单引号的值改用双引号包裹,并转义 \、" 和 $。 +compose_env_literal() { + local value=$1 + if [[ $value != *"'"* ]]; then + printf "'%s'" "$value" + return + fi + local escaped=${value//\\/\\\\} + escaped=${escaped//\"/\\\"} + escaped=${escaped//\$/\\\$} + printf '"%s"' "$escaped" +} + if [ ! -f "$env_file" ]; then # 兼容旧版安装:尝试从旧 docker-compose.yml 中提取已在用的数据库密码, # 避免生成新密码后与已有 postgres 数据目录不一致导致连不上库 + # 注意:这里回读的是旧版明文 docker-compose.yml(历史格式),与本脚本新生成的 + # 引号化 .env 表示法不同。切勿改成从 .env 回读——.env 里的值可能带引号/转义, + # 下面的 [^ #]* 会在引号或 # 处截断,取到错误密码。 old_pg_password="" old_pg_user="" if [ -f docker-compose.yml ]; then @@ -129,23 +147,40 @@ if [ ! -f "$env_file" ]; then admin_email="${admin_email:-$default_admin_email}" read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password admin_password="${admin_password:-$default_admin_password}" + # 单引号值无法在旧版 docker-compose (v1) 的 .env 解析中可靠转义,直接拒绝 + while [[ $admin_email == *"'"* ]] || [[ $admin_password == *"'"* ]]; do + echo "邮箱或密码不能包含单引号('),请重新输入。" + read -r -p "管理员邮箱(默认 $default_admin_email):" admin_email + admin_email="${admin_email:-$default_admin_email}" + read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password + admin_password="${admin_password:-$default_admin_password}" + done + + pg_user_env=$(compose_env_literal "$pg_user") + pg_password_env=$(compose_env_literal "$pg_password") + pg_db_env=$(compose_env_literal "sub2api") + jwt_secret_env=$(compose_env_literal "$(gen_secret)") + totp_key_env=$(compose_env_literal "$(gen_secret)") + admin_email_env=$(compose_env_literal "$admin_email") + admin_password_env=$(compose_env_literal "$admin_password") + tz_env=$(compose_env_literal "Asia/Shanghai") cat < "$env_file" # Sub2API 环境配置(由安装脚本生成,请勿删除,否则需重新配置) # 数据库 -POSTGRES_USER=$pg_user -POSTGRES_PASSWORD=$pg_password -POSTGRES_DB=sub2api +POSTGRES_USER=$pg_user_env +POSTGRES_PASSWORD=$pg_password_env +POSTGRES_DB=$pg_db_env # 固定密钥:保证容器重建/更新后登录态和 2FA 不失效 -JWT_SECRET=$(gen_secret) -TOTP_ENCRYPTION_KEY=$(gen_secret) +JWT_SECRET=$jwt_secret_env +TOTP_ENCRYPTION_KEY=$totp_key_env # 管理员账户(仅首次启动时创建,之后修改无效) -ADMIN_EMAIL=$admin_email -ADMIN_PASSWORD=$admin_password +ADMIN_EMAIL=$admin_email_env +ADMIN_PASSWORD=$admin_password_env -TZ=Asia/Shanghai +TZ=$tz_env EOF chmod 600 "$env_file" echo "已生成 $env_file(包含数据库密码、密钥和管理员初始密码,请妥善保管)。" @@ -283,8 +318,11 @@ fi echo "------------------------" echo "访问链接:" echo "https://sub2api.ghuang.top" -echo "管理员账户(首次安装时自动创建,凭证保存在 $env_file):" -grep -E '^(ADMIN_EMAIL|ADMIN_PASSWORD)=' "$env_file" || true +echo "管理员账户(首次安装时自动创建,以下为容器实际收到的凭证):" +if ! docker inspect sub2api-app --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null \ + | grep -E '^(ADMIN_EMAIL|ADMIN_PASSWORD)='; then + echo "(容器尚未就绪,凭证可稍后执行 docker inspect sub2api-app 查看;如查看 $env_file,注意值外层的引号不属于密码本身)" +fi echo "------------------------" echo "已启用 AUTO_SETUP 自动初始化:更新/重建容器后无需重新走网页安装向导。" echo "已开启定时自动更新:每天 04:00 仅拉取最新 app 镜像并重启(db / redis 不动)。"