全链路无人值守自动化 SSL
起因
腾讯云的免费 SSL 证书有效期只有 3 个月,且不支持自动续签,不是很方便(其他云厂商也类似)
解决方案
选 B(土豪随意)
结合 acme.sh + 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
| ID:668000
Token:1md3646b5c4d7ba5969c48e521629ird
|
API 密钥配置
腾讯云 → DNSPod 「账号中心 → API密钥」,选择 腾讯云 API 密钥 → 新建密钥,记下 SecretId 和 SecretKey
| SecretId:A******441jEXeZJp9LL8kA22Bl9k8******
SecretKey:B*****FG4e1B7u52LSbcTHzdDl7******
|
自动化配置
可使用 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,等待运行完成,查看腾讯云: