惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
S
SegmentFault 最新的问题
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
美团技术团队
爱范儿
爱范儿
博客园 - 三生石上(FineUI控件)
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
F
Fortinet All Blogs
TaoSecurity Blog
TaoSecurity Blog
D
Docker
C
Cybersecurity and Infrastructure Security Agency CISA
K
Kaspersky official blog
宝玉的分享
宝玉的分享
腾讯CDC
Google Online Security Blog
Google Online Security Blog
Recorded Future
Recorded Future
T
The Exploit Database - CXSecurity.com
T
The Blog of Author Tim Ferriss
V
V2EX
S
Securelist
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
CERT Recently Published Vulnerability Notes
A
Arctic Wolf
Scott Helme
Scott Helme
L
LINUX DO - 热门话题
Y
Y Combinator Blog
P
Proofpoint News Feed
T
Tor Project blog
AWS News Blog
AWS News Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Last Watchdog
The Last Watchdog
博客园 - 聂微东
T
Threat Research - Cisco Blogs
B
Blog
Attack and Defense Labs
Attack and Defense Labs
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
N
News and Events Feed by Topic
博客园 - 司徒正美
H
Help Net Security
C
Cisco Blogs
C
Check Point Blog
S
Secure Thoughts

oldj's blog

SwitchHosts 5.0 - oldj's blog 开源本站博客系统 - Publa - oldj's blog 当写作软件遇上 AI:Scrivener 论坛上一场持续三年的产品哲学之争 - oldj's blog AB 工作法 - oldj's blog 使用 uv 管理 Python 依赖 富文本框架体验 - oldj's blog 使用函数计算运行定时任务 - oldj's blog 使用 Dokploy 部署网站服务 - oldj's blog Electron 中的 Kiosk 窗口 - oldj's blog 使用 acme.sh 申请 SSL 证书 处理苹果平台的 CONSUMPTION_REQUEST 消息 - oldj's blog 在 Flutter 中适配 1Password 登录
使用 GitHub Action 自动更新 SSL 证书
2026-04-10 · via oldj's blog

之前写过一篇使用 acme.sh 申请 SSL 证书的帖子,最近把这个流程放到了 GitHub Action 上自动化完成,在这儿记录一下要点。

GitHub Action 是托管在 GitHub 上的自动化服务,免费账户的私有仓库每月有 2000 分钟的运行额度,可以用来做很多运维、CI 相关的工作。

要使用 GitHub Action,只需在项目的根目录下的 .github/workflows 中创建一个 .yml 文件即可,比如新建一个 .github/workflows/renew-ssl.yml

基本设置

这个 action 的基本结构类似下面这样:

name: Renew SSL Certificate

on:
  schedule:
    - cron: '0 20 * * 0' # 每周一 UTC 20:00(北京时间 04:00)
  workflow_dispatch: # 支持手动触发

permissions:
  contents: read

jobs:
  renew-cert:
    runs-on: ubuntu-latest
    steps:
      - name: Task name
        run: |
          YOUR SCRIPT

代码很直白,这儿就不多解释了,下面继续看最主要的任务步骤(steps)。

第一步:安装 acme.sh

我们要使用 acme.sh 来申请 SSL 证书,因此第一步需要安装 acme.sh。在以上 YML 文件的 steps 部分,添加以下代码:

      - name: Install acme.sh
        run: |
          curl https://get.acme.sh | sh -s email=${{ secrets.ACME_EMAIL }}

第二步:申请证书

接下来,则是使用 acme.sh 申请域名证书。这儿我们申请了一个泛域名证书:

      - name: Issue or Renew Certificate
        id: issue-cert
        env:
          Ali_Key: ${{ secrets.ALI_KEY }}
          Ali_Secret: ${{ secrets.ALI_SECRET }}
        run: |
          ~/.acme.sh/acme.sh --issue \
            -d oldj.net \
            -d '*.oldj.net' \
            --dns dns_ali \
            --keylength ec-256

你需要将上面代码中的 oldj.net 替换为你自己的域名。申请泛域名证书时,需要同时传入 your-domain.com*.your-domain.com

我的域名是在阿里云上,因此 --dns 参数设置的是 dns_ali 。如果你的域名在其他注册商那里,需要把这个参数改为对应的值,具体可查看 acme.sh 的文档

注意,这儿用到了环境变量 secrets.ALI_KEYsecrets.ALI_SECRET ,这两个值需要你去阿里云后台生成,然后在 GitHub 项目仓库的后台添加。

阿里云后台具体位置是 AccessKey 管理页面,建议创建一个子账号,只授予 AliyunDNSFullAccess 权限即可。

GitHub 添加 Secrets 的位置是:仓库页面 → Settings → Secrets and variables → Actions → New repository secret。

第三步:上传到服务器

证书需要上传到服务器才能发挥作用,在这儿我们使用 SSH 的方式连接服务器。为了能顺利连接服务器,需要你准备一对 SSH 密钥,将公钥上传到服务器的 ~./ssh/authorized_keys 下,同时像上面那样将密钥添加到 GitHub Secrets 中,名称是 SSH_PRIVATE_KEY

接下来,再在 renew-ssl.yml 中继续添加以下代码:

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          echo "StrictHostKeyChecking no" > ~/.ssh/config

