90 lines
3.3 KiB
Django/Jinja
90 lines
3.3 KiB
Django/Jinja
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
# 补全 PATH(cron 等非交互式环境默认 PATH 不含 /usr/sbin 等目录)
|
||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH}"
|
||
|
||
# === 由 CertCenter 生成 ===
|
||
DOMAIN="{{ domain }}"
|
||
BASE_URL="{{ base_url }}"
|
||
TOKEN="{{ token }}"
|
||
CERT_DIR="{{ cert_dir }}"
|
||
CHECK_CMD="{{ check_cmd }}"
|
||
RELOAD_CMD="{{ reload_cmd }}"
|
||
CONFIG_ID="{{ acme_config_id }}"
|
||
# ===========================
|
||
|
||
CONFIG_PARAM=""
|
||
[[ -n "${CONFIG_ID}" ]] && CONFIG_PARAM="&acme_config_id=${CONFIG_ID}"
|
||
|
||
VERSION_URL="${BASE_URL}/api/version?domain=${DOMAIN}${CONFIG_PARAM}"
|
||
FULLCHAIN_URL="${BASE_URL}/api/cert/fullchain?domain=${DOMAIN}${CONFIG_PARAM}"
|
||
PRIVATE_URL="${BASE_URL}/api/cert/private?domain=${DOMAIN}${CONFIG_PARAM}"
|
||
TMP_DIR="/tmp/cert-sync-${DOMAIN//\*/_}"
|
||
VERSION_FILE="${CERT_DIR}/.version"
|
||
|
||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
|
||
|
||
mkdir -p "${CERT_DIR}" "${TMP_DIR}"
|
||
auth=(-H "Authorization: Bearer ${TOKEN}")
|
||
# -k 跳过 TLS 证书验证(CertCenter 初期可能使用自签证书)
|
||
# -S 显示错误信息(配合 -s 不显示进度条)
|
||
curl_opts=(-SfsSL -k --connect-timeout 5 --max-time 30)
|
||
|
||
log "开始同步证书: ${DOMAIN}"
|
||
log "配置目录: ${CERT_DIR}, 配置ID: ${CONFIG_ID:-无}"
|
||
|
||
# 1. 检查版本
|
||
log "检查远程版本..."
|
||
REMOTE=$(curl "${curl_opts[@]}" --max-time 15 "${auth[@]}" "${VERSION_URL}") || {
|
||
log "错误: 无法获取远程版本 (${VERSION_URL})"
|
||
exit 1
|
||
}
|
||
LOCAL=$(cat "${VERSION_FILE}" 2>/dev/null || echo "0")
|
||
log "远程版本: ${REMOTE}, 本地版本: ${LOCAL}"
|
||
if [[ "${REMOTE}" == "${LOCAL}" ]]; then
|
||
log "版本一致,跳过更新"
|
||
exit 0
|
||
fi
|
||
|
||
# 2. 下载证书
|
||
log "版本不一致,开始下载证书..."
|
||
curl "${curl_opts[@]}" "${auth[@]}" "${FULLCHAIN_URL}" -o "${TMP_DIR}/fullchain.pem" || {
|
||
log "错误: 下载 fullchain.pem 失败"
|
||
exit 1
|
||
}
|
||
curl "${curl_opts[@]}" "${auth[@]}" "${PRIVATE_URL}" -o "${TMP_DIR}/private.key" || {
|
||
log "错误: 下载 private.key 失败"
|
||
exit 1
|
||
}
|
||
chmod 600 "${TMP_DIR}/private.key"
|
||
log "证书下载完成"
|
||
|
||
# 3. 备份旧证书
|
||
cp -f "${CERT_DIR}/fullchain.pem" "${CERT_DIR}/fullchain.pem.bak" 2>/dev/null || true
|
||
cp -f "${CERT_DIR}/private.key" "${CERT_DIR}/private.key.bak" 2>/dev/null || true
|
||
log "旧证书已备份"
|
||
|
||
# 4. 原子替换
|
||
mv -f "${TMP_DIR}/fullchain.pem" "${CERT_DIR}/fullchain.pem"
|
||
mv -f "${TMP_DIR}/private.key" "${CERT_DIR}/private.key"
|
||
log "证书文件已替换"
|
||
|
||
# 5. 校验 & 重载 & 上报
|
||
HOSTNAME=$(hostname)
|
||
log "执行校验命令: ${CHECK_CMD}"
|
||
if ${CHECK_CMD}; then
|
||
log "校验通过,执行重载: ${RELOAD_CMD}"
|
||
echo "${REMOTE}" > "${VERSION_FILE}"
|
||
${RELOAD_CMD}
|
||
log "更新成功: ${DOMAIN} -> v${REMOTE}"
|
||
curl "${curl_opts[@]}" "${auth[@]}" -X POST "${BASE_URL}/api/report?domain=${DOMAIN}&status=success&hostname=${HOSTNAME}&message=deployed+v${REMOTE}${CONFIG_PARAM}" 2>/dev/null || true
|
||
else
|
||
log "错误: 校验失败,回滚证书"
|
||
mv -f "${CERT_DIR}/fullchain.pem.bak" "${CERT_DIR}/fullchain.pem" 2>/dev/null || true
|
||
mv -f "${CERT_DIR}/private.key.bak" "${CERT_DIR}/private.key" 2>/dev/null || true
|
||
log "已回滚到旧证书"
|
||
curl "${curl_opts[@]}" "${auth[@]}" -X POST "${BASE_URL}/api/report?domain=${DOMAIN}&status=failed&hostname=${HOSTNAME}&message=check+failed+rolled+back${CONFIG_PARAM}" 2>/dev/null || true
|
||
exit 1
|
||
fi
|