使用 GitHub Actions 自动申请 SSL 证书并推送到仓库

使用 GitHub Actions 自动申请 SSL 证书并推送到仓库

在现代 Web 开发中,SSL 证书的管理和自动化是确保网站安全性的重要一环。本文将介绍如何使用 GitHub Actions 自动申请 SSL 证书,并将证书推送到 GitHub 仓库中的指定分支。我们将使用 Let’s Encrypt 和 GTS 作为证书提供者,并通过 Cloudflare 的 DNS API 进行域名验证。确保您具有相应的权限和配置来顺利执行该工作流程。请注意,由于证书会上传到仓库,请将仓库设置为私有

前提条件

配置 Secrets:在仓库的 Settings > Secrets and Variables > Actions 中,配置以下 Secrets:

  • DOMAIN_LIST:需要申请的域名列表,格式为 <域名>,<DNS服务商>,[ACME服务器],[Profile名称],[证书有效期],[证书剩余时间触发续订],每个域名会申请单独的泛域名证书。配置示例:

    1
    2
    3
    example.com,cloudflare,,tlsserver
    example.com,cloudflare,zerossl,,,15
    example.com,cloudflare,google,,30,15
  • DNS_API:DNS API 配置,一行一个,例如:

    1
    2
    export GANDI_API_KEY="xxx"
    export CF_DNS_API_TOKEN="xxx"

    支持的 DNS 服务商列表请参考:lego DNS Providers

  • LEGO_ACCOUNT_TAR:Base64 编码的 lego 配置信息,在本地成功签发后运行 tar cz .lego/accounts | base64 -w0 获取。

    本地签发命令示例:

    1
    2
    3
    4
    CF_DNS_API_TOKEN="***" ./lego --accept-tos \
    --email ssl@example.com --dns cloudflare \
    --server https://dv.acme-v02.api.pki.goog/directory \
    --eab --kid *** --hmac *** --domains example.com run
  • CERT_EMAIL:注册 ACME 的电子邮件地址。

编写 GitHub Actions 配置文件

在仓库根目录下创建 .github/workflows/acme.yml 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
name: ssl automation

on:
workflow_dispatch:
schedule:
- cron: '0 16 * * *'

permissions:
contents: write
packages: read

env:
TZ: Asia/Shanghai

jobs:
ssl-cert-issuer:
runs-on: ubuntu-latest
steps:
- name: 🚚 Checkout Repository
uses: actions/checkout@v4
with:
ref: main
path: .lego

- name: 📦 Install latest lego
shell: bash
run: |
RELEASE_INFO=$(curl -s https://api.github.com/repos/go-acme/lego/releases/latest)
TAG_NAME=$(echo "$RELEASE_INFO" | jq -r '.tag_name')
DOWNLOAD_URL=$(echo "$RELEASE_INFO" | jq -r ".assets[] | select(.name == \"lego_${TAG_NAME}_linux_amd64.tar.gz\") | .browser_download_url")
curl -sLO "$DOWNLOAD_URL"
tar -xzf lego_*

- name: 🔐 Extract lego account
shell: bash
run: |
echo "${{ secrets.LEGO_ACCOUNT_TAR }}" | base64 -d | tar -C . -xz

- name: 📄 Issue or Renew Certificates
shell: bash
run: |
${{ secrets.DNS_API }}
DOMAIN_LIST=(${{ secrets.DOMAIN_LIST }})

for DOMAIN_INFO in "${DOMAIN_LIST[@]}"; do
IFS=',' read -r DOMAIN DNS ACME_SERVER PROFILE VALID_DAYS RENEW_BEFORE_DAYS <<< "$DOMAIN_INFO"

ACME_SERVER=${ACME_SERVER:-letsencrypt}
ACTION="renew --no-random-sleep --ari-disable --days ${RENEW_BEFORE_DAYS:-30}"
if [ ! -f "./.lego/certificates/$DOMAIN.crt" ]; then
ACTION="run"
fi

./lego \
--accept-tos \
--email "${{ secrets.CERT_EMAIL }}" \
--dns "$DNS" \
--server "${!ACME_SERVER}" \
--domains "$DOMAIN" \
--domains "*.$DOMAIN" \
--eab \
$ACTION ${VALID_DAYS:+--not-after=$(date -u -Is -d "+$VALID_DAYS days")} ${PROFILE:+--profile $PROFILE}
done
env:
letsencrypt: https://acme-v02.api.letsencrypt.org/directory
zerossl: https://acme.zerossl.com/v2/DV90
google: https://dv.acme-v02.api.pki.goog/directory

- name: ⬆️ Push Certificates to GitHub
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
cd ./.lego
git add certificates
if git diff --cached --quiet; then
echo "✅ No certificate changes detected."
else
git commit -m "🔒 Update certificates on $(date '+%Y-%m-%d %H:%M:%S')"
git push
fi

运行工作流程

您可以通过以下两种方式触发工作流程:

  1. 手动运行:在 GitHub 仓库的 Actions 页面,选择 acme ssl automation 工作流程,然后点击 Run workflow 手动触发。
  2. 定时运行:工作流程将根据配置的 cron 表达式,每周六运行一次。

总结

通过上述配置,您可以实现 SSL 证书的自动申请和更新,并将最新的证书文件推送到 GitHub 仓库。这样可以方便地管理和分发 SSL 证书,同时确保证书的及时更新。