方案C:分发层容器化(edge nginx)+ Hy2 + DNS-01 续期
deploy-pangolin / deploy (push) Has been cancelled

- edge: 容器化 nginx 接管 80/443,逐字移植现网 vhost(blog/jiu/marzban/pay)+ stream SNI 分流
- singbox: Hysteria2(host 网络,UDP 443)
- certbot: DNS-01(Cloudflare)续期,容器化
- xray: REALITY 仅 profile newnode(本台沿用 Marzban)
- deploy.sh 幂等且切换感知;cutover.sh 一次性停宿主 nginx 切容器(可秒级回滚)
- 弃用方案A 的 host nginx-apply/sudoers/root-setup
- 部署目录改 ~/pangolin(免 root);CI 注入 CF_API_TOKEN

EC2 实测:edge nginx -t 通过(真实证书)、compose 校验通过

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-05-30 16:42:30 +08:00
parent bc77e85b7d
commit 1a346c32c1
20 changed files with 430 additions and 277 deletions
+17 -25
View File
@@ -1,53 +1,45 @@
#!/usr/bin/env bash
# gen-secrets.sh —— 首次在 EC2 生成并持久化密钥/证书(幂等)。
# 产物存于 ./secrets/(700,gitignore,绝不进仓库/CI 日志)。
# - pangolin.env : UUID / REALITY 公私钥 / shortId / Hy2 口令
# - hy2/hy2.crt, hy2/hy2.key : Hysteria2 自签证书(ECDSA)
# gen-secrets.sh —— 幂等生成本台所需密钥/证书,持久化到 ./secrets/(700,gitignore)。
# - pangolin.env : HY2_PASSWORD
# - hy2/hy2.crt,hy2.key: Hysteria2 自签证书(ECDSA)
# - cloudflare.ini : 由 $CF_API_TOKEN 渲染(证书续期用),600
set -euo pipefail
cd "$(dirname "$0")/.." # -> deploy/
SECRETS_DIR="./secrets"
ENV_FILE="${SECRETS_DIR}/pangolin.env"
HY2_DIR="${SECRETS_DIR}/hy2"
XRAY_IMAGE="ghcr.io/xtls/xray-core:26.3.27"
MASQ_CN="www.bing.com"
mkdir -p "$HY2_DIR"
chmod 700 "$SECRETS_DIR"
# Hy2 口令
if [[ -f "$ENV_FILE" ]]; then
echo "[gen-secrets] $ENV_FILE 已存在,跳过密钥生成(幂等)"
echo "[gen-secrets] $ENV_FILE 已存在,跳过"
else
echo "[gen-secrets] 生成 REALITY 密钥对 / UUID / shortId / Hy2 口令 ..."
# REALITY x25519 密钥对(从 xray 输出解析 Private/Public)
KP="$(docker run --rm "$XRAY_IMAGE" x25519)"
REALITY_PRIVATE_KEY="$(printf '%s\n' "$KP" | awk -F': ' '/Private/{print $2}')"
REALITY_PUBLIC_KEY="$(printf '%s\n' "$KP" | awk -F': ' '/Public/{print $2}')"
UUID="$(cat /proc/sys/kernel/random/uuid)"
REALITY_SHORT_ID="$(openssl rand -hex 8)"
HY2_PASSWORD="$(openssl rand -base64 18 | tr -d '/+=' | cut -c1-24)"
umask 177
cat > "$ENV_FILE" <<EOF
# pangolin Phase-0 secrets —— 切勿提交/外泄
UUID=${UUID}
REALITY_PRIVATE_KEY=${REALITY_PRIVATE_KEY}
REALITY_PUBLIC_KEY=${REALITY_PUBLIC_KEY}
REALITY_SHORT_ID=${REALITY_SHORT_ID}
HY2_PASSWORD=${HY2_PASSWORD}
EOF
printf 'HY2_PASSWORD=%s\n' "$HY2_PASSWORD" > "$ENV_FILE"
echo "[gen-secrets] 已写入 $ENV_FILE"
fi
# Hysteria2 自签证书(幂等)
# Hy2 自签证书
if [[ -f "${HY2_DIR}/hy2.crt" && -f "${HY2_DIR}/hy2.key" ]]; then
echo "[gen-secrets] Hy2 证书已存在,跳过"
else
echo "[gen-secrets] 生成 Hy2 自签证书 (CN=${MASQ_CN}) ..."
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}"
# sing-box 容器内以 root 运行,证书需可读
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