使用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 获取提示信息

相关推荐
太行山有西瓜汁1 小时前
ESXi克隆虚拟机踩坑实录
运维
黎阳之光1 小时前
智慧环卫一体化管理:视频融合技术助力环卫作业与设施运维管控
运维
念恒123061 小时前
库制作与原理---库的理解和加载(中)
linux·运维·服务器
宁静@星空2 小时前
009-Linux环境安装宝塔
linux·运维·服务器
蜡笔婧萱2 小时前
LInux---Web网站建立的实战演练(2)
linux·运维·服务器
剑神一笑2 小时前
Linux crontab 命令详解:定时任务的底层实现与实战技巧
linux·运维·chrome
江木1232 小时前
Linux安装Matlab过程
linux·运维·matlab
YuanDaima20482 小时前
Docker 工程化安装与核心命令实战
运维·人工智能·docker·微服务·容器·bash
Lehjy2 小时前
【Linux】文件系统磁盘存储结构
android·linux·运维