全链路无人值守自动化 SSL¶
起因¶
腾讯云的免费 SSL 证书有效期只有 3 个月,且不支持自动续签,不是很方便(其他云厂商也类似)
解决方案¶
- A:每三个月手动更新一次(免费 + 手动续签 + 手动上传)
- B:使用 Let's Encrypt + acme.sh / Certbot 脚本(免费 + 自动续签 + 自动上传)
- C:购买付费证书(付费 + 自动续签 + 无需上传)
选 B(土豪随意)
结合 acme.sh1 + GitHub Actions,撸了一套 全链路无人值守自动化 SSL 脚本,具备以下特色:
| 功能模块 | 特色说明 |
|---|---|
| 高可靠设计 ✓ | 面向长期稳定运行设计,适合云原生自动化部署 |
| 全自动运维 ✓ | 无需人工干预,实现 SSL 证书生命周期全自动管理 |
| 自动签发 ✓ | 自动调用 Let’s Encrypt 申请证书 |
| 智能续期 ✓ | 仅在证书即将过期时续签,避免无效请求 |
| 云端集成 ✓ | 自动上传证书至腾讯云 SSL 证书管理 |
| CDN 热更新 ✓ | 自动更新 CDN HTTPS 配置并立即生效 |
脚本流程指令¶
脚本下载¶
acme-auto.yml
name: Auto SSL Renew & Deploy (Tencent Cloud)
permissions:
contents: read
actions: write
on:
workflow_dispatch:
schedule:
# Triggered on the 1st & 16th of each month
- cron: '0 0 1,16 * *'
# Prevent concurrent: manual trigger + cron
concurrency:
group: ssl-renew-${{ github.repository }}
cancel-in-progress: false
jobs:
issue_cert:
runs-on: ubuntu-latest
steps:
# -------------------------
# Restore acme state
# -------------------------
- name: Restore acme.sh state
uses: actions/cache@v4
with:
path: ~/.acme.sh
key: acme-state-v1
# -------------------------
# Install acme.sh if needed
# -------------------------
- name: Install acme.sh
env:
ACME_EMAIL: ${{ secrets.ACME_EMAIL }}
run: |
export PATH="$HOME/.acme.sh:$PATH"
if [ -f "$HOME/.acme.sh/acme.sh" ]; then
echo "✅ acme.sh already installed, skipping reinstall."
else
echo "⬇️ Installing acme.sh ..."
curl -fsSL https://get.acme.sh | sh -s email=$ACME_EMAIL
fi
~/.acme.sh/acme.sh --upgrade --auto-upgrade
# -------------------------
# Issue / Renew certificate
# -------------------------
- name: Smart issue / renew
id: renew
env:
DOMAIN: ${{ secrets.DOMAIN }}
DP_Id: ${{ secrets.DP_ID }}
DP_Key: ${{ secrets.DP_KEY }}
run: |
export PATH="$HOME/.acme.sh:$PATH"
CERT_PATH="$HOME/.acme.sh/${DOMAIN}_ecc/fullchain.cer"
OLD_HASH=""
if [ -f "$CERT_PATH" ]; then
OLD_HASH=$(sha256sum "$CERT_PATH" | cut -d' ' -f1)
echo "Old cert hash: $OLD_HASH"
fi
# First issue, if cert not exist
if [ ! -f "$CERT_PATH" ]; then
echo "🎬 First certificate issue..."
~/.acme.sh/acme.sh \
--issue \
-d $DOMAIN \
-d www.$DOMAIN \
--dns dns_dp \
--ecc \
--days 30 \
--server letsencrypt \
--dnssleep 120
# If cert exist, use cron to auto check if all certs need to be renewed, if so → renew, otherwise → skip
else
echo "⤵ Running cron..."
~/.acme.sh/acme.sh --cron
fi
NEW_HASH=$(sha256sum "$CERT_PATH" | cut -d' ' -f1)
echo "New cert hash: $NEW_HASH"
if [ "$OLD_HASH" != "$NEW_HASH" ]; then
echo "renewed=true" >> $GITHUB_OUTPUT
echo "🔔 Certificate updated"
else
echo "renewed=false" >> $GITHUB_OUTPUT
echo "🔕 Certificate unchanged"
fi
# -------------------------
# Prepare certificate
# -------------------------
- name: Prepare cert files
if: steps.renew.outputs.renewed == 'true'
env:
DOMAIN: ${{ secrets.DOMAIN }}
run: |
mkdir -p certs
cp ~/.acme.sh/${DOMAIN}_ecc/${DOMAIN}.key certs/
cp ~/.acme.sh/${DOMAIN}_ecc/fullchain.cer certs/
# -------------------------
# Deploy to Tencent Cloud
# -------------------------
- name: Upload cert and update CDN
if: steps.renew.outputs.renewed == 'true'
env:
QCLOUD_SECRET_ID: ${{ secrets.QCLOUD_SECRET_ID }}
QCLOUD_SECRET_KEY: ${{ secrets.QCLOUD_SECRET_KEY }}
DOMAIN: ${{ secrets.DOMAIN }}
run: |
pip install --prefer-binary --disable-pip-version-check tencentcloud-sdk-python==3.1.50
python3 <<'EOF'
import os, json
from tencentcloud.common import credential
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.ssl.v20191205 import ssl_client, models
from tencentcloud.cdn.v20180606 import cdn_client, models as cdn_models
# ---------- Upload cert ----------
cred = credential.Credential(
os.getenv("QCLOUD_SECRET_ID"),
os.getenv("QCLOUD_SECRET_KEY")
)
ssl = ssl_client.SslClient(cred, "ap-guangzhou")
domain = os.getenv("DOMAIN")
with open("certs/fullchain.cer") as f:
pub = f.read()
with open(f"certs/{domain}.key") as f:
key = f.read()
upload_req = models.UploadCertificateRequest()
upload_req.from_json_string(json.dumps({
"CertificatePublicKey": pub,
"CertificatePrivateKey": key,
"Alias": f"AutoSSL-{domain}"
}))
upload_resp = ssl.UploadCertificate(upload_req)
cert_id = json.loads(upload_resp.to_json_string())["CertificateId"]
print("✅ Uploaded new cert: ", cert_id)
# ---------- Update CDN ----------
domains = [domain, f"www.{domain}"]
cdn = cdn_client.CdnClient(cred, "ap-guangzhou")
for d in domains:
try:
print("🔧 Updating CDN for domain: ", d)
req = cdn_models.UpdateDomainConfigRequest()
req.from_json_string(json.dumps({
"Domain": d,
"Https": {
"Switch": "on",
"CertInfo": {"CertId": cert_id}
}
}))
cdn.UpdateDomainConfig(req)
print("✅ CDN updated: ", d)
except Exception as e:
print("❌ CDN update failed: ", d, e)
raise
EOF
# -------------------------
# Result
# -------------------------
- name: Result
if: always()
run: |
if [ "${{ steps.renew.outputs.renewed }}" = "true" ]; then
echo "✅ SSL renewed and deployed."
else
echo "🔹 No renewal needed."
fi
执行流程¶
脚本执行流程如下:
trigger workflow
│
▼
restore ~/.acme.sh state
│
▼
install acme.sh if needed
│
▼
if cert_not_exist
issue
else
cron (renew or skip)
│
▼
check cert hash
│
├── unchanged ── ▶︎ exit
│
└── changed
│
▼
upload cert
│
▼
update CDN
签发指令¶
| 命令 | 作用 | 是否需要验证参数 | 使用场景 |
|---|---|---|---|
| issue | 申请新证书(也能续期) | 需要 | 第一次申请 / 重新签发 |
| renew | 只续期已有证书 | 不需要 | 已有证书续期 |
| cron | 自动检测并续期所有证书 | 智能续期,自动做 expiry check 和 renew |
配置使用¶
脚本以腾讯云为样例,如需使用其他云厂商,只需把脚本中的步骤 Deploy to Tencent Cloud 代码替换为对应的版本
DNSPod Token 配置¶
腾讯云 → DNSPod 「账号中心 → API密钥」,选择 DNSPod Token → 创建密钥,记下 ID 和 Token
API 密钥配置¶
腾讯云 → DNSPod 「账号中心 → API密钥」,选择 腾讯云 API 密钥 → 新建密钥,记下 SecretId 和 SecretKey
自动化配置¶
可使用 GitHub Actions 工作流来触发定时任务完成证书的自动签发、上传与更新(省去云服务器)
创建 GitHub 仓库¶
在 GitHub 创建一个仓库
设置仓库 secrets¶
仓库 → Settings → Secrets and variables → Actions → New repository secret,分别添加:
| Secret 名称 | 内容 | 来源 |
|---|---|---|
| DP_Id | DNSPod ID | DNSPod 控制台 |
| DP_Key | DNSPod Token | DNSPod 控制台 |
| QCLOUD_SECRET_ID | 腾讯云 SecretId | 腾讯云访问管理 |
| QCLOUD_SECRET_KEY | 腾讯云 SecretKey | 腾讯云访问管理 |
| DOMAIN | 你的备案域名 | 备案域名 |
| ACME_EMAIL | 随意一个真实邮箱 | 手动填写 |
创建工作流¶
仓库首页 → Add file → Create a new file: .github/workflows/acme-auto.yml,内容见 脚本下载
运行与测试¶
仓库 → Actions → 选中左侧 Auto SSL Renew & Deploy → 点击右侧 Run workflow,等待运行完成,查看腾讯云:
-
acme.sh 是一个用于 SSL / TLS 证书自动化的纯 Unix shell 脚本,实现了 acme 协议,可以从 ZeroSSL,Let's Encrypt 等 CA 生成免费的证书 ↩