使用acme.sh进行阿里云域名SSL证书申请与部署自动化

1 前言

在访问我的博客时,发现我的图床又又又挂掉了,肯定是ssl证书到期,导致https访问出问题了(已经很多次了),本以为还能在阿里云上申请个人免费测试证书,续个命;登上阿里云控制台一看,发现个人免费测试证书无了(应该是当时从oss那里的跳转入口不对,以为没有了,直接跳转到下面的这个付费版页面了)。

刚刚为了严谨,在阿里云控制台又走了一遍,发现还在,申请流程如下

唉,为了这碗醋,包了顿饺子;为了这个脚本,写了这篇博客

2 证书自动化申请、部署的条件与流程

2.1 准备工作

  1. RAM 访问密钥(AccessKey): 准备一个阿里云 RAM 子账号的 Api_KeyApi_Secret
  2. 核心授权: 该子账号必须同时具备以下三项权限:
    • AliyunDNSFullAccess(用于向你的域名自动写入 DNS 解析记录以通过 ACME 验证)。
    • AliyunOSSFullAccess(用于将证书推送到指定的存储桶)。
    • AliyunYundunCertFullAccess 具备 yundun-cert:CreateSSLCertificate(数字证书管理服务)的权限。

其中,不要忘记给这个AliyunYundunCertFullAccess权限,因为 OSS 的 put-cname 接口在接收到你的托管证书时,会在底层将证书托管到阿里云证书服务中,没有这个权限会直接触发 403 拒绝。

2.2 流程概述

首先执行 aliyun cli 和 acme.sh 的安装;

然后,为了防止脚本未能完整执行成功,多次向 ZeroSSL 申请时,触发申请时间限制,对acme已经申请到的证书签发时间使用openssl与系统时间进行对比分析,进而划分两个路径:

  1. 如果证书签发未满 60 天(2个月),说明证书依然非常新。脚本将直接跳过向云端 CA 申请的步骤,直接提取本地证书发起部署。避免 ZeroSSL 重复申请导致延时。

  2. 如果证书超过 60 天,脚本才会自动向 CA 发起续期(Renew)申请。

最后,上传阿里云平台部署,基于最新 API 传参:动态将证书内容塞入最新规范的 BucketCnameConfiguration XML 结构中,通过新版 aliyun ossutil api put-cname 命令和 --cname-configuration file:// 传参,完成一键全自动托管部署。

阿里云oss控制台查看ssl证书,成功!

完整脚本

完整脚本如下,直接复制粘贴

bash 复制代码
#!/bin/bash
# ==============================================================================
# 脚本名称: acme_deploy_ssl_to_aliyun_oss.sh
# 适用环境: 完美适配最新一代 aliyun ossutil api 命令与自动化证书年龄检测
# ==============================================================================

# ==================== 【配置区域 - 请务必根据实际情况修改】 ====================
# 1. 阿里云 RAM 访问密钥(需具备 DNS、OSS 以及 yundun-cert 的相关权限)
export Ali_Key="cc&uu"
export Ali_Secret="https://caibucai.top"

# 2. 证书申请域名(即你绑定在 OSS 的自定义域名)
DOMAIN="你的oss绑定域名"

# 3. 接收证书过期通知的邮箱
EMAIL="cc&uu@gmail.com"

# 4. 阿里云 OSS 存储桶信息
OSS_BUCKET="bucket_name"  # 注意:严格填写纯粹的 Bucket 名字,不要带后缀
OSS_REGION="cn-hangzhou"       # 存储桶所在的区域 ID
FORCE_RENEW=false              # 是否强制重新申请证书(true/false)
# ==============================================================================

echo "[1/4] 开始检查并安装 Aliyun CLI 工具..."
if ! command -v aliyun &> /dev/null; then
    echo "未检测到 aliyun cli,正在下载并安装..."
    curl -L -o aliyun-cli.tgz https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz
    tar -zxvf aliyun-cli.tgz
    sudo mv aliyun /usr/local/bin/
    rm -f aliyun-cli.tgz
    echo "Aliyun CLI 安装成功!"
else
    echo "Aliyun CLI 已存在,跳过安装。"
fi

echo "[2/4] 动态配置高级版 Aliyun CLI 凭证..."
aliyun configure set \
  --profile current \
  --mode AK \
  --region "$OSS_REGION" \
  --access-key-id "$Ali_Key" \
  --access-key-secret "$Ali_Secret"

echo "[3/4] 开始检查并安装 acme.sh..."
if [ ! -f "$HOME/.acme.sh/acme.sh" ]; then
    curl https://gh-proxy.org/https://raw.githubusercontent.com/acmesh-official/acme.sh/master/acme.sh | sh -s -- --install-online -m $EMAIL
    source "$HOME/.bashrc" || true
else
    echo "acme.sh 已存在,跳过安装。"
fi

# 设置默认 CA 为 ZeroSSL(可根据习惯调整为 letsencrypt)
"$HOME/.acme.sh/acme.sh" --set-default-ca --server zerossl

# 临时证书导出目录
CERT_DIR="/tmp/oss_cert_out"
mkdir -p "$CERT_DIR"

# acme.sh 默认的证书存放路径(自动兼容 ECC 与普通路径)
ACME_CERT_PATH="$HOME/.acme.sh/${DOMAIN}_ecc/fullchain.cer"
if [ ! -f "$ACME_CERT_PATH" ]; then
    ACME_CERT_PATH="$HOME/.acme.sh/${DOMAIN}/fullchain.cer"
