使用 acme.sh 配合阿里云 OSS 存储如何实现 Let's Encrypt 证书续签备份?

文章导读
acme.sh 本身不直接支持阿里云 OSS 备份,但可以通过自定义 deploy hook 脚本实现证书续签后自动上传到 OSS,适合需要异地备份证书的场景。
📋 目录
  1. 环境准备
  2. 编写部署脚本
  3. 配置 acme.sh 对接
  4. 验证方法
  5. 常见坑与排查
A A

acme.sh 本身不直接支持阿里云 OSS 备份,但可以通过自定义 deploy hook 脚本实现证书续签后自动上传到 OSS,适合需要异地备份证书的场景。

先说结论:acme.sh 的证书默认存储在服务器本地 ~/.acme.sh/ 目录,要实现 OSS 备份需要自行编写部署脚本,在证书更新完成后触发上传。

  • 适合:有多台服务器需要统一证书备份、或希望证书文件异地存储的场景
  • 核心:利用 acme.sh 的 `--deploy-hook` 参数调用自定义脚本
  • 建议:先确保本地证书续签正常,再添加 OSS 备份逻辑,脚本执行失败不应影响续签流程

环境准备

在编写脚本前,需要在服务器上安装阿里云 OSS 命令行工具 ossutil,并配置好访问凭证。

1. 安装 ossutil

wget https://gosspublic.alicdn.com/ossutil/1.7.13/ossutil64 -O /usr/bin/ossutil64
chmod +x /usr/bin/ossutil64

2. 配置凭证(建议使用子账户)

不要在脚本中硬编码密钥,建议在系统环境变量中配置,或运行一次 config 命令。

ossutil64 config -e oss-cn-hangzhou.aliyuncs.com -i LTAI5t... -k Secret...

注意:为了安全,建议创建阿里云 RAM 子账户,仅授予 oss:PutObject 权限,避免使用主账户 AccessKey。

编写部署脚本

~/.acme.sh/deploy/ 目录下创建 deploy_oss.sh 脚本。acme.sh 在执行 deploy hook 时会传入证书路径等环境变量,脚本需读取这些变量并执行上传。

#!/bin/bash
# ~/.acme.sh/deploy/deploy_oss.sh

# acme.sh 传入的环境变量
DOMAIN="${CERT_DOMAIN}"
CERT_PATH="${CERT_PATH}"
KEY_PATH="${KEY_PATH}"
FULLCHAIN_PATH="${FULLCHAIN_PATH}"

# OSS 配置(根据实际修改)
OSS_ENDPOINT="oss-cn-hangzhou.aliyuncs.com"
OSS_BUCKET="your-backup-bucket"
OSS_PREFIX="ssl-certs/${DOMAIN}"

# 检查 ossutil 是否存在
if ! command -v ossutil64 > /dev/null 2>&1; then
    echo "[Error] ossutil64 not found" >&2
    exit 0
fi

# 上传证书文件
ossutil64 cp "${CERT_PATH}" "oss://${OSS_BUCKET}/${OSS_PREFIX}/cert.pem" -f -e "${OSS_ENDPOINT}" > /dev/null
ossutil64 cp "${KEY_PATH}" "oss://${OSS_BUCKET}/${OSS_PREFIX}/key.pem" -f -e "${OSS_ENDPOINT}" > /dev/null
ossutil64 cp "${FULLCHAIN_PATH}" "oss://${OSS_BUCKET}/${OSS_PREFIX}/fullchain.pem" -f -e "${OSS_ENDPOINT}" > /dev/null

if [ $? -ne 0 ]; then
    echo "[Warn] OSS backup failed for ${DOMAIN}, but renewal continues" >&2
fi

# 关键:无论上传成功与否,都返回 0,避免阻断证书续签
exit 0

赋予脚本执行权限:

使用 acme.sh 配合阿里云 OSS 存储如何实现 Let's Encrypt 证书续签备份?
chmod +x ~/.acme.sh/deploy/deploy_oss.sh

配置 acme.sh 对接

确认证书签发或安装时指定 deploy hook。如果是已有证书,可重新执行 installcert 命令关联脚本:

acme.sh `--installcert` -d yourdomain.com \
  `--key-file` /etc/nginx/ssl/key.pem \
  `--fullchain-file` /etc/nginx/ssl/cert.pem \
  `--reloadcmd` "service nginx force-reload" \
  `--deploy-hook` ~/.acme.sh/deploy/deploy_oss.sh

如果是新签发证书,直接在 issue 命令后加上 `--deploy-hook` 参数即可。配置完成后,acme.sh 会在每次续签成功后自动调用该脚本。

验证方法

1. 手动测试脚本

在不触发续签的情况下,手动执行脚本测试 OSS 连通性:

~/.acme.sh/deploy/deploy_oss.sh

检查 OSS 控制台是否生成文件。如果报错,检查 ossutil 配置及网络连通性。

2. 强制续签测试

acme.sh `--renew` -d yourdomain.com `--force`

观察输出日志中是否有 deploy hook 执行记录,并检查 ~/.acme.sh/acme.sh.log 确认无报错。

3. 验证 OSS 文件

登录阿里云 OSS 控制台,查看备份目录下的文件时间戳是否与本地 ~/.acme.sh/yourdomain.com/ 下的证书一致。

使用 acme.sh 配合阿里云 OSS 存储如何实现 Let's Encrypt 证书续签备份?

常见坑与排查

1. 脚本导致续签失败

acme.sh 默认认为 deploy hook 返回非零值即部署失败。对于备份类脚本,务必在脚本末尾使用 exit 0,确保即使 OSS 上传失败也不会影响证书续签。

2. 密钥泄露风险

严禁将 AccessKey 和 Secret 明文写在脚本中。推荐使用阿里云 RAM 角色(如果是 ECS)或系统环境变量。若使用 ossutil config,配置文件 ~/.ossutilconfig 权限应设置为 600。

chmod 600 ~/.ossutilconfig

3. 权限不足

如果上传报错 AccessDenied,检查 RAM 子账户权限策略。最小权限建议如下:

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "oss:PutObject",
      "Resource": "acs:oss:*:*:your-bucket-name/*"
    }
  ]
}

4. 路径变量差异

不同版本的 acme.sh 传入的环境变量名可能略有差异,建议在脚本开头添加 env > /tmp/acme_env.log 调试,确认证书路径变量名是 CERT_PATH 还是其他。