121 lines
3.7 KiB
Bash
121 lines
3.7 KiB
Bash
#!/bin/bash
|
||
|
||
# 为本地 .env 初始化持久化安全密钥。只处理空值和已知历史默认值,
|
||
# 已存在的自定义值保持不变;数据库中的 JWT 轮转配置仍由后端优先使用。
|
||
|
||
generate_security_secret() {
|
||
if command -v openssl >/dev/null 2>&1; then
|
||
openssl rand -base64 48 | tr -d '\r\n'
|
||
return
|
||
fi
|
||
if [ -r /dev/urandom ]; then
|
||
od -An -N48 -tx1 /dev/urandom | tr -d ' \r\n'
|
||
return
|
||
fi
|
||
echo "❌ 无法生成安全随机值:需要 openssl 或 /dev/urandom" >&2
|
||
return 1
|
||
}
|
||
|
||
generate_initial_admin_password() {
|
||
if command -v openssl >/dev/null 2>&1; then
|
||
# 10 random bytes encoded as hexadecimal: exactly 20 alphanumeric characters.
|
||
openssl rand -hex 10 | tr -d '\r\n'
|
||
return
|
||
fi
|
||
if [ -r /dev/urandom ]; then
|
||
od -An -N10 -tx1 /dev/urandom | tr -d ' \r\n'
|
||
return
|
||
fi
|
||
echo "❌ 无法生成初始管理员密码:需要 openssl 或 /dev/urandom" >&2
|
||
return 1
|
||
}
|
||
|
||
read_env_value() {
|
||
local file="$1"
|
||
local key="$2"
|
||
local value
|
||
value="$(awk -v key="$key" 'index($0, key "=") == 1 { print substr($0, length(key) + 2); exit }' "$file")"
|
||
value="$(printf '%s' "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
||
case "$value" in
|
||
\"*\") value="${value#\"}"; value="${value%\"}" ;;
|
||
\'*\') value="${value#\'}"; value="${value%\'}" ;;
|
||
esac
|
||
printf '%s' "$value"
|
||
}
|
||
|
||
write_env_value() {
|
||
local file="$1"
|
||
local key="$2"
|
||
local value="$3"
|
||
local tmp
|
||
tmp="$(mktemp "${file}.tmp.XXXXXX")"
|
||
awk -v key="$key" -v value="$value" '
|
||
BEGIN { replaced = 0 }
|
||
index($0, key "=") == 1 {
|
||
if (!replaced) {
|
||
print key "=" value
|
||
replaced = 1
|
||
}
|
||
next
|
||
}
|
||
{ print }
|
||
END {
|
||
if (!replaced) print key "=" value
|
||
}
|
||
' "$file" > "$tmp"
|
||
chmod 600 "$tmp"
|
||
mv "$tmp" "$file"
|
||
}
|
||
|
||
ensure_security_secret() {
|
||
local file="$1"
|
||
local key="$2"
|
||
local legacy_value="${3:-}"
|
||
local minimum_length="${4:-32}"
|
||
local current
|
||
current="$(read_env_value "$file" "$key")"
|
||
if [ "${#current}" -ge "$minimum_length" ] && { [ -z "$legacy_value" ] || [ "$current" != "$legacy_value" ]; }; then
|
||
return 0
|
||
fi
|
||
write_env_value "$file" "$key" "$(generate_security_secret)"
|
||
echo " ✓ 已初始化安全密钥: ${key}"
|
||
}
|
||
|
||
ensure_initial_admin_password() {
|
||
local file="$1"
|
||
local current
|
||
current="$(read_env_value "$file" "CONFIG_INITIAL_ADMIN_PASSWORD")"
|
||
if [ "${#current}" -ge 12 ]; then
|
||
return 0
|
||
fi
|
||
write_env_value "$file" "CONFIG_INITIAL_ADMIN_PASSWORD" "$(generate_initial_admin_password)"
|
||
echo " ✓ 已初始化管理员密码: CONFIG_INITIAL_ADMIN_PASSWORD"
|
||
}
|
||
|
||
init_security_env() {
|
||
local file="${1:-.env}"
|
||
local mode="${2:-upgrade}"
|
||
if [ ! -f "$file" ]; then
|
||
echo "❌ 环境配置文件不存在: $file" >&2
|
||
return 1
|
||
fi
|
||
if [ "$mode" != "new" ] && [ "$mode" != "upgrade" ]; then
|
||
echo "❌ 安全环境初始化模式无效: $mode(仅支持 new/upgrade)" >&2
|
||
return 1
|
||
fi
|
||
|
||
ensure_security_secret "$file" "CONFIG_JWT_SECRET" "this is a very secret secret"
|
||
if [ "$mode" = "new" ]; then
|
||
ensure_security_secret "$file" "CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY"
|
||
ensure_security_secret "$file" "CONFIG_OTP_HASH_SECRET"
|
||
ensure_security_secret "$file" "CONFIG_AUDIT_HASH_PEPPER"
|
||
ensure_security_secret "$file" "CONFIG_AUDIT_INTEGRITY_KEY"
|
||
ensure_initial_admin_password "$file"
|
||
fi
|
||
chmod 600 "$file"
|
||
}
|
||
|
||
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
||
init_security_env "${1:-.env}" "${2:-upgrade}"
|
||
fi
|