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

推荐订阅源

阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
雷峰网
雷峰网
U
Unit 42
Y
Y Combinator Blog
I
InfoQ
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
量子位
Microsoft Security Blog
Microsoft Security Blog
B
Blog
The Cloudflare Blog
F
Fortinet All Blogs
Google DeepMind News
Google DeepMind News
MyScale Blog
MyScale Blog
C
Check Point Blog
S
SegmentFault 最新的问题
爱范儿
爱范儿
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
罗磊的独立博客
T
Tailwind CSS Blog

kshao-blog-前端知识记录

win or mac clash 无 TUN 让 Antigravity、Chrome 强制 proxy(解决 Antigravity 无法加载选择 model、自动更新无法登录、跳转) 【大杂烩】在 pnpm 中直接修改 node_modules(.pnpm) 中的依赖项,项目中持久化 - pnpm 中的依赖处理、幽灵依赖、寻址规则等 在 html 中直接使用 Esm、Jsx 脚本快速调试和使用 React@19 和 Vue@3 源码,解决 React19 UMD 构建等问题 一键在本地批量检测并升级更新 package.json 中的模块依赖,ncu(npm-check-updates)在 npm、pnpm 或 workspace 项目中的使用教程 解决 Mac Docker Desktop 中启动出现的问题合集 通过阿里云、腾讯云无服务器搭建自定义的企业域名邮箱,实现在 QQ邮箱 收发等功能(附腾讯 SMTP 和 IMAP) 解决使用代理(clash 等)进行 SSH 连接(如 Github ssh key clone/push)出现 kex_exchange_identification 错误 静态文件资源 cdnjs, jsdelivr 抖音字节国内快速 CDN 镜像推荐【2025】- 仍在使用 bootcdn 和 staticfile CDN 请注意验证资源的完整性(SRI) pnpm monorepo 中管理依赖的最佳实践,与 Catalogs(目录)协议的使用(monorepo 中统一版本管理) Web 安全中的 Secure Contexts(安全上下文)- 解决在本地中使用 clipboard 或 Crypto 等 API 限制或关闭上下文限制 使用 serve 配合 openssl 或 mkcert 创建本地自签名可信任的证书 - 创建本地 TLS\SSL https 协议服务 【CSS】解决在 flex 容器中使用 align-content 或 justify-content 属性 center 居中时的溢出滚动和截断问题 - 理解 safe 关键字 在线工具 - 一键获取下载抖音无水印视频、抖音去水印解析工具、下载抖音无水印高清图集【2025 最新】 【React Router】v6 data router 在非组件(或工具方法)中如何优雅的跳转路由 【React】为什么路由跳转时页面滚动高度不会被重置(保留上个页面高度)?理解 history scrollRestoration 的场景与使用,以及如何使用 React Router 重置和跳转前保留滚动高度 【React】在本地 Html 中快速 debug(调试)React 源码 【React】结合源码和 EventLoop 分析 - 为什么 useLayoutEffect 会阻止 DOM 重绘(而 useEffect 闪烁)?为什么其内部 useState 会“同步”执行? React Compiler - 解放在函数中编程时的性能焦虑(React Conf 2024)附 Next 在线演示 在 github actions 中获取时间,并转换为中国标准时间(中国时区) 【npm】npm ci - npm clean install,在 CI、CD 中保持构建的一致性和可重复性 eslint 9.x 升级或使用指南,eslint.config.js 配置,包含 react、typescript、prettier 等常用配置升级迁移 使用 Spicetify 自定义 Spotify - 歌词翻译、全屏展示、主题替换 在 node 中快速代理请求(Proxy),解决跨域或请求转发问题 - http-proxy-middleware 修改请求体和返回 在 Hexo 中使用 AI(Gemini、deepseek、Azure)生成文章摘要,支持自定义模板。hexo-ai-summaries 插件文档(默认适配主题 Butterfly) 解决在 webstorm 或 idea 等 jetbrains 工具中遇到 Git 无法 force push,或 force push 灰色禁用无法点击(protected branches) hexo-seo-submit,Hexo 博客 SEO 优化插件 - 每日定时自动或手动提交链接至百度、Bing、Google,支持 Github Actions 和 Coding Jenkins 等CI(Hexo 插件编写) 一文吃透 pnpm 如何使用 workspace 构建 monorepo,与 npm、yarn 的用法对比(pnpm 9.x 内部安装依赖问题 link-workspace-packages) 【Node】Corepack - 解决 pnpm 或 yarn 的多版本管理、解决本地版本与 packageManager 中的版本一致性问题 解决 npm、cnpm 或 pnpm install 遇到 certificate has expired (证书已过期) npm、yarn、pnpm 设置最新国内镜像源(附官方镜像源和最新阿里源),以及 nrm 的使用教程【2025】
利用 Github Actions 和 Acme 自动申请、更新和部署至阿里云...
kshao · 2024-09-10 · via kshao-blog-前端知识记录

