fix: 保留 Compose 环境变量中的特殊字符
- 引用并转义生成的 .env 值,防止 Compose 意外执行变量插值 - 为兼容旧版 Compose,拒绝管理员凭证中不受支持的单引号 - 从运行中的容器显示实际生效的管理员凭证
This commit is contained in:
+48
-10
@@ -170,9 +170,27 @@ gen_secret() {
|
|||||||
openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'
|
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
|
if [ ! -f "$env_file" ]; then
|
||||||
# 兼容旧版安装:尝试从旧 docker-compose.yml 中提取已在用的数据库密码,
|
# 兼容旧版安装:尝试从旧 docker-compose.yml 中提取已在用的数据库密码,
|
||||||
# 避免生成新密码后与已有 postgres 数据目录不一致导致连不上库
|
# 避免生成新密码后与已有 postgres 数据目录不一致导致连不上库
|
||||||
|
# 注意:这里回读的是旧版明文 docker-compose.yml(历史格式),与本脚本新生成的
|
||||||
|
# 引号化 .env 表示法不同。切勿改成从 .env 回读——.env 里的值可能带引号/转义,
|
||||||
|
# 下面的 [^ #]* 会在引号或 # 处截断,取到错误密码。
|
||||||
old_pg_password=""
|
old_pg_password=""
|
||||||
old_pg_user=""
|
old_pg_user=""
|
||||||
if [ -f docker-compose.yml ]; then
|
if [ -f docker-compose.yml ]; then
|
||||||
@@ -192,23 +210,40 @@ if [ ! -f "$env_file" ]; then
|
|||||||
admin_email="${admin_email:-$default_admin_email}"
|
admin_email="${admin_email:-$default_admin_email}"
|
||||||
read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password
|
read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password
|
||||||
admin_password="${admin_password:-$default_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 <<EOF > "$env_file"
|
cat <<EOF > "$env_file"
|
||||||
# Sub2API 环境配置(由安装脚本生成,请勿删除,否则需重新配置)
|
# Sub2API 环境配置(由安装脚本生成,请勿删除,否则需重新配置)
|
||||||
# 数据库
|
# 数据库
|
||||||
POSTGRES_USER=$pg_user
|
POSTGRES_USER=$pg_user_env
|
||||||
POSTGRES_PASSWORD=$pg_password
|
POSTGRES_PASSWORD=$pg_password_env
|
||||||
POSTGRES_DB=sub2api
|
POSTGRES_DB=$pg_db_env
|
||||||
|
|
||||||
# 固定密钥:保证容器重建/更新后登录态和 2FA 不失效
|
# 固定密钥:保证容器重建/更新后登录态和 2FA 不失效
|
||||||
JWT_SECRET=$(gen_secret)
|
JWT_SECRET=$jwt_secret_env
|
||||||
TOTP_ENCRYPTION_KEY=$(gen_secret)
|
TOTP_ENCRYPTION_KEY=$totp_key_env
|
||||||
|
|
||||||
# 管理员账户(仅首次启动时创建,之后修改无效)
|
# 管理员账户(仅首次启动时创建,之后修改无效)
|
||||||
ADMIN_EMAIL=$admin_email
|
ADMIN_EMAIL=$admin_email_env
|
||||||
ADMIN_PASSWORD=$admin_password
|
ADMIN_PASSWORD=$admin_password_env
|
||||||
|
|
||||||
TZ=Asia/Shanghai
|
TZ=$tz_env
|
||||||
EOF
|
EOF
|
||||||
chmod 600 "$env_file"
|
chmod 600 "$env_file"
|
||||||
echo "已生成 $env_file(包含数据库密码、密钥和管理员初始密码,请妥善保管)。"
|
echo "已生成 $env_file(包含数据库密码、密钥和管理员初始密码,请妥善保管)。"
|
||||||
@@ -346,8 +381,11 @@ fi
|
|||||||
echo "------------------------"
|
echo "------------------------"
|
||||||
echo "访问链接:"
|
echo "访问链接:"
|
||||||
echo "http://服务器IP:$port80"
|
echo "http://服务器IP:$port80"
|
||||||
echo "管理员账户(首次安装时自动创建,凭证保存在 $env_file):"
|
echo "管理员账户(首次安装时自动创建,以下为容器实际收到的凭证):"
|
||||||
grep -E '^(ADMIN_EMAIL|ADMIN_PASSWORD)=' "$env_file" || true
|
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 "------------------------"
|
||||||
echo "已启用 AUTO_SETUP 自动初始化:更新/重建容器后无需重新走网页安装向导。"
|
echo "已启用 AUTO_SETUP 自动初始化:更新/重建容器后无需重新走网页安装向导。"
|
||||||
echo "已开启定时自动更新:每天 04:10 仅拉取最新 app 镜像并重启(db / redis 不动)。"
|
echo "已开启定时自动更新:每天 04:10 仅拉取最新 app 镜像并重启(db / redis 不动)。"
|
||||||
|
|||||||
+48
-10
@@ -107,9 +107,27 @@ gen_secret() {
|
|||||||
openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'
|
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
|
if [ ! -f "$env_file" ]; then
|
||||||
# 兼容旧版安装:尝试从旧 docker-compose.yml 中提取已在用的数据库密码,
|
# 兼容旧版安装:尝试从旧 docker-compose.yml 中提取已在用的数据库密码,
|
||||||
# 避免生成新密码后与已有 postgres 数据目录不一致导致连不上库
|
# 避免生成新密码后与已有 postgres 数据目录不一致导致连不上库
|
||||||
|
# 注意:这里回读的是旧版明文 docker-compose.yml(历史格式),与本脚本新生成的
|
||||||
|
# 引号化 .env 表示法不同。切勿改成从 .env 回读——.env 里的值可能带引号/转义,
|
||||||
|
# 下面的 [^ #]* 会在引号或 # 处截断,取到错误密码。
|
||||||
old_pg_password=""
|
old_pg_password=""
|
||||||
old_pg_user=""
|
old_pg_user=""
|
||||||
if [ -f docker-compose.yml ]; then
|
if [ -f docker-compose.yml ]; then
|
||||||
@@ -129,23 +147,40 @@ if [ ! -f "$env_file" ]; then
|
|||||||
admin_email="${admin_email:-$default_admin_email}"
|
admin_email="${admin_email:-$default_admin_email}"
|
||||||
read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password
|
read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password
|
||||||
admin_password="${admin_password:-$default_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 <<EOF > "$env_file"
|
cat <<EOF > "$env_file"
|
||||||
# Sub2API 环境配置(由安装脚本生成,请勿删除,否则需重新配置)
|
# Sub2API 环境配置(由安装脚本生成,请勿删除,否则需重新配置)
|
||||||
# 数据库
|
# 数据库
|
||||||
POSTGRES_USER=$pg_user
|
POSTGRES_USER=$pg_user_env
|
||||||
POSTGRES_PASSWORD=$pg_password
|
POSTGRES_PASSWORD=$pg_password_env
|
||||||
POSTGRES_DB=sub2api
|
POSTGRES_DB=$pg_db_env
|
||||||
|
|
||||||
# 固定密钥:保证容器重建/更新后登录态和 2FA 不失效
|
# 固定密钥:保证容器重建/更新后登录态和 2FA 不失效
|
||||||
JWT_SECRET=$(gen_secret)
|
JWT_SECRET=$jwt_secret_env
|
||||||
TOTP_ENCRYPTION_KEY=$(gen_secret)
|
TOTP_ENCRYPTION_KEY=$totp_key_env
|
||||||
|
|
||||||
# 管理员账户(仅首次启动时创建,之后修改无效)
|
# 管理员账户(仅首次启动时创建,之后修改无效)
|
||||||
ADMIN_EMAIL=$admin_email
|
ADMIN_EMAIL=$admin_email_env
|
||||||
ADMIN_PASSWORD=$admin_password
|
ADMIN_PASSWORD=$admin_password_env
|
||||||
|
|
||||||
TZ=Asia/Shanghai
|
TZ=$tz_env
|
||||||
EOF
|
EOF
|
||||||
chmod 600 "$env_file"
|
chmod 600 "$env_file"
|
||||||
echo "已生成 $env_file(包含数据库密码、密钥和管理员初始密码,请妥善保管)。"
|
echo "已生成 $env_file(包含数据库密码、密钥和管理员初始密码,请妥善保管)。"
|
||||||
@@ -283,8 +318,11 @@ fi
|
|||||||
echo "------------------------"
|
echo "------------------------"
|
||||||
echo "访问链接:"
|
echo "访问链接:"
|
||||||
echo "https://sub2api.ghuang.top"
|
echo "https://sub2api.ghuang.top"
|
||||||
echo "管理员账户(首次安装时自动创建,凭证保存在 $env_file):"
|
echo "管理员账户(首次安装时自动创建,以下为容器实际收到的凭证):"
|
||||||
grep -E '^(ADMIN_EMAIL|ADMIN_PASSWORD)=' "$env_file" || true
|
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 "------------------------"
|
||||||
echo "已启用 AUTO_SETUP 自动初始化:更新/重建容器后无需重新走网页安装向导。"
|
echo "已启用 AUTO_SETUP 自动初始化:更新/重建容器后无需重新走网页安装向导。"
|
||||||
echo "已开启定时自动更新:每天 04:00 仅拉取最新 app 镜像并重启(db / redis 不动)。"
|
echo "已开启定时自动更新:每天 04:00 仅拉取最新 app 镜像并重启(db / redis 不动)。"
|
||||||
|
|||||||
Reference in New Issue
Block a user