再接下来是正式的上传步骤:

      - name: Deploy to Server-001
        if: steps.issue-cert.outcome == 'success'
        continue-on-error: true
        run: |
          scp ~/.acme.sh/oldj.net_ecc/fullchain.cer \
              ~/.acme.sh/oldj.net_ecc/oldj.net.key \
              ${{ secrets.SERVER_USER_GATEWAY }}@${{ secrets.SERVER_HOST_GATEWAY }}:${{ secrets.CERT_REMOTE_DIR }}/
          ssh ${{ secrets.SERVER_USER_GATEWAY }}@${{ secrets.SERVER_HOST_GATEWAY }} "sudo nginx -s reload"

注意 SERVER_USER_GATEWAYSERVER_HOST_GATEWAY 分别是你的服务器的用户名和地址(IP 或域名),CERT_REMOTE_DIR 是你准备把证书上传到的服务器文件夹,需要先创建对应的文件夹。这三个变量也像上面一样添加到 GitHub Secrets 中。

步骤的第一行 if: steps.issue-cert.outcome == 'success' 是要确保前面申请证书成功才会执行后续的操作。

最后一行的 sudo nginx -s reload 作用是让服务器上的 Nginx 重新加载配置以及证书,没有这一句,即使证书文件已经上传了,Nginx 仍会继续使用老证书,直到下一次重载或重启。

到这儿,证书的申请 → 上传 → 应用流程就基本完整了,如果你有多台服务器在使用这个证书,可以继续添加上传任务。

更新腾讯云上的证书

我在使用腾讯云 CDN,所以也研究了一下如何自动更新腾讯云的上传证书。其他云服务商应该也有类似的接口。

另外,腾讯云的 EdgeOne 已经可以自动申请和更新免费证书了,无需再自己上传,不过传统的 CDN 服务目前似乎还需要自己管理免费证书。

相关代码如下:

      - name: Upload to Tencent Cloud
        if: steps.issue-cert.outcome == 'success'
        continue-on-error: true
        run: |
          pip install tccli

          # 配置腾讯云 CLI
          tccli configure set secretId ${{ secrets.TENCENT_SECRET_ID }}
          tccli configure set secretKey ${{ secrets.TENCENT_SECRET_KEY }}
          tccli configure set region ap-guangzhou

          CERT=$(cat ~/.acme.sh/oldj.net_ecc/fullchain.cer | base64 -w 0)
          KEY=$(cat ~/.acme.sh/oldj.net_ecc/oldj.net.key | base64 -w 0)

          # 上传新证书
          RESULT=$(tccli ssl UploadCertificate \
            --CertificatePublicKey "$(cat ~/.acme.sh/oldj.net_ecc/fullchain.cer)" \
            --CertificatePrivateKey "$(cat ~/.acme.sh/oldj.net_ecc/oldj.net.key)" \
            --Alias "oldj.net-wildcard-$(date +%Y%m%d)")

          NEW_CERT_ID=$(echo "$RESULT" | jq -r '.CertificateId')
          echo "New certificate ID: $NEW_CERT_ID"

          # 如果有旧证书 ID,自动替换关联资源
          if [ -n "${{ secrets.TENCENT_OLD_CERT_ID }}" ]; then
            tccli ssl UpdateCertificateInstance \
              --OldCertificateId ${{ secrets.TENCENT_OLD_CERT_ID }} \
              --ResourceTypes '["cdn"]' \
              --CertificateId "$NEW_CERT_ID"
            echo "Associated resources updated from ${{ secrets.TENCENT_OLD_CERT_ID }} to $NEW_CERT_ID"
          fi

          # 自动更新 Secret 中的证书 ID,供下次续期使用
          echo "$NEW_CERT_ID" | gh secret set TENCENT_OLD_CERT_ID
          echo "TENCENT_OLD_CERT_ID updated to: $NEW_CERT_ID"
        env:
          GH_TOKEN: ${{ secrets.GH_PAT }}
          GH_REPO: ${{ github.repository }}

请注意将代码中的证书路径(本例中是 oldj.net_ecc)替换为你的实际路径。

这段代码中使用了腾讯云的 tccli 来实现证书上传和更新工作,和上面类似,你需要先设置一些 Secrets:

  • TENCENT_SECRET_ID 腾讯云 API SecretId

  • TENCENT_SECRET_KEY 腾讯云 API SecretKey

  • TENCENT_OLD_CERT_ID 当前正在使用的证书 ID(如果仅上传,不需要自动替换老证书,可不填此项)

其中 TENCENT_SECRET_*可以前往腾讯云 API 密钥管理页面生成,建议创建子账号,授予 QcloudSSLFullAccess 权限,以及你需要更新的资源的权限,比如 QcloudCDNFullAccess

上面的 --ResourceTypes 用于指定要更新的资源,这儿我只写了 CDN,你可以根据需要调整。

首次运行前,需要你先设置一下要更新的证书 ID。由于每次自动更新证书之后,证书 ID 都会变化,为了实现完全自动化,这儿添加了一个 GH_PAT 参数,用于记录你的 GitHub Personal Access Token

这个 GitHub Personal Access Token 的创建方式如下:

  1. 进入 GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens

  2. 选择对应的组织以及仓库,权限勾选 Secrets → Read and Write

  3. 生成后,将 Token 存入仓库 Secret 中,名称是 GH_PAT

这样自动更新腾讯云上的证书的流程便也自动化了。