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

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
Cloudbric
Cloudbric
N
News and Events Feed by Topic
S
Secure Thoughts
Vercel News
Vercel News
S
Security @ Cisco Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
Hacker News: Ask HN
Hacker News: Ask HN
博客园 - 聂微东
WordPress大学
WordPress大学
Google Online Security Blog
Google Online Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
PCI Perspectives
PCI Perspectives
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
Blog — PlanetScale
Blog — PlanetScale
Apple Machine Learning Research
Apple Machine Learning Research
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
博客园 - 司徒正美
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
A
About on SuperTechFans
P
Proofpoint News Feed
大猫的无限游戏
大猫的无限游戏
V
V2EX
I
Intezer
H
Hacker News: Front Page
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
Lohrmann on Cybersecurity
F
Fortinet All Blogs
Schneier on Security
Schneier on Security
博客园 - 叶小钗
The Cloudflare Blog
月光博客
月光博客
W
WeLiveSecurity
T
Tenable Blog
P
Proofpoint News Feed
aimingoo的专栏
aimingoo的专栏
Help Net Security
Help Net Security
L
LangChain Blog
C
CERT Recently Published Vulnerability Notes
T
The Exploit Database - CXSecurity.com
美团技术团队
B
Blog

oldj's blog

挖井 - 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

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