Files
youlegames/codes/agent/game-docker/init-ssl.sh
2026-04-13 17:06:02 +08:00

226 lines
7.5 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ============================================
# SSL 证书初始化脚本(首次部署时运行)
#
# 使用 Let's Encrypt (certbot) 为 3 个域名申请证书
# 流程:
# 1. 启动 Nginx仅 HTTP 80 端口,用于 ACME 验证)
# 2. 用 certbot 对每个域名申请证书
# 3. 重新加载 Nginx 以启用 HTTPS
#
# 用法:
# ./init-ssl.sh # 正式申请证书
# ./init-ssl.sh --staging # 使用 staging 环境测试(不受速率限制)
# ./init-ssl.sh --dry-run # 仅测试,不真正申请
# ============================================
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 加载 .env
if [ ! -f .env ]; then
log_error ".env 文件不存在!请先执行: cp .env.example .env 并填写配置"
exit 1
fi
source .env
# 检查必要变量
if [ -z "$SSL_EMAIL" ]; then
log_error "请在 .env 中设置 SSL_EMAIL用于 Let's Encrypt 证书申请通知)"
exit 1
fi
# 从 ROOT_DOMAIN 自动推导子域名(如 .env 中未单独配置)
if [ -z "$ROOT_DOMAIN" ]; then
log_error "请在 .env 中设置 ROOT_DOMAIN父域名如 example.com"
exit 1
fi
: ${API_DOMAIN:="api.${ROOT_DOMAIN}"}
: ${DLWEB_DOMAIN:="dlapi.${ROOT_DOMAIN}"}
: ${WX_DOMAIN:="wxapi.${ROOT_DOMAIN}"}
DOMAINS=("$API_DOMAIN" "$DLWEB_DOMAIN" "$WX_DOMAIN")
# 判断使用 docker-compose 还是 docker compose
COMPOSE_CMD="docker compose"
if ! docker compose version &> /dev/null 2>&1; then
COMPOSE_CMD="docker-compose"
fi
# 获取 Docker Compose 项目名(用于 volume 前缀)
PROJECT_NAME="$($COMPOSE_CMD ps --format '{{.Project}}' 2>/dev/null | head -1)"
if [ -z "$PROJECT_NAME" ]; then
PROJECT_NAME="$(basename "$SCRIPT_DIR")"
fi
# 解析参数
STAGING_ARG=""
DRY_RUN=""
for arg in "$@"; do
case $arg in
--staging)
STAGING_ARG="--staging"
log_warn "使用 Let's Encrypt Staging 环境(测试用,证书不受信任)"
;;
--dry-run)
DRY_RUN="--dry-run"
log_warn "Dry-run 模式,不会真正申请证书"
;;
esac
done
# ============================================
# Step 1: 生成临时自签名证书(让 Nginx 能先启动)
# ============================================
log_info "Step 1: 生成临时自签名证书..."
for domain in "${DOMAINS[@]}"; do
CERT_DIR="./docker/nginx/dummy-certs/$domain"
mkdir -p "$CERT_DIR"
if [ ! -f "$CERT_DIR/fullchain.pem" ]; then
openssl req -x509 -nodes -newkey rsa:2048 -days 1 \
-keyout "$CERT_DIR/privkey.pem" \
-out "$CERT_DIR/fullchain.pem" \
-subj "/CN=$domain" 2>/dev/null
log_info " 已生成临时自签名证书: $domain"
fi
done
# ============================================
# Step 2: 将临时证书写入 certbot-certs volume
# ============================================
log_info "Step 2: 初始化证书 volume..."
# 先构建镜像并创建 volume不启动 nginx因为证书还没写入
$COMPOSE_CMD build nginx 2>/dev/null || true
$COMPOSE_CMD up --no-start nginx 2>/dev/null || true
# 用临时 alpine 容器直接挂载 certbot-certs volume读写写入证书
# nginx 挂载该卷为 :ro不能通过 docker cp 写入,需绕过
DUMMY_CERTS_ABS="$(cd "$(dirname "$0")" && pwd)/docker/nginx/dummy-certs"
for domain in "${DOMAINS[@]}"; do
LIVE_DIR="/etc/letsencrypt/live/$domain"
log_info " 写入临时证书: $domain"
docker run --rm \
-v "${PROJECT_NAME}_certbot-certs:/etc/letsencrypt" \
-v "$DUMMY_CERTS_ABS/$domain:/src:ro" \
alpine sh -c "mkdir -p '$LIVE_DIR' && cp /src/fullchain.pem '$LIVE_DIR/' && cp /src/privkey.pem '$LIVE_DIR/'"
done
# 启动 nginx证书已就绪
$COMPOSE_CMD up -d nginx 2>/dev/null || true
sleep 2
# 重新加载 Nginx 以确认证书加载
docker exec youle-nginx nginx -s reload 2>/dev/null || true
log_info " Nginx 已使用临时证书启动"
# ============================================
# Step 3: 用 certbot 申请真实证书
# ============================================
log_info "Step 3: 申请 Let's Encrypt 证书..."
# 清除 volume 中的 dummy 证书目录,避免 certbot 报 "live directory exists"
log_info " 清理 volume 中的临时证书目录..."
CLEAN_CMD="rm -rf"
for domain in "${DOMAINS[@]}"; do
CLEAN_CMD="$CLEAN_CMD /etc/letsencrypt/live/$domain /etc/letsencrypt/live/${domain}-* /etc/letsencrypt/archive/$domain /etc/letsencrypt/renewal/$domain.conf"
done
docker run --rm \
-v "${PROJECT_NAME}_certbot-certs:/etc/letsencrypt" \
alpine sh -c "$CLEAN_CMD" 2>/dev/null || true
for domain in "${DOMAINS[@]}"; do
log_info " 正在为 $domain 申请证书..."
docker run --rm \
-v "${PROJECT_NAME}_certbot-webroot:/var/www/certbot" \
-v "${PROJECT_NAME}_certbot-certs:/etc/letsencrypt" \
--entrypoint certbot \
certbot/certbot:latest \
certonly \
--webroot \
-w /var/www/certbot \
--email "$SSL_EMAIL" \
--agree-tos \
--no-eff-email \
--force-renewal \
-d "$domain" \
$STAGING_ARG \
$DRY_RUN
if [ $? -eq 0 ]; then
log_info "$domain 证书申请成功"
else
log_error "$domain 证书申请失败!请检查:"
log_error " - 域名 DNS 是否已正确解析到本服务器"
log_error " - 服务器 80 端口是否对外开放"
log_error " - 是否超过 Let's Encrypt 速率限制(可用 --staging 测试)"
fi
done
# ============================================
# Step 4: 重新加载 Nginx 以使用真实证书
# ============================================
log_info "Step 4: 重新加载 Nginx..."
docker exec youle-nginx nginx -s reload
# ============================================
# Step 5: 清理临时文件并启动 certbot 定时续签
# ============================================
rm -rf ./docker/nginx/dummy-certs
log_info "Step 5: 启动 certbot 自动续签服务..."
$COMPOSE_CMD up -d certbot
# ============================================
# Step 6: 安装 crontab 定时重载 Nginx使续签生效
# ============================================
log_info "Step 6: 设置自动重载 Nginx 的 crontab..."
CRON_JOB="0 */12 * * * docker exec youle-nginx nginx -s reload >/dev/null 2>&1"
CRON_MARKER="# youle-nginx-ssl-reload"
# 检查是否已存在
if crontab -l 2>/dev/null | grep -q "$CRON_MARKER"; then
log_info " crontab 已存在,跳过"
else
# 追加到当前用户的 crontab
(crontab -l 2>/dev/null; echo "$CRON_JOB $CRON_MARKER") | crontab -
log_info " 已添加 crontab: 每 12 小时重载 Nginx使续签的证书生效"
fi
echo ""
log_info "============================================"
log_info "SSL 初始化完成!"
log_info "============================================"
echo ""
echo " 证书信息:"
for domain in "${DOMAINS[@]}"; do
echo " https://$domain"
done
echo ""
echo " 证书有效期: 90 天"
echo " 自动续签: certbot 容器每 12 小时检查一次"
echo " 自动重载: crontab 每 12 小时执行 nginx -s reload"
echo ""
echo " 查看证书状态:"
echo " $COMPOSE_CMD run --rm certbot certificates"
echo ""
echo " 手动续签:"
echo " ./deploy.sh ssl-renew"
echo ""