#!/usr/bin/env bash # gen-secrets.sh —— 幂等生成本台所需密钥/证书,持久化到 ./secrets/(700,gitignore)。 # - pangolin.env : HY2_PASSWORD # - hy2/hy2.crt,hy2.key : Hysteria2 自签证书(ECDSA) # - cloudflare.ini : 由 $CF_API_TOKEN 渲染(证书续期用),600 # - backup/age-keypair.txt : age 加密密钥对(私钥请离线保存后从服务器删除) # - backup.env : 备份配置模板(首次生成,需手动填写真实值) set -euo pipefail cd "$(dirname "$0")/.." # -> deploy/ SECRETS_DIR="./secrets" ENV_FILE="${SECRETS_DIR}/pangolin.env" HY2_DIR="${SECRETS_DIR}/hy2" MASQ_CN="www.bing.com" mkdir -p "$HY2_DIR" chmod 700 "$SECRETS_DIR" # Hy2 口令 if [[ -f "$ENV_FILE" ]]; then echo "[gen-secrets] $ENV_FILE 已存在,跳过" else HY2_PASSWORD="$(openssl rand -base64 18 | tr -d '/+=' | cut -c1-24)" umask 177 printf 'HY2_PASSWORD=%s\n' "$HY2_PASSWORD" > "$ENV_FILE" echo "[gen-secrets] 已写入 $ENV_FILE" fi # Hy2 自签证书 if [[ -f "${HY2_DIR}/hy2.crt" && -f "${HY2_DIR}/hy2.key" ]]; then echo "[gen-secrets] Hy2 证书已存在,跳过" else openssl ecparam -genkey -name prime256v1 -out "${HY2_DIR}/hy2.key" openssl req -new -x509 -days 3650 -key "${HY2_DIR}/hy2.key" \ -out "${HY2_DIR}/hy2.crt" -subj "/CN=${MASQ_CN}" chmod 644 "${HY2_DIR}/hy2.crt" "${HY2_DIR}/hy2.key" echo "[gen-secrets] 已写入 Hy2 证书" fi # Cloudflare 续期凭据(有 token 才渲染) if [[ -n "${CF_API_TOKEN:-}" ]]; then umask 177 sed "s|__CF_API_TOKEN__|${CF_API_TOKEN}|g" ./certbot/cloudflare.ini.tmpl > "${SECRETS_DIR}/cloudflare.ini" echo "[gen-secrets] 已渲染 cloudflare.ini" else echo "[gen-secrets] 未设置 CF_API_TOKEN,跳过 cloudflare.ini(续期前需补)" fi # ────────────────────────────────────────────────────────── # age 备份加密密钥对 # 首次生成后: # 1. 将 age-keypair.txt 里的私钥(AGE-SECRET-KEY-...)离线保存 # 2. 从服务器删除私钥(只保留公钥供加密) # 3. 将公钥填入 secrets/backup.env 的 AGE_PUBLIC_KEY 字段 # ────────────────────────────────────────────────────────── BACKUP_DIR="${SECRETS_DIR}/backup" AGE_KEYPAIR="${BACKUP_DIR}/age-keypair.txt" mkdir -p "${BACKUP_DIR}" chmod 700 "${BACKUP_DIR}" if [[ -f "${AGE_KEYPAIR}" ]]; then echo "[gen-secrets] age 密钥对已存在,跳过 (${AGE_KEYPAIR})" else # 检查 age-keygen 是否可用(需要先在容器内或本机安装) if command -v age-keygen > /dev/null 2>&1; then umask 177 age-keygen 2>&1 | tee "${AGE_KEYPAIR}" chmod 600 "${AGE_KEYPAIR}" AGE_PUB="$(grep '^# public key:' "${AGE_KEYPAIR}" | awk '{print $NF}')" echo "" echo "[gen-secrets] ╔═══════════════════════════════════════════════════╗" echo "[gen-secrets] ║ age 密钥对已生成: ${AGE_KEYPAIR}" echo "[gen-secrets] ║ 公钥: ${AGE_PUB}" echo "[gen-secrets] ║" echo "[gen-secrets] ║ ⚠️ 重要:请立即离线备份私钥,然后从服务器删除:" echo "[gen-secrets] ║ grep 'AGE-SECRET' ${AGE_KEYPAIR} # 私钥" echo "[gen-secrets] ║ rm ${AGE_KEYPAIR} # 服务器上删除" echo "[gen-secrets] ╚═══════════════════════════════════════════════════╝" echo "" else echo "[gen-secrets] age-keygen 未安装,跳过密钥对生成" echo "[gen-secrets] 请在备份容器内执行: age-keygen | tee ${AGE_KEYPAIR}" fi fi # 备份配置模板(首次生成,需手动填写) BACKUP_ENV="${SECRETS_DIR}/backup.env" if [[ -f "${BACKUP_ENV}" ]]; then echo "[gen-secrets] ${BACKUP_ENV} 已存在,跳过" else cp ./backup/backup.env.example "${BACKUP_ENV}" chmod 600 "${BACKUP_ENV}" echo "[gen-secrets] 已生成备份配置模板: ${BACKUP_ENV}" echo "[gen-secrets] ⚠️ 请编辑此文件并填写真实 S3 / MySQL / PostgreSQL / age 配置" fi