diff --git a/deploy/baidu_cdn.sh b/deploy/baidu_cdn.sh new file mode 100644 index 00000000..6e418cd7 --- /dev/null +++ b/deploy/baidu_cdn.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env sh +# shellcheck disable=SC2034,SC2154 + +# Deploy hook: Baidu Cloud CDN +# +# Code generated by GitHub Copilot with Claude Sonnet 4.6 +# +# API Doc: https://cloud.baidu.com/doc/CDN/s/Zkna2r57w +# +# Credentials are shared with dnsapi/dns_baidu.sh: +# export Baidu_AK="your-access-key-id" +# export Baidu_SK="your-secret-access-key" +# +# To deploy to a CDN domain different from the certificate CN +# (e.g. wildcard or multi-domain certs): +# export DEPLOY_BAIDU_CDN_DOMAIN="cdn.example.com" +# +# Multiple CDN domains sharing the same certificate: +# export DEPLOY_BAIDU_CDN_DOMAIN="cdn1.example.com cdn2.example.com" + +BAIDU_CDN_HOST="cdn.baidubce.com" + +baidu_cdn_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + # Source dnsapi/dns_baidu.sh to reuse BCE authentication and credential helpers + _dnsapi_baidu="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_baidu)" + # shellcheck source=/dev/null + if ! . "$_dnsapi_baidu"; then + _err "Error loading $_dnsapi_baidu. Please check your API file and try again." + return 1 + fi + + if ! _baidu_load_credentials; then + return 1 + fi + + _getdeployconf DEPLOY_BAIDU_CDN_DOMAIN + if [ "$DEPLOY_BAIDU_CDN_DOMAIN" ]; then + _savedeployconf DEPLOY_BAIDU_CDN_DOMAIN "$DEPLOY_BAIDU_CDN_DOMAIN" + else + DEPLOY_BAIDU_CDN_DOMAIN="$_cdomain" + fi + + # Build JSON "domains" array from space-separated domain list + _domains_json="" + for _d in $DEPLOY_BAIDU_CDN_DOMAIN; do + _d_e="$(_baidu_json_escape "$_d")" + if [ -z "$_domains_json" ]; then + _domains_json="\"${_d_e}\"" + else + _domains_json="${_domains_json},\"${_d_e}\"" + fi + done + + # Build a valid cert name: must start with a letter, allow [A-Za-z0-9-/.], max 65 chars + _cert_name="$(printf "%s" "$_cdomain" | sed 's/\*\./wildcard./g;s/[^A-Za-z0-9./]/-/g' | cut -c 1-65)" + case "$_cert_name" in + [A-Za-z]*) ;; + *) _cert_name="c${_cert_name}" ;; + esac + + # PEM content is already Base64 inside the -----BEGIN/END----- wrappers. + # The API expects the raw PEM as a JSON string, so newlines must be escaped as \n. + _cert_pem="$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')" + _key_pem="$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')" + + _debug2 _cert_name "$_cert_name" + _debug2 _domains_json "[$_domains_json]" + + # Build JSON payload + _payload="{\"domains\":[${_domains_json}],\"certificate\":{\"certName\":\"${_cert_name}\",\"certServerData\":\"${_cert_pem}\",\"certPrivateData\":\"${_key_pem}\"}}" + + # Generate BCE v1 authorization header (query string included in canonical request) + _cdn_path="/v2/domain/certificate" + _cdn_query="action=put" + _ts="$(_utc_date | sed 's/ /T/')Z" + _content_type="application/json; charset=utf-8" + _payload_hash="$(printf "%s" "$_payload" | _digest sha256 hex)" + + if ! _baidu_cdn_bce_auth "POST" "$_cdn_path" "$_cdn_query" "$BAIDU_CDN_HOST" "$_ts" "3600" "$_content_type" "$_payload_hash"; then + _err "Failed to sign request" + return 1 + fi + + _H1="Authorization: $_BAIDU_BCE_AUTH_RESULT" + _H2="x-bce-date: $_ts" + _H3="x-bce-content-sha256: $_payload_hash" + _H4="Host: $BAIDU_CDN_HOST" + _H5="" + + _url="https://${BAIDU_CDN_HOST}${_cdn_path}?${_cdn_query}" + response="$(_post "$_payload" "$_url" "" "POST" "$_content_type")" + if [ "$?" != "0" ]; then + _err "Failed to call Baidu Cloud CDN API" + return 1 + fi + + _debug2 response "$response" + + if _contains "$response" "\"certId\""; then + _info "Certificate deployed to Baidu Cloud CDN for: $DEPLOY_BAIDU_CDN_DOMAIN" + return 0 + fi + + _err "Failed to deploy certificate to Baidu Cloud CDN: $response" + return 1 +} + +# BCE v1 signing with canonical query string support. +# dns_baidu.sh's _baidu_bce_auth omits query strings (BCD API has none); +# the CDN endpoint uses ?action=put so it must be included in the canonical request. +_baidu_cdn_bce_auth() { + _method="$1" + _uri="$2" + _query="$3" + _host="$4" + _ts="$5" + _expire="$6" + _ct="$7" + _payload_hash="$8" + + _BAIDU_BCE_AUTH_RESULT="" + + _auth_prefix="bce-auth-v1/${Baidu_AK}/${_ts}/${_expire}" + _signed_headers="content-type;host;x-bce-content-sha256;x-bce-date" + _canonical_uri="$(_baidu_bce_encode_path "$_uri")" + + _host_e="$(printf "%s" "$_host" | _url_encode upper-hex)" + _date_e="$(printf "%s" "$_ts" | _url_encode upper-hex)" + _ct_e="$(printf "%s" "$_ct" | _url_encode upper-hex)" + _hash_e="$(printf "%s" "$_payload_hash" | _url_encode upper-hex)" + + _canonical_headers="content-type:${_ct_e} +host:${_host_e} +x-bce-content-sha256:${_hash_e} +x-bce-date:${_date_e}" + + _canonical_request="${_method} +${_canonical_uri} +${_query} +${_canonical_headers}" + + _sk_hex="$(printf "%s" "$Baidu_SK" | _hex_dump | tr -d " ")" + _signing_key="$(_baidu_hmac_sha256_hexkey "$_sk_hex" "$_auth_prefix")" + _signing_key_hex="$(printf "%s" "$_signing_key" | _hex_dump | tr -d " ")" + _signature="$(_baidu_hmac_sha256_hexkey "$_signing_key_hex" "$_canonical_request")" + + _BAIDU_BCE_AUTH_RESULT="${_auth_prefix}/${_signed_headers}/${_signature}" +}