早在去年年底和今年年初,各大证书厂商都陆续限制了免费 SSL 证书的有效期,从 12 个月缩短至 3 个月。时间将至,来看本文的兄弟们证书是否快要过期了?哈哈~

ACME: Automatic Certificate Management Environment(自动证书管理环境),是一种用于自动化管理和获取 SSL\TLS 证书的协议

ACME 提供了一种标准化的方式,使能够自动请求、验证和获取证书,无需人工干预。完成标准化的获取证书流程需要 ACME 客户端与 ACME 服务端进行通信,常见的 ACME 的客户端有:acme.shcertbot

本文将使用 ACME 协议的 acme.sh 客户端脚本和支持 ACME 服务端的 CA Let’s Encrypt 实现自动证书管理,并自动部署至腾讯云和阿里云 CDN。

acme.sh

主要实现逻辑依靠 acme.sh 的证书申请 和 dns 验证。

相关命令

帮助理解 acme 脚本,方便后续自定义,若无需求可忽略。(actionsubuntu 环境,理论其他环境逻辑同。若需要自定义,建议先阅读文档)

安装 acme.sh

curl  https://get.acme.sh | sh -s email=$EMAIL

执行完上述命令后,会创建并复制 acme 脚本至 ~/.acme.sh/actions/home/runner/.acme.sh),后续安装的证书默认也会在此文件夹下。

email 参数为申请证书使用的邮箱,例如 Let’s EncryptACME 服务端需要邮箱注册账户、提供邮件提醒等。

生成证书 - DNSpod

  1. 使用 DNSpod api--dns dns_dp)进行自动域名 TXT 解析,验证域名所有权。
  2. DNSpod Token 中获取的 ID(对应 DP_Id) 和 Token(对应 DP_Key
  3. 使用 证书 CAletsencrypt --server letsencrypt
  4. -dksh7.com*.ksh7.com 为多个域名申请证书(或泛域名证书)
export DP_Id="1234"

export DP_Key="sADDsdasdgdsf"

acme.sh --issue --server letsencrypt --dns dns_dp -d ksh7.com -d *.ksh7.com

更换 CA

acme.sh 默认使用 ZeroSSL CA,命令中不指定 server 即可,若需要更换其他 CA 可参考 Server 描述。

CA 之间的对比

创建 DNSpod Token

进入 DNSpod Token 页面(点击链接进入即可,或者右上角点击 API 密钥),选择 创建密钥,获得的 IDToken 分别对应 DP_IdDP_Key

[{"url":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnn096gjdj30go0jcdif.webp?imageMogr2/thumbnail/!50p","dataset":{"originPic":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnn096gjdj30go0jcdif.webp","thumbnail":""}},{"url":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnmshmwllj30z60redm3.webp?imageMogr2/thumbnail/!50p","dataset":{"originPic":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnmshmwllj30z60redm3.webp","thumbnail":""}}]

证书验证成功后,会自动添加和删除对应的 TXT 记录,配置了几个域名就会有几条记录,可在 DNSpod 操作记录 中查看。

生成证书 - 阿里云

export Ali_Key="<key>"
export Ali_Secret="<secret>"

acme.sh --issue --dns dns_ali -d ksh7.com -d *.ksh7.com --server letsencrypt

使用主账号的 AccessKey

进入 访问凭证管理 或右上角选择 AccessKey 管理,创建 AccessKey 或者使用之前的 AccessKey 即可,AccessKey IDAccessKey Secret 分别对应 Ali_Key Ali_Secret。简单暴力,但需要注意权限。

