From 6efd6d5b5a7c582f35807d758a264252bb69e2cf Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Wed, 1 Apr 2026 05:10:04 +0700 Subject: [PATCH 01/21] Add BytePlus ALB deployment script This script deploys SSL/TLS certificates issued by acme.sh to BytePlus Application Load Balancer (ALB), supporting automatic renewal with zero-downtime certificate rotation. --- deploy/byteplus_alb.sh | 449 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 449 insertions(+) create mode 100644 deploy/byteplus_alb.sh diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh new file mode 100644 index 00000000..0cffa750 --- /dev/null +++ b/deploy/byteplus_alb.sh @@ -0,0 +1,449 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034,SC2154 +# +# acme.sh deploy hook: BytePlus Application Load Balancer (ALB) +# https://github.com/acmesh-official/acme.sh/wiki/deployhooks +# +# Deploys SSL/TLS certificates issued by acme.sh to BytePlus ALB. +# Supports automatic renewal with zero-downtime certificate rotation. +# +# ┌─────────────────────────────────────────────────────────────────────┐ +# │ FIRST TIME (new domain) │ +# │ 1. acme.sh --issue -d example.com -w /var/www/html/ │ +# │ 2. acme.sh --deploy -d example.com --deploy-hook byteplus_alb │ +# │ → UploadCertificate → saves CertificateId │ +# │ 3. Manually assign cert to ALB Listener (one-time only) │ +# │ │ +# │ RENEWAL (fully automatic) │ +# │ acme.sh cron triggers renew → deploy hook runs automatically │ +# │ → ReplaceCertificate (UpdateMode=new) — single API call │ +# │ → All attached listeners updated, old cert auto-deleted │ +# └─────────────────────────────────────────────────────────────────────┘ +# +# Required environment variables: +# export BYTEPLUS_ACCESS_KEY="AKAPxxxxxxxxxx" +# export BYTEPLUS_SECRET_KEY="your-secret-key" +# +# Optional environment variables: +# export BYTEPLUS_REGION="ap-southeast-3" # default: ap-southeast-3 +# export BYTEPLUS_HOST="alb.ap-southeast-3.byteplusapi.com" # custom API host +# export BYTEPLUS_PROJECT_NAME="live" # default: "default" project +# export BYTEPLUS_CERT_NAME="" # default: acme-{domain}-{YYYYMMDD-HHMM} +# export BYTEPLUS_CERT_DESCRIPTION="" # default: empty +# export BYTEPLUS_DELETE_OLD_CERT="true" # default: true — auto-delete after replace +# +# API notes: +# - All BytePlus ALB APIs use GET with query string parameters +# - Request signing: HMAC-SHA256 with signed headers host;x-date +# - PublicKey/PrivateKey are URL-encoded (RFC 3986) in query string +# - ReplaceCertificate with UpdateMode=new uploads + replaces in 1 call +# +# Dependencies: curl, openssl, awk (standard on most Linux) +# +# Docs: +# Signing — https://docs.byteplus.com/en/docs/byteplus-platform/reference-how-to-calculate-a-signature +# ALB API — https://docs.byteplus.com/en/docs/byteplus-alb + +# ══════════════════════════════════════════════════════════════════════════════ +# Constants +# ══════════════════════════════════════════════════════════════════════════════ + +# SHA-256 hash of empty string (used for GET requests with no body) +_BYTEPLUS_EMPTY_HASH="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + +# ══════════════════════════════════════════════════════════════════════════════ +# Main deploy function — called by acme.sh +# ══════════════════════════════════════════════════════════════════════════════ + +byteplus_alb_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" + + # ── 1. Load & validate credentials ────────────────────────────────────────── + + # Preserve environment values before _getdeployconf (which may reset them) + _env_project_name="${BYTEPLUS_PROJECT_NAME:-}" + _env_delete_old="${BYTEPLUS_DELETE_OLD_CERT:-}" + + _getdeployconf BYTEPLUS_ACCESS_KEY + _getdeployconf BYTEPLUS_SECRET_KEY + _getdeployconf BYTEPLUS_REGION + _getdeployconf BYTEPLUS_HOST + _getdeployconf BYTEPLUS_PROJECT_NAME + _getdeployconf BYTEPLUS_DELETE_OLD_CERT + _getdeployconf BYTEPLUS_CERT_NAME + _getdeployconf BYTEPLUS_CERT_DESCRIPTION + + # Restore from environment if _getdeployconf cleared them + if [ -z "$BYTEPLUS_PROJECT_NAME" ] && [ -n "$_env_project_name" ]; then + _debug "Restoring BYTEPLUS_PROJECT_NAME from environment" + BYTEPLUS_PROJECT_NAME="$_env_project_name" + fi + if [ -z "$BYTEPLUS_DELETE_OLD_CERT" ] && [ -n "$_env_delete_old" ]; then + BYTEPLUS_DELETE_OLD_CERT="$_env_delete_old" + fi + + # Validate required credentials + if [ -z "$BYTEPLUS_ACCESS_KEY" ]; then + _err "BYTEPLUS_ACCESS_KEY is not set." + _err "Please run: export BYTEPLUS_ACCESS_KEY=\"your-access-key\"" + return 1 + fi + if [ -z "$BYTEPLUS_SECRET_KEY" ]; then + _err "BYTEPLUS_SECRET_KEY is not set." + _err "Please run: export BYTEPLUS_SECRET_KEY=\"your-secret-key\"" + return 1 + fi + + # Save credentials for future runs + _savedeployconf BYTEPLUS_ACCESS_KEY "$BYTEPLUS_ACCESS_KEY" + _savedeployconf BYTEPLUS_SECRET_KEY "$BYTEPLUS_SECRET_KEY" + + # Region (default: ap-southeast-3) + BYTEPLUS_REGION="${BYTEPLUS_REGION:-ap-southeast-3}" + _savedeployconf BYTEPLUS_REGION "$BYTEPLUS_REGION" + + # Project name + if [ -n "$BYTEPLUS_PROJECT_NAME" ]; then + _savedeployconf BYTEPLUS_PROJECT_NAME "$BYTEPLUS_PROJECT_NAME" + _info "Using project: $BYTEPLUS_PROJECT_NAME" + else + _info "WARNING: BYTEPLUS_PROJECT_NAME is not set. Cert will go to 'default' project." + fi + + # Delete old cert toggle (default: true) + BYTEPLUS_DELETE_OLD_CERT="${BYTEPLUS_DELETE_OLD_CERT:-true}" + _savedeployconf BYTEPLUS_DELETE_OLD_CERT "$BYTEPLUS_DELETE_OLD_CERT" + + # API host — custom override or auto-build from region + if [ -n "$BYTEPLUS_HOST" ]; then + _BYTEPLUS_HOST="$BYTEPLUS_HOST" + _savedeployconf BYTEPLUS_HOST "$BYTEPLUS_HOST" + else + _BYTEPLUS_HOST="alb.${BYTEPLUS_REGION}.byteplusapi.com" + fi + _info "Using API host: $_BYTEPLUS_HOST" + _BYTEPLUS_SERVICE="alb" + + # ── 2. Build certificate name ──────────────────────────────────────────────── + + _date_tag=$(date -u +%Y%m%d-%H%M) + # Replace wildcard * and dots for a valid cert name + _safe_domain=$(echo "$_cdomain" | sed 's/\*\.//g' | sed 's/\./-/g') + # Underscore version for bash variable names (hyphens not allowed in var names) + _conf_key=$(echo "$_cdomain" | sed 's/\*\.//g' | sed 's/\./_/g') + + if [ -z "$BYTEPLUS_CERT_NAME" ]; then + BYTEPLUS_CERT_NAME="acme-${_safe_domain}-${_date_tag}" + fi + + # Enforce BytePlus naming rules: start with letter, max 128 chars + BYTEPLUS_CERT_NAME=$(echo "$BYTEPLUS_CERT_NAME" | sed 's/[^a-zA-Z0-9._-]/-/g' | cut -c1-128) + + _info "Certificate name: $BYTEPLUS_CERT_NAME" + + # ── 3. Read cert and key ───────────────────────────────────────────────────── + # BytePlus requires NO blank lines between PEM blocks in the certificate chain + + _public_key=$(sed '/^[[:space:]]*$/d' "$_cfullchain" | tr -d '\r') + _private_key=$(sed '/^[[:space:]]*$/d' "$_ckey" | tr -d '\r') + + if [ -z "$_public_key" ] || [ -z "$_private_key" ]; then + _err "Failed to read certificate or key file." + return 1 + fi + + # ── 4. Deploy: first-time upload or renewal replace ───────────────────────── + + _getdeployconf "BYTEPLUS_CERT_ID_${_conf_key}" + _old_cert_id=$(eval echo "\$BYTEPLUS_CERT_ID_${_conf_key}") + + if [ -z "$_old_cert_id" ]; then + _byteplus_first_time_deploy + else + _byteplus_renewal_deploy + fi + + # Check if deploy step set _new_cert_id + if [ -z "$_new_cert_id" ]; then + return 1 + fi + + # ── 5. Save new CertificateId for next renewal ─────────────────────────────── + + _savedeployconf "BYTEPLUS_CERT_ID_${_conf_key}" "$_new_cert_id" + _info "Saved CertificateId '$_new_cert_id' for domain '$_cdomain'." + + return 0 +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Deploy: First time — UploadCertificate +# ══════════════════════════════════════════════════════════════════════════════ + +_byteplus_first_time_deploy() { + _info "No previous CertificateId found. Uploading new certificate..." + + if [ -n "$BYTEPLUS_PROJECT_NAME" ]; then + _upload_response=$(_byteplus_alb_api "UploadCertificate" \ + "CertificateType=Server" \ + "CertificateName=${BYTEPLUS_CERT_NAME}" \ + "ProjectName=${BYTEPLUS_PROJECT_NAME}" \ + "PublicKey=${_public_key}" \ + "PrivateKey=${_private_key}") + else + _upload_response=$(_byteplus_alb_api "UploadCertificate" \ + "CertificateType=Server" \ + "CertificateName=${BYTEPLUS_CERT_NAME}" \ + "PublicKey=${_public_key}" \ + "PrivateKey=${_private_key}") + fi + + _debug2 _upload_response "$_upload_response" + + _new_cert_id=$(_byteplus_extract_cert_id "$_upload_response") + + if [ -z "$_new_cert_id" ]; then + _err "UploadCertificate failed: $(_byteplus_extract_error "$_upload_response")" + _debug2 "Full response" "$_upload_response" + return 1 + fi + + _info "Certificate uploaded. CertificateId: $_new_cert_id" + + # Set description if provided + if [ -n "$BYTEPLUS_CERT_DESCRIPTION" ]; then + _info "Setting certificate description..." + _byteplus_alb_api "ModifyCertificateAttributes" \ + "CertificateId=${_new_cert_id}" \ + "CertificateName=${BYTEPLUS_CERT_NAME}" \ + "Description=${BYTEPLUS_CERT_DESCRIPTION}" >/dev/null + fi + + _info "" + _info "╔══════════════════════════════════════════════════════════════════╗" + _info "║ ACTION REQUIRED (one-time only) ║" + _info "║ Assign CertificateId '$_new_cert_id'" + _info "║ to your ALB Listener in BytePlus Console. ║" + _info "║ After that, all future renewals will be fully automatic. ║" + _info "╚══════════════════════════════════════════════════════════════════╝" + _info "" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Deploy: Renewal — ReplaceCertificate (UpdateMode=new) +# ══════════════════════════════════════════════════════════════════════════════ + +_byteplus_renewal_deploy() { + _info "Replacing old certificate '$_old_cert_id' (UpdateMode=new)..." + + if [ -n "$BYTEPLUS_PROJECT_NAME" ]; then + _replace_response=$(_byteplus_alb_api "ReplaceCertificate" \ + "OldCertificateId=${_old_cert_id}" \ + "UpdateMode=new" \ + "CertificateName=${BYTEPLUS_CERT_NAME}" \ + "ProjectName=${BYTEPLUS_PROJECT_NAME}" \ + "PublicKey=${_public_key}" \ + "PrivateKey=${_private_key}") + else + _replace_response=$(_byteplus_alb_api "ReplaceCertificate" \ + "OldCertificateId=${_old_cert_id}" \ + "UpdateMode=new" \ + "CertificateName=${BYTEPLUS_CERT_NAME}" \ + "PublicKey=${_public_key}" \ + "PrivateKey=${_private_key}") + fi + + _debug2 _replace_response "$_replace_response" + + _new_cert_id=$(_byteplus_extract_cert_id "$_replace_response") + + if [ -z "$_new_cert_id" ]; then + _err "ReplaceCertificate failed: $(_byteplus_extract_error "$_replace_response")" + _debug2 "Full response" "$_replace_response" + return 1 + fi + + _info "Certificate replaced successfully on all attached listeners." + _info "New CertificateId: $_new_cert_id" + + # Auto-cleanup old certificate + if [ "$BYTEPLUS_DELETE_OLD_CERT" = "true" ]; then + _byteplus_delete_old_cert "$_old_cert_id" + else + _info "Auto-delete disabled. Old certificate '$_old_cert_id' kept in inventory." + fi +} + +# ══════════════════════════════════════════════════════════════════════════════ +# Delete old certificate (with retry) +# ══════════════════════════════════════════════════════════════════════════════ + +_byteplus_delete_old_cert() { + _del_cert_id="$1" + + _info "Waiting 5s for cert status to settle..." + sleep 5 + + _info "Deleting old certificate '$_del_cert_id'..." + _del_response=$(_byteplus_alb_api "DeleteCertificate" "CertificateId=${_del_cert_id}") + + if echo "$_del_response" | grep -q '"Error"'; then + _info "Delete failed, retrying in 10s..." + sleep 10 + _del_response=$(_byteplus_alb_api "DeleteCertificate" "CertificateId=${_del_cert_id}") + + if echo "$_del_response" | grep -q '"Error"'; then + _info "Warning: Could not delete old certificate '$_del_cert_id'." + _info "Error: $(_byteplus_extract_error "$_del_response")" + _info "Please remove it manually from BytePlus Console." + else + _info "Old certificate '$_del_cert_id' deleted (retry succeeded)." + fi + else + _info "Old certificate '$_del_cert_id' deleted." + fi +} + +# ══════════════════════════════════════════════════════════════════════════════ +# JSON response helpers +# ══════════════════════════════════════════════════════════════════════════════ + +# Extract CertificateId from API response JSON +_byteplus_extract_cert_id() { + echo "$1" | _egrep_o '"CertificateId"\s*:\s*"[^"]*"' | head -1 | _egrep_o '"[^"]*"$' | tr -d '"' +} + +# Extract error message from API response JSON +_byteplus_extract_error() { + _code=$(echo "$1" | _egrep_o '"Code"\s*:\s*"[^"]*"' | head -1 | _egrep_o '"[^"]*"$' | tr -d '"') + _msg=$(echo "$1" | _egrep_o '"Message"\s*:\s*"[^"]*"' | head -1 | _egrep_o '"[^"]*"$' | tr -d '"') + if [ -n "$_code" ]; then + printf '%s — %s' "$_code" "$_msg" + else + printf '%s' "$1" + fi +} + +# ══════════════════════════════════════════════════════════════════════════════ +# BytePlus ALB API caller +# ══════════════════════════════════════════════════════════════════════════════ + +# Usage: _byteplus_alb_api ACTION [param1=val1] [param2=val2] ... +# All parameters sent via GET query string. Signing: HMAC-SHA256, host;x-date. +_byteplus_alb_api() { + _action="$1" + shift + + # Build query string — all params go in URL + _query_params="Action=${_action}&Version=2020-04-01" + + for _param in "$@"; do + _pname="${_param%%=*}" + _pval="${_param#*=}" + _query_params="${_query_params}&${_pname}=$(_byteplus_urlencode "$_pval")" + done + + # Timestamps + _x_date=$(date -u +%Y%m%dT%H%M%SZ) + _date_only=$(date -u +%Y%m%d) + + # Sort query params for canonical request + _sorted_query=$(echo "$_query_params" | tr '&' '\n' | sort | tr '\n' '&' | sed 's/&$//') + + # Canonical headers — only host and x-date + _canonical_headers="host:${_BYTEPLUS_HOST} +x-date:${_x_date} +" + _signed_headers="host;x-date" + + # Canonical request + _canonical_request="GET +/ +${_sorted_query} +${_canonical_headers} +${_signed_headers} +${_BYTEPLUS_EMPTY_HASH}" + + _debug2 _canonical_request "$_canonical_request" + + # Hash of canonical request + _cr_hash=$(printf '%s' "$_canonical_request" | openssl dgst -sha256 | awk '{print $NF}') + + # Credential scope + _credential_scope="${_date_only}/${BYTEPLUS_REGION}/${_BYTEPLUS_SERVICE}/request" + + # String to sign + _string_to_sign="HMAC-SHA256 +${_x_date} +${_credential_scope} +${_cr_hash}" + + _debug2 _string_to_sign "$_string_to_sign" + + # Signing key derivation (HMAC chain) + _k_date=$(printf '%s' "$_date_only" | openssl dgst -sha256 -hmac "$BYTEPLUS_SECRET_KEY" | awk '{print $NF}') + _k_region=$(printf '%s' "$BYTEPLUS_REGION" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_date}" | awk '{print $NF}') + _k_service=$(printf '%s' "$_BYTEPLUS_SERVICE" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_region}" | awk '{print $NF}') + _k_signing=$(printf '%s' "request" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_service}" | awk '{print $NF}') + + # Final signature + _signature=$(printf '%s' "$_string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_signing}" | awk '{print $NF}') + + # Authorization header + _auth="HMAC-SHA256 Credential=${BYTEPLUS_ACCESS_KEY}/${_credential_scope}, SignedHeaders=${_signed_headers}, Signature=${_signature}" + + _debug2 _auth "$_auth" + + # Build URL and execute GET request + _url="https://${_BYTEPLUS_HOST}/?${_sorted_query}" + + _response=$(curl -s --connect-timeout 10 --max-time 60 -X GET \ + -H "Authorization: ${_auth}" \ + -H "X-Date: ${_x_date}" \ + -H "Host: ${_BYTEPLUS_HOST}" \ + "${_url}") + + _debug2 "_byteplus_alb_api response [$_action]" "$_response" + printf '%s' "$_response" +} + +# ══════════════════════════════════════════════════════════════════════════════ +# URL encode (RFC 3986) — awk-based for performance +# ══════════════════════════════════════════════════════════════════════════════ + +_byteplus_urlencode() { + printf '%s' "$1" | awk 'BEGIN { + for (i = 0; i <= 255; i++) { + c = sprintf("%c", i) + if (c ~ /[a-zA-Z0-9.~_\-]/) + safe[i] = c + else + safe[i] = sprintf("%%%02X", i) + } + } + { + n = length($0) + for (i = 1; i <= n; i++) { + c = substr($0, i, 1) + printf "%s", safe[ord(c)] + } + # Print newline as %0A (except trailing, which command substitution strips) + if (NR > 0) printf "%%0A" + } + function ord(c, i2) { + for (i2 = 0; i2 <= 255; i2++) + if (sprintf("%c", i2) == c) return i2 + return 0 + } + END { }' | sed 's/%0A$//' +} From 86d98b046189f38162a61b49bc3177cac1cfa7ff Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:55:12 +0700 Subject: [PATCH 02/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 0cffa750..642f8bc4 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -139,8 +139,8 @@ byteplus_alb_deploy() { _date_tag=$(date -u +%Y%m%d-%H%M) # Replace wildcard * and dots for a valid cert name _safe_domain=$(echo "$_cdomain" | sed 's/\*\.//g' | sed 's/\./-/g') - # Underscore version for bash variable names (hyphens not allowed in var names) - _conf_key=$(echo "$_cdomain" | sed 's/\*\.//g' | sed 's/\./_/g') + # Safe identifier version for deployconf keys: map all non [A-Za-z0-9_] to _ + _conf_key=$(echo "$_cdomain" | sed 's/^\*\.//' | sed 's/[^A-Za-z0-9_]/_/g') if [ -z "$BYTEPLUS_CERT_NAME" ]; then BYTEPLUS_CERT_NAME="acme-${_safe_domain}-${_date_tag}" From 044371b00a53dcedcb26980101616f2ccf6fe959 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:57:10 +0700 Subject: [PATCH 03/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 52 +++++------------------------------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 642f8bc4..c23b4479 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -191,52 +191,12 @@ byteplus_alb_deploy() { # ══════════════════════════════════════════════════════════════════════════════ _byteplus_first_time_deploy() { - _info "No previous CertificateId found. Uploading new certificate..." - - if [ -n "$BYTEPLUS_PROJECT_NAME" ]; then - _upload_response=$(_byteplus_alb_api "UploadCertificate" \ - "CertificateType=Server" \ - "CertificateName=${BYTEPLUS_CERT_NAME}" \ - "ProjectName=${BYTEPLUS_PROJECT_NAME}" \ - "PublicKey=${_public_key}" \ - "PrivateKey=${_private_key}") - else - _upload_response=$(_byteplus_alb_api "UploadCertificate" \ - "CertificateType=Server" \ - "CertificateName=${BYTEPLUS_CERT_NAME}" \ - "PublicKey=${_public_key}" \ - "PrivateKey=${_private_key}") - fi - - _debug2 _upload_response "$_upload_response" - - _new_cert_id=$(_byteplus_extract_cert_id "$_upload_response") - - if [ -z "$_new_cert_id" ]; then - _err "UploadCertificate failed: $(_byteplus_extract_error "$_upload_response")" - _debug2 "Full response" "$_upload_response" - return 1 - fi - - _info "Certificate uploaded. CertificateId: $_new_cert_id" - - # Set description if provided - if [ -n "$BYTEPLUS_CERT_DESCRIPTION" ]; then - _info "Setting certificate description..." - _byteplus_alb_api "ModifyCertificateAttributes" \ - "CertificateId=${_new_cert_id}" \ - "CertificateName=${BYTEPLUS_CERT_NAME}" \ - "Description=${BYTEPLUS_CERT_DESCRIPTION}" >/dev/null - fi - - _info "" - _info "╔══════════════════════════════════════════════════════════════════╗" - _info "║ ACTION REQUIRED (one-time only) ║" - _info "║ Assign CertificateId '$_new_cert_id'" - _info "║ to your ALB Listener in BytePlus Console. ║" - _info "║ After that, all future renewals will be fully automatic. ║" - _info "╚══════════════════════════════════════════════════════════════════╝" - _info "" + _info "No previous CertificateId found." + _err "Refusing to upload certificate material because this hook passes PublicKey/PrivateKey as request parameters." + _err "Uploading a private key in the request URL can leak it via logs, proxies, and process listings." + _err "Please upload the certificate to BytePlus manually for the initial deployment, set BYTEPLUS_CERT_ID, and rerun." + _err "This hook must be updated to send PublicKey and PrivateKey in a POST body before automatic first-time upload can be enabled safely." + return 1 } # ══════════════════════════════════════════════════════════════════════════════ From 4178c33524c2b360fc07c04fedb3a071cdccfa81 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:57:37 +0700 Subject: [PATCH 04/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index c23b4479..f87be9a0 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -367,12 +367,24 @@ ${_cr_hash}" # Build URL and execute GET request _url="https://${_BYTEPLUS_HOST}/?${_sorted_query}" - _response=$(curl -s --connect-timeout 10 --max-time 60 -X GET \ - -H "Authorization: ${_auth}" \ - -H "X-Date: ${_x_date}" \ - -H "Host: ${_BYTEPLUS_HOST}" \ - "${_url}") + _saved_H1="${_H1:-}" + _saved_H2="${_H2:-}" + _saved_H3="${_H3:-}" + _H1="Authorization: ${_auth}" + _H2="X-Date: ${_x_date}" + _H3="Host: ${_BYTEPLUS_HOST}" + _response="$(_get "$_url")" + _request_ret="$?" + + _H1="$_saved_H1" + _H2="$_saved_H2" + _H3="$_saved_H3" + + if [ "$_request_ret" != "0" ]; then + _err "byteplus_alb_api request failed for [$_action]" + return 1 + fi _debug2 "_byteplus_alb_api response [$_action]" "$_response" printf '%s' "$_response" } From d5c8060a65f9ecd92fec34db16dd3b575c118394 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:58:15 +0700 Subject: [PATCH 05/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index f87be9a0..ab6decc5 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -390,32 +390,9 @@ ${_cr_hash}" } # ══════════════════════════════════════════════════════════════════════════════ -# URL encode (RFC 3986) — awk-based for performance +# URL encode (RFC 3986) # ══════════════════════════════════════════════════════════════════════════════ _byteplus_urlencode() { - printf '%s' "$1" | awk 'BEGIN { - for (i = 0; i <= 255; i++) { - c = sprintf("%c", i) - if (c ~ /[a-zA-Z0-9.~_\-]/) - safe[i] = c - else - safe[i] = sprintf("%%%02X", i) - } - } - { - n = length($0) - for (i = 1; i <= n; i++) { - c = substr($0, i, 1) - printf "%s", safe[ord(c)] - } - # Print newline as %0A (except trailing, which command substitution strips) - if (NR > 0) printf "%%0A" - } - function ord(c, i2) { - for (i2 = 0; i2 <= 255; i2++) - if (sprintf("%c", i2) == c) return i2 - return 0 - } - END { }' | sed 's/%0A$//' + _url_encode "$1" } From ad71a785ec454b22c421d7c349a6119afe961735 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:58:28 +0700 Subject: [PATCH 06/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index ab6decc5..f3b99232 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # shellcheck disable=SC2034,SC2154 # # acme.sh deploy hook: BytePlus Application Load Balancer (ALB) From 28f1f07f49983af7ec839325ca3d2ad38e5fb1eb Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:58:52 +0700 Subject: [PATCH 07/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index f3b99232..196f708b 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -318,7 +318,7 @@ _byteplus_alb_api() { _date_only=$(date -u +%Y%m%d) # Sort query params for canonical request - _sorted_query=$(echo "$_query_params" | tr '&' '\n' | sort | tr '\n' '&' | sed 's/&$//') + _sorted_query=$(echo "$_query_params" | tr '&' '\n' | LC_ALL=C sort | tr '\n' '&' | sed 's/&$//') # Canonical headers — only host and x-date _canonical_headers="host:${_BYTEPLUS_HOST} From 8eea7ca307abd365ffffd25ee8a7a648894c1ded Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:59:17 +0700 Subject: [PATCH 08/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 196f708b..6a01184c 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -334,7 +334,8 @@ ${_canonical_headers} ${_signed_headers} ${_BYTEPLUS_EMPTY_HASH}" - _debug2 _canonical_request "$_canonical_request" + # Do not log _canonical_request because the query string may contain + # URL-encoded certificate or private key material. # Hash of canonical request _cr_hash=$(printf '%s' "$_canonical_request" | openssl dgst -sha256 | awk '{print $NF}') From 934870fc7769cab740759175ce42560bf8814dcf Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:59:26 +0700 Subject: [PATCH 09/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 6a01184c..3d8818f1 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -338,7 +338,7 @@ ${_BYTEPLUS_EMPTY_HASH}" # URL-encoded certificate or private key material. # Hash of canonical request - _cr_hash=$(printf '%s' "$_canonical_request" | openssl dgst -sha256 | awk '{print $NF}') + _cr_hash=$(_digest "sha256" "hex" "$_canonical_request") # Credential scope _credential_scope="${_date_only}/${BYTEPLUS_REGION}/${_BYTEPLUS_SERVICE}/request" From 8587c3e74467e21998074a9942abe4790cbda90e Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 12:00:39 +0700 Subject: [PATCH 10/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 3d8818f1..30cd89a5 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -252,14 +252,14 @@ _byteplus_delete_old_cert() { _del_cert_id="$1" _info "Waiting 5s for cert status to settle..." - sleep 5 + _sleep 5 _info "Deleting old certificate '$_del_cert_id'..." _del_response=$(_byteplus_alb_api "DeleteCertificate" "CertificateId=${_del_cert_id}") if echo "$_del_response" | grep -q '"Error"'; then _info "Delete failed, retrying in 10s..." - sleep 10 + _sleep 10 _del_response=$(_byteplus_alb_api "DeleteCertificate" "CertificateId=${_del_cert_id}") if echo "$_del_response" | grep -q '"Error"'; then From d0e123cb027aa24b5615a4ddbc101362c2256fbe Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 12:01:06 +0700 Subject: [PATCH 11/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 30cd89a5..26661918 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -363,7 +363,7 @@ ${_cr_hash}" # Authorization header _auth="HMAC-SHA256 Credential=${BYTEPLUS_ACCESS_KEY}/${_credential_scope}, SignedHeaders=${_signed_headers}, Signature=${_signature}" - _debug2 _auth "$_auth" + _secure_debug2 _auth "$_auth" # Build URL and execute GET request _url="https://${_BYTEPLUS_HOST}/?${_sorted_query}" From 668427f2855ca67cfee80841e32ccd6c2df35dc5 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Sun, 5 Apr 2026 12:01:28 +0700 Subject: [PATCH 12/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 26661918..eada236c 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -147,7 +147,15 @@ byteplus_alb_deploy() { fi # Enforce BytePlus naming rules: start with letter, max 128 chars - BYTEPLUS_CERT_NAME=$(echo "$BYTEPLUS_CERT_NAME" | sed 's/[^a-zA-Z0-9._-]/-/g' | cut -c1-128) + BYTEPLUS_CERT_NAME=$(echo "$BYTEPLUS_CERT_NAME" | sed 's/[^A-Za-z0-9._-]/-/g') + case "$BYTEPLUS_CERT_NAME" in + [A-Za-z]*) + ;; + *) + BYTEPLUS_CERT_NAME="a$BYTEPLUS_CERT_NAME" + ;; + esac + BYTEPLUS_CERT_NAME=$(echo "$BYTEPLUS_CERT_NAME" | cut -c1-128) _info "Certificate name: $BYTEPLUS_CERT_NAME" From 75642a125216b19e52eb77770b0c883de5d0d83e Mon Sep 17 00:00:00 2001 From: Achmad Alif Nasrulloh Date: Sun, 5 Apr 2026 12:11:31 +0700 Subject: [PATCH 13/21] Update bteplus_alb.sh --- deploy/byteplus_alb.sh | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index eada236c..31831b72 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -346,7 +346,8 @@ ${_BYTEPLUS_EMPTY_HASH}" # URL-encoded certificate or private key material. # Hash of canonical request - _cr_hash=$(_digest "sha256" "hex" "$_canonical_request") + # _digest is provided by acme.sh and works across OpenSSL versions. + _cr_hash=$(printf '%s' "$_canonical_request" | _digest sha256 hex) # Credential scope _credential_scope="${_date_only}/${BYTEPLUS_REGION}/${_BYTEPLUS_SERVICE}/request" @@ -360,20 +361,29 @@ ${_cr_hash}" _debug2 _string_to_sign "$_string_to_sign" # Signing key derivation (HMAC chain) - _k_date=$(printf '%s' "$_date_only" | openssl dgst -sha256 -hmac "$BYTEPLUS_SECRET_KEY" | awk '{print $NF}') - _k_region=$(printf '%s' "$BYTEPLUS_REGION" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_date}" | awk '{print $NF}') - _k_service=$(printf '%s' "$_BYTEPLUS_SERVICE" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_region}" | awk '{print $NF}') - _k_signing=$(printf '%s' "request" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_service}" | awk '{print $NF}') + # _hmac reads data from stdin and returns a hex digest. + # acme.sh's _hmac abstracts away OpenSSL version differences, so this works + # on both modern (-mac HMAC -macopt hexkey:) and older (-hmac) OpenSSL builds. + # + # The first step seeds the chain from the raw secret key, so we convert it + # to hex first with _hex_dump (also an acme.sh built-in). + _secret_hex=$(printf '%s' "$BYTEPLUS_SECRET_KEY" | _hex_dump | tr -d ' \n') + _k_date=$(printf '%s' "$_date_only" | _hmac sha256 "$_secret_hex" hex) + _k_region=$(printf '%s' "$BYTEPLUS_REGION" | _hmac sha256 "$_k_date" hex) + _k_service=$(printf '%s' "$_BYTEPLUS_SERVICE" | _hmac sha256 "$_k_region" hex) + _k_signing=$(printf '%s' "request" | _hmac sha256 "$_k_service" hex) # Final signature - _signature=$(printf '%s' "$_string_to_sign" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${_k_signing}" | awk '{print $NF}') + _signature=$(printf '%s' "$_string_to_sign" | _hmac sha256 "$_k_signing" hex) # Authorization header _auth="HMAC-SHA256 Credential=${BYTEPLUS_ACCESS_KEY}/${_credential_scope}, SignedHeaders=${_signed_headers}, Signature=${_signature}" _secure_debug2 _auth "$_auth" - # Build URL and execute GET request + # Build URL and execute GET request via acme.sh's _get helper. + # _get handles exit-status checking, respects _H1/_H2/_H3 extra headers, + # and provides consistent error handling across platforms. _url="https://${_BYTEPLUS_HOST}/?${_sorted_query}" _saved_H1="${_H1:-}" From a739bf3e3adc421cbcc8bce2c6e0a61d39f7b21c Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:42:55 +0700 Subject: [PATCH 14/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 41 ++++------------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 31831b72..abaf443e 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -213,43 +213,10 @@ _byteplus_first_time_deploy() { _byteplus_renewal_deploy() { _info "Replacing old certificate '$_old_cert_id' (UpdateMode=new)..." - - if [ -n "$BYTEPLUS_PROJECT_NAME" ]; then - _replace_response=$(_byteplus_alb_api "ReplaceCertificate" \ - "OldCertificateId=${_old_cert_id}" \ - "UpdateMode=new" \ - "CertificateName=${BYTEPLUS_CERT_NAME}" \ - "ProjectName=${BYTEPLUS_PROJECT_NAME}" \ - "PublicKey=${_public_key}" \ - "PrivateKey=${_private_key}") - else - _replace_response=$(_byteplus_alb_api "ReplaceCertificate" \ - "OldCertificateId=${_old_cert_id}" \ - "UpdateMode=new" \ - "CertificateName=${BYTEPLUS_CERT_NAME}" \ - "PublicKey=${_public_key}" \ - "PrivateKey=${_private_key}") - fi - - _debug2 _replace_response "$_replace_response" - - _new_cert_id=$(_byteplus_extract_cert_id "$_replace_response") - - if [ -z "$_new_cert_id" ]; then - _err "ReplaceCertificate failed: $(_byteplus_extract_error "$_replace_response")" - _debug2 "Full response" "$_replace_response" - return 1 - fi - - _info "Certificate replaced successfully on all attached listeners." - _info "New CertificateId: $_new_cert_id" - - # Auto-cleanup old certificate - if [ "$BYTEPLUS_DELETE_OLD_CERT" = "true" ]; then - _byteplus_delete_old_cert "$_old_cert_id" - else - _info "Auto-delete disabled. Old certificate '$_old_cert_id' kept in inventory." - fi + _err "Refusing to replace certificate material because this hook passes PublicKey/PrivateKey as request parameters." + _err "Uploading a private key in the request URL can leak it via logs, proxies, and process listings." + _err "Please replace the certificate in BytePlus manually for renewal until this hook is updated to send PublicKey and PrivateKey in a POST body safely." + return 1 } # ══════════════════════════════════════════════════════════════════════════════ From 3843495397058dece66891e4cff564d80a6d861b Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:43:11 +0700 Subject: [PATCH 15/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index abaf443e..42b544c7 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -348,24 +348,28 @@ ${_cr_hash}" _secure_debug2 _auth "$_auth" - # Build URL and execute GET request via acme.sh's _get helper. - # _get handles exit-status checking, respects _H1/_H2/_H3 extra headers, - # and provides consistent error handling across platforms. - _url="https://${_BYTEPLUS_HOST}/?${_sorted_query}" + # Send request parameters in the POST body instead of the URL query string. + # This avoids exposing sensitive or large values in debug-logged URLs and + # reduces the risk of exceeding URL length limits. + _url="https://${_BYTEPLUS_HOST}/" + _body="$_sorted_query" _saved_H1="${_H1:-}" _saved_H2="${_H2:-}" _saved_H3="${_H3:-}" + _saved_H4="${_H4:-}" _H1="Authorization: ${_auth}" _H2="X-Date: ${_x_date}" _H3="Host: ${_BYTEPLUS_HOST}" - _response="$(_get "$_url")" + _H4="Content-Type: application/x-www-form-urlencoded" + _response="$(_post "$_body" "$_url" "" "POST")" _request_ret="$?" _H1="$_saved_H1" _H2="$_saved_H2" _H3="$_saved_H3" + _H4="$_saved_H4" if [ "$_request_ret" != "0" ]; then _err "byteplus_alb_api request failed for [$_action]" From 5c94af86f381a52d3960269b5d4f3d26646f1512 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:43:25 +0700 Subject: [PATCH 16/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 42b544c7..28c229e8 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -384,5 +384,5 @@ ${_cr_hash}" # ══════════════════════════════════════════════════════════════════════════════ _byteplus_urlencode() { - _url_encode "$1" + printf '%s' "$1" | _url_encode } From a1b94db94d11fbabbcc41c94b0061bfc2cd74c31 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:43:37 +0700 Subject: [PATCH 17/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 28c229e8..76b3b381 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -5,16 +5,18 @@ # https://github.com/acmesh-official/acme.sh/wiki/deployhooks # # Deploys SSL/TLS certificates issued by acme.sh to BytePlus ALB. -# Supports automatic renewal with zero-downtime certificate rotation. +# Supports automatic renewal with zero-downtime certificate rotation +# for certificates that have already been uploaded and have a saved +# BytePlus CertificateId. # # ┌─────────────────────────────────────────────────────────────────────┐ # │ FIRST TIME (new domain) │ # │ 1. acme.sh --issue -d example.com -w /var/www/html/ │ -# │ 2. acme.sh --deploy -d example.com --deploy-hook byteplus_alb │ -# │ → UploadCertificate → saves CertificateId │ -# │ 3. Manually assign cert to ALB Listener (one-time only) │ +# │ 2. Upload/import the certificate to BytePlus ALB manually │ +# │ 3. Save/configure the existing CertificateId for this hook │ +# │ 4. Manually assign cert to ALB Listener (one-time only) │ # │ │ -# │ RENEWAL (fully automatic) │ +# │ RENEWAL (fully automatic after CertificateId is configured) │ # │ acme.sh cron triggers renew → deploy hook runs automatically │ # │ → ReplaceCertificate (UpdateMode=new) — single API call │ # │ → All attached listeners updated, old cert auto-deleted │ From 73a682e561ce3e7ae6618e8a86b2f8897250aa9f Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:43:52 +0700 Subject: [PATCH 18/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 76b3b381..35db4c64 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -204,7 +204,8 @@ _byteplus_first_time_deploy() { _info "No previous CertificateId found." _err "Refusing to upload certificate material because this hook passes PublicKey/PrivateKey as request parameters." _err "Uploading a private key in the request URL can leak it via logs, proxies, and process listings." - _err "Please upload the certificate to BytePlus manually for the initial deployment, set BYTEPLUS_CERT_ID, and rerun." + _err "Please upload the certificate to BytePlus manually for the initial deployment, set BYTEPLUS_CERT_ID_${_conf_key} to that CertificateId, and rerun." + _err "This hook stores CertificateId values per domain using deployconf, so the variable name must include the current domain-specific suffix." _err "This hook must be updated to send PublicKey and PrivateKey in a POST body before automatic first-time upload can be enabled safely." return 1 } From 00090d24b8aee4cc1d7f500cb575b5581413b2a5 Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:44:19 +0700 Subject: [PATCH 19/21] Update deploy/byteplus_alb.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deploy/byteplus_alb.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index 35db4c64..fd333703 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -83,7 +83,6 @@ byteplus_alb_deploy() { _getdeployconf BYTEPLUS_PROJECT_NAME _getdeployconf BYTEPLUS_DELETE_OLD_CERT _getdeployconf BYTEPLUS_CERT_NAME - _getdeployconf BYTEPLUS_CERT_DESCRIPTION # Restore from environment if _getdeployconf cleared them if [ -z "$BYTEPLUS_PROJECT_NAME" ] && [ -n "$_env_project_name" ]; then From f89a9a5de3438f34656c9d23d75e4e9f61e7390d Mon Sep 17 00:00:00 2001 From: ACHMAD ALIF NASRULLOH <106044706+achmadalifn4@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:56:51 +0700 Subject: [PATCH 20/21] Add new header variable _H5 in byteplus_alb.sh Added a new header variable _H5 to the byteplus_alb.sh script. --- deploy/byteplus_alb.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index fd333703..e89ab966 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -360,11 +360,14 @@ ${_cr_hash}" _saved_H2="${_H2:-}" _saved_H3="${_H3:-}" _saved_H4="${_H4:-}" + _saved_H5="${_H5:-}" _H1="Authorization: ${_auth}" _H2="X-Date: ${_x_date}" _H3="Host: ${_BYTEPLUS_HOST}" _H4="Content-Type: application/x-www-form-urlencoded" + _H5="" + _response="$(_post "$_body" "$_url" "" "POST")" _request_ret="$?" @@ -372,6 +375,7 @@ ${_cr_hash}" _H2="$_saved_H2" _H3="$_saved_H3" _H4="$_saved_H4" + _H5="$_saved_H5" if [ "$_request_ret" != "0" ]; then _err "byteplus_alb_api request failed for [$_action]" From e9b0cafac52673f5c9aac96917a55356b44b82a0 Mon Sep 17 00:00:00 2001 From: Achmad Alif Nasrulloh Date: Fri, 24 Apr 2026 11:21:43 +0700 Subject: [PATCH 21/21] Fix byteplus_alb.sh --- deploy/byteplus_alb.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh index e89ab966..8443bb99 100644 --- a/deploy/byteplus_alb.sh +++ b/deploy/byteplus_alb.sh @@ -150,11 +150,11 @@ byteplus_alb_deploy() { # Enforce BytePlus naming rules: start with letter, max 128 chars BYTEPLUS_CERT_NAME=$(echo "$BYTEPLUS_CERT_NAME" | sed 's/[^A-Za-z0-9._-]/-/g') case "$BYTEPLUS_CERT_NAME" in - [A-Za-z]*) - ;; - *) - BYTEPLUS_CERT_NAME="a$BYTEPLUS_CERT_NAME" - ;; + [A-Za-z]*) ;; + + *) + BYTEPLUS_CERT_NAME="a$BYTEPLUS_CERT_NAME" + ;; esac BYTEPLUS_CERT_NAME=$(echo "$BYTEPLUS_CERT_NAME" | cut -c1-128) @@ -337,10 +337,10 @@ ${_cr_hash}" # The first step seeds the chain from the raw secret key, so we convert it # to hex first with _hex_dump (also an acme.sh built-in). _secret_hex=$(printf '%s' "$BYTEPLUS_SECRET_KEY" | _hex_dump | tr -d ' \n') - _k_date=$(printf '%s' "$_date_only" | _hmac sha256 "$_secret_hex" hex) - _k_region=$(printf '%s' "$BYTEPLUS_REGION" | _hmac sha256 "$_k_date" hex) - _k_service=$(printf '%s' "$_BYTEPLUS_SERVICE" | _hmac sha256 "$_k_region" hex) - _k_signing=$(printf '%s' "request" | _hmac sha256 "$_k_service" hex) + _k_date=$(printf '%s' "$_date_only" | _hmac sha256 "$_secret_hex" hex) + _k_region=$(printf '%s' "$BYTEPLUS_REGION" | _hmac sha256 "$_k_date" hex) + _k_service=$(printf '%s' "$_BYTEPLUS_SERVICE" | _hmac sha256 "$_k_region" hex) + _k_signing=$(printf '%s' "request" | _hmac sha256 "$_k_service" hex) # Final signature _signature=$(printf '%s' "$_string_to_sign" | _hmac sha256 "$_k_signing" hex) @@ -367,7 +367,7 @@ ${_cr_hash}" _H3="Host: ${_BYTEPLUS_HOST}" _H4="Content-Type: application/x-www-form-urlencoded" _H5="" - + _response="$(_post "$_body" "$_url" "" "POST")" _request_ret="$?"