fi

echo "===================================================="
echo "🎯 开始检查域名 [ $DOMAIN ] 的本地证书状态..."
echo "===================================================="

NEED_ISSUE=true

# ==============================================================================
# 逻辑检查:检查本地是否存在未满 2 个月(60天)的最新证书
# ==============================================================================
if [ -f "$ACME_CERT_PATH" ]; then
    echo "发现本地已存在历史证书,正在计算签发时间..."
    
    # 获取证书的签发时间戳(秒)
    START_DATE=$(openssl x509 -in "$ACME_CERT_PATH" -noout -startdate | cut -d= -f2)
    START_TIME_STAMP=$(date -d "$START_DATE" +%s)
    
    # 获取当前系统时间戳(秒)
    CURRENT_TIME_STAMP=$(date +%s)
    
    # 精准计算已经过去了多少天(一天为 86400 秒)
    AGE_DAYS=$(( (CURRENT_TIME_STAMP - START_TIME_STAMP) / 86400 ))
    
    echo "💡 该本地证书已签发了 $AGE_DAYS 天。"

    if [ "$AGE_DAYS" -lt 60 ] && [ "$FORCE_RENEW" = false ]; then
        echo "✅ 证书签发时间未满 60 天(2个月),无需向 ZeroSSL 重复申请。"
        echo "🚀 脚本将直接提取本地证书并推送到阿里云 OSS 部署。"
        NEED_ISSUE=false
    else
        if [ "$FORCE_RENEW" = true ]; then
            echo "⚠️ 已开启强制重新申请 (FORCE_RENEW=true)。"
        else
            echo "⌛ 证书已签发超过 60 天,准备向 CA 发起更新申请。"
        fi
    fi
else
    echo "ℹ️ 本地未检测到历史证书,将首次发起申请流程。"
fi

# ==============================================================================
# 步骤 [4/4]:执行证书申领(如果需要)与最新 API 推送
# ==============================================================================
if [ "$NEED_ISSUE" = true ]; then
    echo ""
    echo "正在向证书局申请新证书..."
    
    # 安全捕获命令,防止 ZeroSSL 熔断直接导致脚本硬崩溃
    if ! "$HOME/.acme.sh/acme.sh" --issue --dns dns_ali -d "$DOMAIN"; then
        echo "❌ 错误: 证书申请由于网络或 ZeroSSL 接口限制失败!"
        echo "💡 容错提示: 由于本地已有历史证书,我们将强制尝试用本地旧证书为您同步,防止线上直接断流。"
        echo "正在跳过报错,尝试提取本地已有证书..."
    fi
else
    echo ""
    echo "⏭️ 已跳过申请步骤。"
fi

echo ""
echo "正在从 acme.sh 提取证书..."
"$HOME/.acme.sh/acme.sh" --install-cert -d "$DOMAIN" \
  --key-file       "$CERT_DIR/privkey.key" \
  --fullchain-file "$CERT_DIR/fullchain.pem"

XML_FILE="/tmp/oss_cname_final.xml"

echo "正在组装阿里云官方最新规范的 XML 配置文件..."
cat << EOF > "$XML_FILE"
<?xml version="1.0" encoding="UTF-8"?>
<BucketCnameConfiguration>
  <Cname>
    <Domain>$DOMAIN</Domain>
    <CertificateConfiguration>
      <Certificate>$(cat $CERT_DIR/fullchain.pem)</Certificate>
      <PrivateKey>$(cat $CERT_DIR/privkey.key)</PrivateKey>
      <Force>true</Force>
    </CertificateConfiguration>
  </Cname>
</BucketCnameConfiguration>
EOF

# 安全擦除临时导出的明文证书目录
rm -rf "$CERT_DIR"

echo "正在执行 aliyun ossutil api put-cname 同步到云端..."
# 严格适配最新版 V2 命令行参数:--cname-configuration file://...
if aliyun ossutil api put-cname --bucket "$OSS_BUCKET" --cname-configuration "file://$XML_FILE" --region "$OSS_REGION"; then
    echo "===================================================="
    echo "🎉 完美成功!SSL 证书已成功在阿里云 OSS 部署生效!"
    echo "安全访问域名: https://$DOMAIN"
    echo "===================================================="
else
    echo "❌ 错误: 推送证书到阿里云 OSS 失败,请检查 RAM 密钥权限或存储桶名称是否正确。"
fi

# 无论成功或失败,都必须清理掉带有敏感私钥的临时配置文件
rm -f "$XML_FILE"

赋予执行权限并运行即可:

bash 复制代码
chmod +x acme_deploy_ssl_to_aliyun_oss.sh
./acme_deploy_ssl_to_aliyun_oss.sh

再配合个定时任务,执行,即可实现自动化申请与部署。

参考

  1. 阿里云官方开发者参考指南 - PutCname 接口规范

  2. ACME.sh 官方开源 Wiki 探讨了关于利用阿里云 DNS API(dns_ali)进行自动化验证

  3. acme.sh Github repo

  4. aliyun cli 有效内容很少,直接在终端使用 -h 获取提示信息

相关推荐
乘云数字DATABUFF2 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
荣--4 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森4 天前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜5 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB6 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode7 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220708 天前
如何搭建本地yum源(上)
运维
大树8811 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠11 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质11 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务