[{"url":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnobahaorj30hs0fkn1j.webp?imageMogr2/thumbnail/!50p","dataset":{"originPic":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnobahaorj30hs0fkn1j.webp","thumbnail":""}},{"url":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnobak1vfj31tq0konjf.webp?imageMogr2/thumbnail/!50p","dataset":{"originPic":"https://static.ksh7.com/post/ssl-cert-auto-deploy/0085UwQ9ly1htnobak1vfj31tq0konjf.webp","thumbnail":""}}]

使用子账号的 AccessKey

子账号创建流程参考 CertCloud
更多关于 DNS API 的说明或其他平台(如 cloudflareaws 等)请参考 How to use DNS API

安装证书

申请完证书后,证书默认存放在 ~/.acme.sh/ 文件夹下(可以在 --issue 时 指定 --cert-home,或者设置环境变量 export CERT_HOME 更改路径),若你需要将证书部署至服务器的指定文件下,如 NginxApache 可使用 --installcert 命令

acme.sh --installcert -d ksh7.com -d *.ksh7.com --key-file /mycertify/ssl/ksh7.com.key --fullchain-file /mycertify/ssl/ksh7.com.cer

部署证书

使用 Actions 将证书部署至 CDN 平台

账户需要权限:QcloudCDNFullAccess、QcloudSSLFullAccess

deploy-to-tencent-cdn:
  name: Deploy certificate to Tencent Cloud CDN
  runs-on: ubuntu-latest
  # 需要在申请完证书后进行部署任务
  needs: issue

  steps:
    - name: Check out
      uses: actions/checkout@v2
      with:
        ref: ${{ github.ref }}

    - uses: renbaoshuo/deploy-certificate-to-tencentcloud@v2
      with:
        secret-id: ${{ secrets.SECRETID }}
        secret-key: ${{ secrets.SECRETKEY }}
        # cert file like ksh7.com.cer
        # 证书路径
        fullchain-file: ${{ env.CERTS_OUTPUT_DIRECTORY }}/${{ env.FILE_FULLCHAIN }}
        # 私钥路径
        key-file: ${{ env.CERTS_OUTPUT_DIRECTORY }}/${{ env.FILE_KEY }}
        cdn-domains: |
          ksh7.com

阿里云 CDN

AccessKey 的配置,可参考 生成证书-阿里云

deploy-to-aliyun:
  name: Deploy Certificate to Aliyun
  runs-on: ubuntu-latest
  needs: issue-ssl-certificate

  steps:
    - name: Checkout
      uses: actions/checkout@v2

    # 上传证书
    - name: Deploy certificate to aliyun
      uses: Menci/deploy-certificate-to-aliyun@beta-v1
      with:
        access-key-id: ${{ secrets.ALIYUN_ACCESS_KEY_ID }}
        access-key-secret: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }}
        fullchain-file: ${{ env.CERTS_OUTPUT_DIRECTORY }}/${{ env.FILE_FULLCHAIN }}
        key-file: ${{ env.CERTS_OUTPUT_DIRECTORY }}/${{ env.FILE_KEY }}
        certificate-name: example.com
        cdn-domains: |
          ksh7.com

申请证书

在使用 Actions 之前,可以通过 acme.sh 脚本将相关流程本地测试一次,可有效减少未知问题。

需要注意!证书与密钥文件需要妥善保管,请勿使用 公开仓库储存

Menci/acme

使用 Actions 插件来快速开启证书申请流程。参数详解直达

jobs:
  issue-ssl-certificate:
    name: Issue SSL certificate
    runs-on: ubuntu-latest
    steps:
      - uses: Menci/acme@v2
        with:
          version: 3.0.2

          # Register your account and try issue a certificate with DNS API mode
          # Then fill with the output of `tar cz ca account.conf | base64 -w0` running in your `~/.acme.sh`
          account-tar: ${{ secrets.ACME_SH_ACCOUNT_TAR }}

          domains: example.com example.net example.org example.edu
          domains-file: ''
          append-wildcard: true

          arguments: --dns dns_cf --challenge-alias example.com
          arguments-file: ''

          output-fullchain: output/fullchain.pem
          output-key: output/key.pem
          output-pfx: output/certificate.pfx
          output-pfx-password: qwq

          # uninstall: true # Uninstall acme.sh after this action by default

自定义 acme.shActions

支持自动申请证书、以及每天检查证书是否过期,并自动重新申请证书(时间小于30天)。申请 ECCRSA泛域名证书。

使用 腾讯 CDN 的完整案例,详细文档和参数请转至:auto-ssl-cert

name: DnsPod SSL Certificates

on:
  schedule: # execute every 24 hours
    - cron: '35 6 * * *'
  workflow_dispatch:

env:
  ACME: /home/runner/.acme.sh/acme.sh
  DP_ID: ${{ secrets.DP_ID }}
  DP_KEY: ${{ secrets.DP_KEY }}
  EMAIL: ${{ secrets.EMAIL }}

