背景

火山云全站加速配置SSL证书,手动上传并绑定证书的过程虽然简单,但多域名重复操作既繁琐又容易出错。于是决定写一个自动化脚本,利用火山引擎 CLI 工具(ve)完成证书上传与绑定的全流程。

工具选型与思路

火山引擎提供了统一的命令行工具 ve,支持 CDN、DCDN 等多个服务。一开始查看了 DCDN 相关的命令:

ve dcdn CreateCertBind --help

发现 CreateCertBind 需要一个已存在于证书中心的 CertId,而不能直接传递证书文件内容。这意味着必须先将证书上传到火山引擎的证书中心,拿到 CertId 后再绑定到 DCDN 域名。

庆幸的是,ve cdn AddCertificate 命令可以将证书上传至火山引擎证书中心(设置 Sourcevolc_cert_center),并且返回的 CertId 可以在 DCDN 中直接使用。这样一来,流程就清晰了:

  1. 读取本地证书文件(fullchain.pem 和 privkey.pem)
  2. 调用 AddCertificate 上传,获取 CertId(若证书已存在则复用)
  3. 调用 CreateCertBind 将证书绑定到 DCDN 域名

遇到的两个坑

1. 通配符域名不被 DCDN 接受

一开始脚本中的 DomainNames 写成了 ["example.com", "*.example.com"],执行后报错:

DomainNotExist: Domain Name [*.example.com] does not exist

通过 ve dcdn ListDomainConfig 查看实际已添加的加速域名,发现 DCDN 控制台并不接受 *.example.com 这样的通配符形式。实际存在的域名是 example.comsub.example.com 以及一个以点开头的特殊域名 .example.com(可能是历史原因或内部标识)。因此最终使用的域名列表需要与实际配置完全一致。

2. 错误响应的解析路径不一致

ve cdn AddCertificate 的错误信息位于 .ResponseMetadata.Error,而 ve dcdn CreateCertBind 的错误信息却直接在根节点 .Error 中。最初脚本只检查了 .ResponseMetadata.Error,导致 CreateCertBind 明明失败了,脚本却输出“绑定成功”。修正后同时检查两个可能的路径。

最终脚本

以下脚本实现了自动上传证书并绑定到 DCDN 域名。使用时请根据实际情况修改证书存放目录、日志目录以及目标域名列表。

#!/bin/bash
#set -xv

log_date() {
    date '+[%Y/%m/%d %H:%M:%S]'
}

# 配置 - 请按实际修改
CERT_STORE_DIR="/path/to/your/certificates"
LOG_DIR="/path/to/log"
mkdir -p "$LOG_DIR"
exec > >(tee -a "$LOG_DIR/cert.log") 2>&1

echo "$(log_date) 脚本开始执行"

# 加载证书内容
cert_content=$(cat "$CERT_STORE_DIR/fullchain.pem")
key_content=$(cat "$CERT_STORE_DIR/privkey.pem")

echo "$(log_date) 证书内容长度: ${#cert_content}"
echo "$(log_date) 私钥内容长度: ${#key_content}"

# 1. 上传证书到火山引擎证书中心
upload_json_request() {
    jq -n \
        --arg cert "$cert_content" \
        --arg key "$key_content" \
        '{
            "Source": "volc_cert_center",
            "CertType": "server_cert",
            "Certificate": $cert,
            "PrivateKey": $key
        }'
}

echo "$(log_date) 正在上传证书到火山引擎证书中心..."
response=$(ve cdn AddCertificate --body "$(upload_json_request)")

if echo "$response" | jq -e '.ResponseMetadata.Error' > /dev/null 2>&1; then
    error_code=$(echo "$response" | jq -r '.ResponseMetadata.Error.Code')
    if [ "$error_code" = "InvalidParameter.Certificate.Duplicated" ]; then
        # 证书已存在时从错误消息中提取已有证书ID
        cert_id=$(echo "$response" | jq -r '.ResponseMetadata.Error.Message | capture("ID为 (?<id>[^。]+)。") | .id')
        echo "$(log_date) 证书已存在,使用已有证书ID: $cert_id"
    else
        error_msg=$(echo "$response" | jq -r '.ResponseMetadata.Error.Message')
        echo "$(log_date) 上传失败: $error_msg"
        exit 1
    fi
else
    request_id=$(echo "$response" | jq -r '.ResponseMetadata.RequestId')
    cert_id=$(echo "$response" | jq -r '.Result.CertId')
    echo "$(log_date) 上传成功,RequestId: $request_id"
    echo "$(log_date) 证书ID: $cert_id"
fi

# 2. 绑定证书到 DCDN 域名(按实际域名列表修改)
deploy_json_request() {
    jq -n \
        --arg cert_id "$cert_id" \
        '{
            "CertSource": "volc",
            "CertId": $cert_id,
            "DomainNames": ["example.com", "sub.example.com", ".example.com"]
        }'
}

echo "$(log_date) 正在将证书绑定到全站加速域名..."
response=$(ve dcdn CreateCertBind --body "$(deploy_json_request)")

# 增强错误检测:同时检查根级 .Error 和 .ResponseMetadata.Error
if echo "$response" | jq -e '.Error' > /dev/null 2>&1 || echo "$response" | jq -e '.ResponseMetadata.Error' > /dev/null 2>&1; then
    error_msg=$(echo "$response" | jq -r '.Error.Message // .ResponseMetadata.Error.Message')
    echo "$(log_date) 绑定失败: $error_msg"
    exit 1
else
    request_id=$(echo "$response" | jq -r '.ResponseMetadata.RequestId // "unknown"')
    echo "$(log_date) 绑定成功,RequestId: $request_id"
    exit 0
fi

执行效果

脚本执行时会输出详细日志:

[2026/05/19 14:38:06] 脚本开始执行
[2026/05/19 14:38:06] 证书内容长度: 2864
[2026/05/19 14:38:06] 私钥内容长度: 226
[2026/05/19 14:38:06] 正在上传证书到火山引擎证书中心...
[2026/05/19 14:38:11] 上传成功,RequestId: xxx
[2026/05/19 14:38:11] 证书ID: cert-xxxx
[2026/05/19 14:38:11] 正在将证书绑定到全站加速域名...
[2026/05/19 14:38:15] 绑定成功,RequestId: yyy

如果证书已经存在,会直接复用原有证书 ID,避免重复上传。

总结

通过结合 ve cdn AddCertificateve dcdn CreateCertBind,可以高效地完成火山引擎全站加速的证书自动更新。关键点在于:

  • 证书中心是统一的,CDN 上传的证书可供 DCDN 使用
  • DCDN 的 DomainNames 必须精确匹配控制台中已添加的加速域名(不支持通配符形式的域名)
  • 不同命令的错误响应结构可能不同,需要编写健壮的解析逻辑

上述脚本配合1Panel执行,即可在证书更新后自动部署,告别手动操作的烦恼。