#!/bin/bash
# ==============================================================================
# Qiniu CDN SSL Certificate Update Script (Hardcoded Configuration)
#
# Description:
# This script uploads a specific SSL certificate and private key to Qiniu Cloud
# and binds it to a predefined domain. All settings are configured directly
# within this file.
#
# Dependencies:
# - curl: For making HTTP requests.
# - openssl: For HMAC-SHA1 signature calculation.
# - jq: For parsing JSON responses from the API.
# - md5sum: For creating a unique name for the certificate.
#
# Usage:
# 1. Fill in your details in the "User Configuration" section below.
# 2. Make the script executable: chmod +x update_qiniu_ssl.sh
# 3. Run the script without any arguments: ./update_qiniu_ssl.sh
#
# ==============================================================================

set -e # Exit immediately if a command exits with a non-zero status.

# --- START: User Configuration ---
# 请在此处填写所有必需的信息

# --- 七牛云API密钥 ---
ACCESS_KEY="<YOUR_QINIU_ACCESS_KEY>"
SECRET_KEY="<YOUR_QINIU_SECRET_KEY>"

# --- 域名和证书信息 ---
# 需要更新证书的域名
DOMAIN_TO_UPDATE="a.testdomain.com"

# 证书文件路径 (可以使用绝对路径或相对路径)
CERT_FILE_PATH="./fullchain.cer"

# 私钥文件路径 (可以使用绝对路径或相对路径)
KEY_FILE_PATH="./cert.key"

# 证书的通用名称 (Common Name), 例如 '*.testdomain.com'
CERT_COMMON_NAME="*.testdomain.com"

# --- END: User Configuration ---


# --- Script Constants ---
API_HOST="https://api.qiniu.com"


# --- Script Functions ---

# 日志记录函数
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}

# URL安全的Base64编码 (tr命令用于替换 '+' 和 '/' 字符)
urlsafe_base64_encode() {
    base64 -w 0 | tr '+/' '-_'
}

# 生成七牛API请求的管理凭证 (QBox Token)
# $1: 请求的URL (包括path和query)
# $2: 请求的Body (可选, 仅当Content-Type为application/x-www-form-urlencoded时需要)
generate_qbox_token() {
    local url=$1
    local body=$2
    local path=$(echo "$url" | sed -n 's|https\?://[^/]\+||p')
    
    local signing_str="$path\n"
    if [[ -n "$body" ]]; then
        signing_str="$signing_str$body"
    fi

    local sign=$(echo -n -e "$signing_str" | openssl dgst -sha1 -hmac "$SECRET_KEY" -binary | urlsafe_base64_encode)
    echo "QBox ${ACCESS_KEY}:${sign}"
}

# 主函数:上传并绑定证书
main() {
    # 1. 验证配置和环境
    log "开始执行证书更新任务..."
    
    # 验证AK/SK是否已填写
    if [[ "$ACCESS_KEY" == "<YOUR_QINIU_ACCESS_KEY>" || "$SECRET_KEY" == "<YOUR_QINIU_SECRET_KEY>" ]]; then
        log "错误: 请在脚本顶部的 'User Configuration' 区域填入您的 QINIU_ACCESSKEY 和 QINIU_SECRETKEY。"
        exit 1
    fi

    # 验证依赖工具是否存在
    for cmd in curl openssl jq md5sum; do
        if ! command -v $cmd &> /dev/null; then
            log "错误: 必需的命令 '$cmd' 未找到。请先安装它。"
            exit 1
        fi
    done
    
    # 验证证书和私钥文件是否存在
    if [ ! -f "$CERT_FILE_PATH" ]; then
        log "错误: 证书文件未找到: $CERT_FILE_PATH"
        exit 1
    fi
    if [ ! -f "$KEY_FILE_PATH" ]; then
        log "错误: 私钥文件未找到: $KEY_FILE_PATH"
        exit 1
    fi

    # 2. 准备上传数据
    log "正在为域名 '$DOMAIN_TO_UPDATE' 准备证书..."
    local CERT_CONTENT=$(cat "$CERT_FILE_PATH")
    local KEY_CONTENT=$(cat "$KEY_FILE_PATH")
    
    # 根据证书文件md5生成一个固定的证书名称
    local CERT_NAME="qiniu_helper_$(md5sum "$CERT_FILE_PATH" | cut -d' ' -f1)"
    log "生成的证书名称为: $CERT_NAME"

    # 3. 检查证书是否已经存在
    log "正在检查证书 '$CERT_NAME' 是否已存在..."
    local list_url="${API_HOST}/sslcert"
    local list_token=$(generate_qbox_token "$list_url")
    
    local list_response=$(curl -s -X GET "$list_url" \
        -H "Authorization: ${list_token}" \
        -H "Content-Type: application/json")

    # 使用jq从返回的json中查找具有相同名称的证书ID
    local CERT_ID=$(echo "$list_response" | jq -r --arg name "$CERT_NAME" '.certs[] | select(.name == $name) | .certid')

    # 4. 如果证书不存在,则上传
    if [ -z "$CERT_ID" ]; then
        log "证书不存在,正在上传新证书..."
        local upload_url="${API_HOST}/sslcert"
        
        # 准备POST请求的JSON Body
        local upload_data=$(jq -n \
            --arg name "$CERT_NAME" \
            --arg common_name "$CERT_COMMON_NAME" \
            --arg pri "$KEY_CONTENT" \
            --arg ca "$CERT_CONTENT" \
            '{name: $name, common_name: $common_name, pri: $pri, ca: $ca}')
        
        local upload_token=$(generate_qbox_token "$upload_url")
        
        local upload_response=$(curl -s -X POST "$upload_url" \
            -H "Authorization: ${upload_token}" \
            -H "Content-Type: application/json" \
            -d "$upload_data")

        # 从上传响应中提取新的证书ID
        CERT_ID=$(echo "$upload_response" | jq -r '.certID')

        if [ -z "$CERT_ID" ] || [ "$CERT_ID" == "null" ]; then
            log "错误: 证书上传失败!"
            log "API 响应: $upload_response"
            exit 1
        fi
        log "证书上传成功!新的证书ID为: $CERT_ID"
    else
        log "证书已存在。证书ID为: $CERT_ID"
    fi

    # 5. 将证书绑定到域名
    log "正在将证书ID '$CERT_ID' 绑定到域名 '$DOMAIN_TO_UPDATE'..."
    local bind_url="${API_HOST}/domain/${DOMAIN_TO_UPDATE}/httpsconf"
    
    # 准备PUT请求的JSON Body
    local bind_data=$(jq -n \
        --arg certId "$CERT_ID" \
        --argjson forceHttps false \
        --argjson http2Enable true \
        '{certId: $certId, forceHttps: $forceHttps, http2Enable: $http2Enable}')
        
    local bind_token=$(generate_qbox_token "$bind_url")
    
    local bind_response=$(curl -s -o /dev/null -w "%{http_code}" -X PUT "$bind_url" \
        -H "Authorization: ${bind_token}" \
        -H "Content-Type: application/json" \
        -d "$bind_data")

    if [ "$bind_response" -eq 200 ]; then
        log "成功!证书已成功更新并绑定到域名 '$DOMAIN_TO_UPDATE'。"
    else
        log "错误: 绑定证书失败!"
        log "HTTP 状态码: $bind_response"
        log "请检查您的域名、权限以及证书与域名是否匹配。"
        exit 1
    fi
}

# 执行主函数
main

标签: none

评论已关闭