jobs:
  issue-cert:
    runs-on: ubuntu-latest
    outputs:
      certificate_updated: ${{ steps.check_certificate.outputs.certificate_updated }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install & Configure acme.sh
        run: |
          curl  https://get.acme.sh | sh -s email=$EMAIL
      - name: Issue & Deploy Certificates
        id: check_certificate
        run: |
          export DP_Id=$DP_ID
          export DP_Key=$DP_KEY

          git config --global user.email $EMAIL
          git config --global user.name acme

          # 如果想要其他证书发行机构,可以把 acme.sh 的ca目录拷贝到 repo 的 ca目录
          # mkdir -p /home/runner/.acme.sh/ca/
          # cp -r ca/* /home/runner/.acme.sh/ca/          

          certificate_updated=false

          check_certificate_validity() {
            cert_path=$1
            if [ -f "$cert_path" ]; then
              if openssl x509 -checkend $(( 30 * 86400 )) -noout -in "$cert_path"; then
                echo "Certificate at $cert_path is valid for more than 30 days, skipping..."
                return 0
              else
                return 1
              fi
            else
              return 1
            fi
          }

          issue_and_install_certificate() {
            domain=$1
            cert_type=$2 # "EC" or "RSA"
            acme_server=$3 # default choose "letsencrypt" 其他 CA 请参考 https://github.com/acmesh-official/acme.sh/wiki/CA
            keylength=$4 # empty for EC, "3072" for RSA

            cert_path="./ssl/$domain"
            [ "$cert_type" = "RSA" ] && cert_path="$cert_path/rsa"
            cert_file="$cert_path/$domain.cer"
            key_file="$cert_path/$domain.key"
            
            # Issue certificate          
            issue_status=0
            $ACME --issue --server $acme_server --debug --dns dns_dp -d "$domain" -d "*.$domain" ${keylength:+--keylength $keylength}|| issue_status=$?
            if [ $issue_status -ne 0 ]; then
              echo "Failed to issue $cert_type certificate for $domain, skipping..."
              return
            fi

            # Install certificate          
            install_status=0
            $ACME --installcert -d "$domain" --key-file "$key_file" --fullchain-file "$cert_file" || install_status=$?
            if [ $install_status -ne 0 ]; then
              echo "Failed to install $cert_type certificate for $domain, skipping..."
              return
            fi

            certificate_updated=true
            TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
            git add $cert_path/
            git commit -m "Update $cert_type certificate files for $domain at $TIMESTAMP"
          }

          while IFS= read -r domain || [ -n "$domain" ]; do
            mkdir -p "./ssl/$domain/rsa"

            # Check and issue/install EC certificate
            if ! check_certificate_validity "./ssl/$domain/$domain.cer"; then
              issue_and_install_certificate "$domain" "EC" "letsencrypt" ""
            fi

            # Check and issue/install RSA certificate
            if ! check_certificate_validity "./ssl/$domain/rsa/$domain.cer"; then
              issue_and_install_certificate "$domain" "RSA" "letsencrypt" "3072"
            fi

          done < dnspod_domains_list.txt

          echo "certificate_updated=$certificate_updated" >> $GITHUB_OUTPUT

      - name: Push changes
        if: steps.check_certificate.outputs.certificate_updated == 'true'
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

  deploy-to-tencent-cdn:
    name: Deploy certificate to Tencent Cloud CDN
    runs-on: ubuntu-latest
    needs: issue-cert
    if: needs.build.outputs.certificate_updated == 'true'

    steps:
      - name: Check out
        uses: actions/checkout@v2
        with:
          ref: ${{ github.ref }}

      - uses: renbaoshuo/deploy-certificate-to-tencentcloud@v2
        with:
          secret-id: ${{ secrets.SECRETID }}
          secret-key: ${{ secrets.SECRETKEY }}
          # cert file like ksh7.com.cer
          fullchain-file: ${{ env.CERTS_OUTPUT_DIRECTORY }}/${{ env.FILE_FULLCHAIN }}
          key-file: ${{ env.CERTS_OUTPUT_DIRECTORY }}/${{ env.FILE_KEY }}
          cdn-domains: |
            ksh7.com

其他推荐

httpsok

https://httpsok.com/doc/

相关链接

申请证书与部署证书参考: auto-ssl-cert
申请证书参考:auto-ssl
部署腾讯 CDN Action deploy-certificate-to-tencentcloud
部署腾讯 CDN Action 功能更完善 qcloud-ssl-cdn