From 45b99821725ef3359497cfe3b7b34e31101a3052 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Tue, 15 Apr 2025 14:30:18 +0800 Subject: [PATCH 01/21] Add Spaceship DNS API --- dnsapi/dns_spaceship.sh | 197 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 dnsapi/dns_spaceship.sh diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh new file mode 100644 index 00000000..f94d9027 --- /dev/null +++ b/dnsapi/dns_spaceship.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env sh +# shellcheck disable=SC2034 +dns_spaceship_info='Spaceship.com +Site: Spaceship.com +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_spaceship +Options: + SPACESHIP_API_KEY Spaceship API Key + SPACESHIP_API_SECRET Spaceship API Secret + SPACESHIP_ROOT_DOMAIN (Optional) Manually specify the root domain if auto-detection fails +Issues: github.com/acmesh-official/acme.sh/issues/6304 +Author: Meow +' + +# Spaceship API +# https://docs.spaceship.dev/ + +######## Public functions ##################### + +SPACESHIP_API_BASE="https://spaceship.dev/api/v1" + +# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +# Used to add txt record +dns_spaceship_add() { + fulldomain="$1" + txtvalue="$2" + + _info "Adding TXT record for $fulldomain with value $txtvalue" + + # Initialize API credentials and headers + if ! _spaceship_init; then + return 1 + fi + + # Detect root zone + if ! _get_root "$fulldomain"; then + return 1 + fi + + # Extract subdomain part relative to root domain + subdomain=$(echo "$fulldomain" | sed "s/\.$_domain$//") + if [ "$subdomain" = "$fulldomain" ]; then + _err "Failed to extract subdomain from $fulldomain relative to root domain $_domain" + return 1 + fi + _debug "Extracted subdomain: $subdomain for root domain: $_domain" + + # Escape txtvalue to prevent JSON injection (e.g., quotes in txtvalue) + escaped_txtvalue=$(echo "$txtvalue" | sed 's/"/\\"/g') + + # Prepare payload and URL for adding TXT record + # Note: 'name' in payload uses subdomain (e.g., _acme-challenge.sub) as required by Spaceship API + payload="{\"force\": true, \"items\": [{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\", \"ttl\": 600}]}" + url="$SPACESHIP_API_BASE/dns/records/$_domain" + + # Send API request + if _spaceship_api_request "PUT" "$url" "$payload"; then + _info "Successfully added TXT record for $fulldomain" + return 0 + else + _err "Failed to add TXT record. If the domain $_domain is incorrect, set SPACESHIP_ROOT_DOMAIN to the correct root domain." + return 1 + fi +} + +# Usage: fulldomain txtvalue +# Used to remove the txt record after validation +dns_spaceship_rm() { + fulldomain="$1" + txtvalue="$2" + + _info "Removing TXT record for $fulldomain with value $txtvalue" + + # Initialize API credentials and headers + if ! _spaceship_init; then + return 1 + fi + + # Detect root zone + if ! _get_root "$fulldomain"; then + return 1 + fi + + # Extract subdomain part relative to root domain + subdomain=$(echo "$fulldomain" | sed "s/\.$_domain$//") + if [ "$subdomain" = "$fulldomain" ]; then + _err "Failed to extract subdomain from $fulldomain relative to root domain $_domain" + return 1 + fi + _debug "Extracted subdomain: $subdomain for root domain: $_domain" + + # Escape txtvalue to prevent JSON injection + escaped_txtvalue=$(echo "$txtvalue" | sed 's/"/\\"/g') + + # Prepare payload and URL for deleting TXT record + # Note: 'name' in payload uses subdomain (e.g., _acme-challenge.sub) as required by Spaceship API + payload="{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\"}" + url="$SPACESHIP_API_BASE/dns/records/$_domain" + + # Send API request + if _spaceship_api_request "DELETE" "$url" "$payload"; then + _info "Successfully deleted TXT record for $fulldomain" + return 0 + else + _err "Failed to delete TXT record. If the domain $_domain is incorrect, set SPACESHIP_ROOT_DOMAIN to the correct root domain." + return 1 + fi +} + +#################### Private functions below ################################## + +_spaceship_init() { + SPACESHIP_API_KEY="${SPACESHIP_API_KEY:-$(_readaccountconf_mutable SPACESHIP_API_KEY)}" + SPACESHIP_API_SECRET="${SPACESHIP_API_SECRET:-$(_readaccountconf_mutable SPACESHIP_API_SECRET)}" + + if [ -z "$SPACESHIP_API_KEY" ] || [ -z "$SPACESHIP_API_SECRET" ]; then + _err "Spaceship API credentials are not set. Please set SPACESHIP_API_KEY and SPACESHIP_API_SECRET." + _err "Ensure ~/.acme.sh directory has restricted permissions (chmod 700 ~/.acme.sh) to protect credentials." + return 1 + fi + + # Save credentials to account config for future renewals + _saveaccountconf_mutable SPACESHIP_API_KEY "$SPACESHIP_API_KEY" + _saveaccountconf_mutable SPACESHIP_API_SECRET "$SPACESHIP_API_SECRET" + + # Set common headers for API requests + export _H1="X-API-Key: $SPACESHIP_API_KEY" + export _H2="X-API-Secret: $SPACESHIP_API_SECRET" + export _H3="Content-Type: application/json" + return 0 +} + +_get_root() { + domain="$1" + + # Check if user manually specified root domain + SPACESHIP_ROOT_DOMAIN="${SPACESHIP_ROOT_DOMAIN:-$(_readaccountconf_mutable SPACESHIP_ROOT_DOMAIN)}" + if [ -n "$SPACESHIP_ROOT_DOMAIN" ]; then + _domain="$SPACESHIP_ROOT_DOMAIN" + _debug "Using manually specified or saved root domain: $_domain" + # Ensure it's saved (in case it was read from config but not saved previously) + _saveaccountconf_mutable SPACESHIP_ROOT_DOMAIN "$SPACESHIP_ROOT_DOMAIN" + return 0 + fi + + # Split domain into parts and try from back to front + _debug "Detecting root zone for $domain from back to front" + _parts=$(echo "$domain" | tr '.' '\n' | wc -l) + if [ "$_parts" -lt 2 ]; then + _err "Invalid domain format for $domain" + return 1 + fi + + # Start with the last 2 parts (e.g., example.com) and move forward + i=2 + max_attempts=$((_parts + 1)) + while [ $i -le $max_attempts ]; do + _cutdomain=$(echo "$domain" | rev | cut -d . -f 1-$i | rev) + if [ -z "$_cutdomain" ]; then + break + fi + + _debug "Checking if $_cutdomain is root zone" + if _spaceship_api_request "GET" "$SPACESHIP_API_BASE/dns/records/$_cutdomain?take=1&skip=0"; then + _domain="$_cutdomain" + _debug "Root zone found: $_domain" + # Save the detected root domain to configuration for future use + _saveaccountconf_mutable SPACESHIP_ROOT_DOMAIN "$_domain" + _info "Root domain $_domain saved to configuration for future use." + return 0 + fi + i=$((i + 1)) + done + + _err "Could not detect root zone for $domain after $max_attempts attempts. Please set SPACESHIP_ROOT_DOMAIN manually." + return 1 +} + +_spaceship_api_request() { + method="$1" + url="$2" + payload="$3" + + _debug "Sending $method request to $url with payload $payload" + if [ "$method" = "GET" ]; then + response="$(_get "$url")" + else + response="$(_post "$payload" "$url" "" "$method")" + fi + + if [ "$?" != "0" ]; then + _err "API request failed. Response: $response" + return 1 + fi + + _debug "API response: $response" + return 0 +} From 5e8b40faf65fe2d712698798a25aa209a1c0bdd9 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:10:51 +0800 Subject: [PATCH 02/21] Spaceship: fix rm --- dnsapi/dns_spaceship.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index f94d9027..53dece76 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -93,7 +93,7 @@ dns_spaceship_rm() { # Prepare payload and URL for deleting TXT record # Note: 'name' in payload uses subdomain (e.g., _acme-challenge.sub) as required by Spaceship API - payload="{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\"}" + payload="[{\"type\": \"TXT\", \"name\": \"$subdomain\", \"value\": \"$escaped_txtvalue\"}]" url="$SPACESHIP_API_BASE/dns/records/$_domain" # Send API request @@ -156,6 +156,7 @@ _get_root() { while [ $i -le $max_attempts ]; do _cutdomain=$(echo "$domain" | rev | cut -d . -f 1-$i | rev) if [ -z "$_cutdomain" ]; then + _debug "Reached end of domain parts." break fi From e55a54f3d4cfb92102f3a5036882a64100f563a0 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:30:43 +0800 Subject: [PATCH 03/21] Spaceship: fix get_root --- dnsapi/dns_spaceship.sh | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index 53dece76..d70a9a81 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -132,47 +132,49 @@ _spaceship_init() { _get_root() { domain="$1" - # Check if user manually specified root domain + # Check manual override SPACESHIP_ROOT_DOMAIN="${SPACESHIP_ROOT_DOMAIN:-$(_readaccountconf_mutable SPACESHIP_ROOT_DOMAIN)}" if [ -n "$SPACESHIP_ROOT_DOMAIN" ]; then _domain="$SPACESHIP_ROOT_DOMAIN" _debug "Using manually specified or saved root domain: $_domain" - # Ensure it's saved (in case it was read from config but not saved previously) _saveaccountconf_mutable SPACESHIP_ROOT_DOMAIN "$SPACESHIP_ROOT_DOMAIN" return 0 fi - # Split domain into parts and try from back to front - _debug "Detecting root zone for $domain from back to front" - _parts=$(echo "$domain" | tr '.' '\n' | wc -l) - if [ "$_parts" -lt 2 ]; then - _err "Invalid domain format for $domain" - return 1 - fi + _debug "Detecting root zone for '$domain'" - # Start with the last 2 parts (e.g., example.com) and move forward i=2 - max_attempts=$((_parts + 1)) - while [ $i -le $max_attempts ]; do - _cutdomain=$(echo "$domain" | rev | cut -d . -f 1-$i | rev) + p=1 + while true; do + _cutdomain=$(printf "%s" "$domain" | cut -d . -f "$i"-100) + + _debug "Attempt i=$i: Checking if '$_cutdomain' is root zone (cut ret=$?)" + if [ -z "$_cutdomain" ]; then - _debug "Reached end of domain parts." + _debug "Cut resulted in empty string, root zone not found." break fi - _debug "Checking if $_cutdomain is root zone" + # Call the API to check if this _cutdomain is a manageable zone if _spaceship_api_request "GET" "$SPACESHIP_API_BASE/dns/records/$_cutdomain?take=1&skip=0"; then + # API call succeeded (HTTP 200 OK for GET /dns/records) _domain="$_cutdomain" - _debug "Root zone found: $_domain" - # Save the detected root domain to configuration for future use + _debug "Root zone found: '$_domain'" + + # Save the detected root domain _saveaccountconf_mutable SPACESHIP_ROOT_DOMAIN "$_domain" - _info "Root domain $_domain saved to configuration for future use." + _info "Root domain '$_domain' saved to configuration for future use." + return 0 fi + + _debug "API check failed for '$_cutdomain'. Continuing search." + + p=$i i=$((i + 1)) done - _err "Could not detect root zone for $domain after $max_attempts attempts. Please set SPACESHIP_ROOT_DOMAIN manually." + _err "Could not detect root zone for '$domain'. Please set SPACESHIP_ROOT_DOMAIN manually." return 1 } From 827315e059c1b8ceba9828a9cbaf062768eeec6d Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:49:48 +0800 Subject: [PATCH 04/21] Spaceship: valid api response --- dnsapi/dns_spaceship.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index d70a9a81..501131b8 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -195,6 +195,18 @@ _spaceship_api_request() { return 1 fi - _debug "API response: $response" - return 0 + _debug "API response body: $response" + + if [ "$method" = "GET" ]; then + if _contains "$(_head_n 1 <"$HTTP_HEADER")" '200'; then + return 0 + fi + else + if _contains "$(_head_n 1 <"$HTTP_HEADER")" '204'; then + return 0 + fi + fi + + _debug "API response header: $HTTP_HEADER" + return 1 } From 24a1b93842dda7ce74a83a6165e7a642c499df07 Mon Sep 17 00:00:00 2001 From: Joe Bauser Date: Sat, 2 Mar 2024 13:01:59 -0500 Subject: [PATCH 05/21] Add deploy/zyxel_gs1900.sh Add support for deploying to the Zyxel GS1900 line of switches as long as those switches are running at least firmware V2.80. Tested on a Zyxel GS1900-8 and GS1900-24E Resolves #5042 --- deploy/zyxel_gs1900.sh | 500 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 deploy/zyxel_gs1900.sh diff --git a/deploy/zyxel_gs1900.sh b/deploy/zyxel_gs1900.sh new file mode 100644 index 00000000..443a5b05 --- /dev/null +++ b/deploy/zyxel_gs1900.sh @@ -0,0 +1,500 @@ +#!/usr/bin/env sh + +# Deploy certificates to Zyxel GS1900 series switches +# +# This script uses the https web administration interface in order +# to upload updated certificates to Zyxel GS1900 series switches. +# Only a few models have been tested but untested switches from the +# same model line may work as well. If you test and confirm a switch +# as working please submit a pull request updating this compatibility +# list! +# +# Known Issues: +# 1. This is a consumer grade switch and is a bit underpowered +# the longer the RSA key size the slower your switch web UI +# will be. RSA 2048 will work, RSA 4096 will work but you may +# experience performance problems. +# 2. You must use RSA certificates. The switch will reject EC-256 +# and EC-384 certificates in firmware 2.80 +# See: https://community.zyxel.com/en/discussion/21506/bug-cannot-import-ssl-cert-on-gs1900-8-and-gs1900-24e-firmware-v2-80/ +# +# Current GS1900 Switch Compatibility: +# GS1900-8 - Working as of firmware V2.80 +# GS1900-8HP - Untested +# GS1900-10HP - Untested +# GS1900-16 - Untested +# GS1900-24 - Untested +# GS1900-24E - Working as of firmware V2.80 +# GS1900-24EP - Untested +# GS1900-24HP - Untested +# GS1900-48 - Untested +# GS1900-48HP - Untested +# +# Prerequisite Setup Steps: +# 1. Install at least firmware V2.80 on your switch +# 2. Enable HTTPS web management on your switch +# +# Usage: +# 1. Ensure the switch has firmware V2.80 or later. +# 2. Ensure the switch has HTTPS management enabled. +# 3. Set the appropriate environment variables for your environment. +# +# DEPLOY_ZYXEL_SWITCH - The switch hostname. (Default: _cdomain) +# DEPLOY_ZYXEL_SWITCH_USER - The webadmin user. (Default: admin) +# DEPLOY_ZYXEL_SWITCH_PASSWORD - The webadmin password for the switch. +# DEPLOY_ZYXEL_SWITCH_REBOOT - If "1" reboot after update. (Default: "0") +# +# 4. Run the deployment plugin: +# acme.sh --deploy --deploy-hook zyxel_gs1900 -d example.com +# +# returns 0 means success, otherwise error. + +#domain keyfile certfile cafile fullchain +zyxel_gs1900_deploy() { + _zyxel_gs1900_minimum_firmware_version="v2.80" + + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug2 _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + _getdeployconf DEPLOY_ZYXEL_SWITCH + _getdeployconf DEPLOY_ZYXEL_SWITCH_USER + _getdeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD + _getdeployconf DEPLOY_ZYXEL_SWITCH_REBOOT + + if [ -z "$DEPLOY_ZYXEL_SWITCH" ]; then + DEPLOY_ZYXEL_SWITCH="$_cdomain" + fi + + if [ -z "$DEPLOY_ZYXEL_SWITCH_USER" ]; then + DEPLOY_ZYXEL_SWITCH_USER="admin" + fi + + if [ -z "$DEPLOY_ZYXEL_SWITCH_PASSWORD" ]; then + DEPLOY_ZYXEL_SWITCH_PASSWORD="1234" + fi + + if [ -z "$DEPLOY_ZYXEL_SWITCH_REBOOT" ]; then + DEPLOY_ZYXEL_SWITCH_REBOOT="0" + fi + + _savedeployconf DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH" + _savedeployconf DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER" + _savedeployconf DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD" + _savedeployconf DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT" + + _debug DEPLOY_ZYXEL_SWITCH "$DEPLOY_ZYXEL_SWITCH" + _debug DEPLOY_ZYXEL_SWITCH_USER "$DEPLOY_ZYXEL_SWITCH_USER" + _secure_debug DEPLOY_ZYXEL_SWITCH_PASSWORD "$DEPLOY_ZYXEL_SWITCH_PASSWORD" + _debug DEPLOY_ZYXEL_SWITCH_REBOOT "$DEPLOY_ZYXEL_SWITCH_REBOOT" + + _zyxel_switch_base_uri="https://${DEPLOY_ZYXEL_SWITCH}" + + _info "Beginning to deploy to a Zyxel GS1900 series switch at ${_zyxel_switch_base_uri}." + _zyxel_gs1900_deployment_precheck || return $? + + _zyxel_gs1900_should_update + if [ "$?" != "0" ]; then + _info "The switch already has our certificate installed. No update required." + return 0 + else + _info "The switch does not yet have our certificate installed." + fi + + _info "Logging into the switch web interface." + _zyxel_gs1900_login || return $? + + _info "Validating the switch is compatible with this deployment process." + _zyxel_gs1900_validate_device_compatibility || return $? + + _info "Uploading the certificate." + _zyxel_gs1900_upload_certificate || return $? + + if [ "$DEPLOY_ZYXEL_SWITCH_REBOOT" = "1" ]; then + _info "Rebooting the switch." + _zyxel_gs1900_trigger_reboot || return $? + fi + + return 0 +} + +_zyxel_gs1900_deployment_precheck() { + # Initialize the keylength if it isn't already + if [ -z "$Le_Keylength" ]; then + Le_Keylength="" + fi + + if _isEccKey "$Le_Keylength"; then + _info "Warning: Zyxel GS1900 switches are not currently known to work with ECC keys!" + _info "You can continue, but your switch may reject your key." + elif [ -n "$Le_Keylength" ] && [ "$Le_Keylength" -gt "2048" ]; then + _info "Warning: Your RSA key length is greater than 2048!" + _info "You can continue, but you may experience performance issues in the web administration interface." + fi + + # Check the server for some common failure modes prior to authentication and certificate upload in order to avoid + # sending a certificate when we may not want to. + test_login_response=$(_post "username=test&password=test&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" 2>&1) + test_login_page_exitcode="$?" + _debug3 "Test Login Response: ${test_login_response}" + if [ "$test_login_page_exitcode" -ne "0" ]; then + if { [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "60" ]; } || { [ "${ACME_USE_WGET:-0}" = "1" ] && [ "$test_login_page_exitcode" = "5" ]; }; then + _err "The SSL certificate at $_zyxel_switch_base_uri could not be validated." + _err "Please double check your hostname, port, and that you are actually connecting to your switch." + _err "If the problem persists then please ensure that the certificate is not self-signed, has not" + _err "expired, and matches the switch hostname. If you expect validation to fail then you can disable" + _err "certificate validation by running with --insecure." + return 1 + elif [ "${ACME_USE_WGET:-0}" = "0" ] && [ "$test_login_page_exitcode" = "56" ]; then + _debug3 "Intentionally ignore curl exit code 56 in our precheck" + else + _err "Failed to submit the initial login attempt to $_zyxel_switch_base_uri." + return 1 + fi + fi +} + +_zyxel_gs1900_login() { + # Login to the switch and set the appropriate auth cookie in _H1 + username_encoded=$(printf "%s" "$DEPLOY_ZYXEL_SWITCH_USER" | _url_encode) + password_encoded=$(_zyxel_gs1900_password_obfuscate "$DEPLOY_ZYXEL_SWITCH_PASSWORD" | _url_encode) + + login_response=$(_post "username=${username_encoded}&password=${password_encoded}&login=true;" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n') + auth_response=$(_post "authId=${login_response}&login_chk=true" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=0.html" '' "POST" "application/x-www-form-urlencoded" | tr -d '\n') + if [ "$auth_response" != "OK" ]; then + _err "Login failed due to invalid credentials." + _err "Please double check the configured username and password and try again." + return 1 + fi + + sessionid=$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'HTTPS_XSSID=[^;]*;' | tr -d ';') + _secure_debug2 "sessionid" "$sessionid" + + export _H1="Cookie: $sessionid" + _secure_debug2 "_H1" "$_H1" + + return 0 +} + +_zyxel_gs1900_validate_device_compatibility() { + # Check the switches model and firmware version and throw errors + # if this script isn't compatible. + device_info_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=12" | tr -d '\n') + + model_name=$(_zyxel_gs1900_get_model "$device_info_html") + _debug2 "model_name" "$model_name" + if [ -z "$model_name" ]; then + _err "Could not find the switch model name." + _err "Please re-run with --debug and report a bug." + return $? + fi + + if ! expr "$model_name" : "GS1900-" >/dev/null; then + _err "Switch is an unsupported model: $model_name" + return 1 + fi + + firmware_version=$(_zyxel_gs1900_get_firmware_version "$device_info_html") + _debug2 "firmware_version" "$firmware_version" + if [ -z "$firmware_version" ]; then + _err "Could not find the switch firmware version." + _err "Please re-run with --debug and report a bug." + return $? + fi + + _debug2 "_zyxel_gs1900_minimum_firmware_version" "$_zyxel_gs1900_minimum_firmware_version" + minimum_major_version=$(_zyxel_gs1900_parse_major_version "$_zyxel_gs1900_minimum_firmware_version") + _debug2 "minimum_major_version" "$minimum_major_version" + minimum_minor_version=$(_zyxel_gs1900_parse_minor_version "$_zyxel_gs1900_minimum_firmware_version") + _debug2 "minimum_minor_version" "$minimum_minor_version" + + _debug2 "firmware_version" "$firmware_version" + firmware_major_version=$(_zyxel_gs1900_parse_major_version "$firmware_version") + _debug2 "firmware_major_version" "$firmware_major_version" + firmware_minor_version=$(_zyxel_gs1900_parse_minor_version "$firmware_version") + _debug2 "firmware_minor_version" "$firmware_minor_version" + + _ret=0 + if [ "$firmware_major_version" -lt "$minimum_major_version" ]; then + _ret=1 + elif [ "$firmware_major_version" -eq "$minimum_major_version" ] && [ "$firmware_minor_version" -lt "$minimum_minor_version" ]; then + _ret=1 + fi + + if [ "$_ret" != "0" ]; then + _err "Unsupported firmware version $firmware_version. Please upgrade to at least version $_zyxel_gs1900_minimum_firmware_version." + fi + + return $? +} + +_zyxel_gs1900_should_update() { + # Get the remote certificate serial number + _remote_cert=$(${ACME_OPENSSL_BIN:-openssl} s_client -showcerts -connect "${DEPLOY_ZYXEL_SWITCH}:443" 2>/dev/null "${upload_post_request}" + + _info "Upload certificate to the switch" + + # Unfortunately we cannot rely upon the switch response across switch models + # to return a consistent body return - so we cannot inspect the result of this + # upload to determine success. + upload_response=$(_zyxel_upload_pkcs12 "${upload_post_request}" "${upload_post_boundary}" 2>&1) + _debug3 "Upload response: ${upload_response}" + rm "${upload_post_request}" + + # Pause for a few seconds to give the switch a chance to process the certificate + # For some reason I've found this to be necessary on my GS1900-24E + _debug2 "Waiting 4 seconds for the switch to process the newly uploaded certificate." + sleep "4" + + # Check to see whether or not our update was successful + _ret=0 + _zyxel_gs1900_should_update + if [ "$?" != "0" ]; then + _info "The certificate was updated successfully" + else + _ret=1 + _err "The certificate upload does not appear to have worked." + _err "The remote certificate does not match the certificate we tried to upload." + _err "Please re-run with --debug 2 and review for unexpected errors. If none can be found please submit a bug." + fi + + # ensure the temporary files are cleaned up + [ -f "${temp_pkcs12}" ] && rm -f "${temp_pkcs12}" + + return $_ret +} + +# make the certificate upload request using either +# --data binary with @ for file access in CURL +# or using --post-file for wget to ensure we upload +# the pkcs12 without getting tripped up on null bytes +# +# Usage _zyxel_upload_pkcs12 [body file name] [post boundary marker] +_zyxel_upload_pkcs12() { + bodyfilename="$1" + multipartformmarker="$2" + _post_url="${_zyxel_switch_base_uri}/cgi-bin/httpuploadcert.cgi" + httpmethod="POST" + _postContentType="multipart/form-data; boundary=${multipartformmarker}" + + if [ -z "$httpmethod" ]; then + httpmethod="POST" + fi + _debug $httpmethod + _debug "_post_url" "$_post_url" + _debug2 "bodyfilename" "$bodyfilename" + _debug2 "_postContentType" "$_postContentType" + + _inithttp + + if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then + _CURL="$_ACME_CURL" + if [ "$HTTPS_INSECURE" ]; then + _CURL="$_CURL --insecure " + fi + if [ "$httpmethod" = "HEAD" ]; then + _CURL="$_CURL -I " + fi + _debug "_CURL" "$_CURL" + + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data-binary "@${bodyfilename}" "$_post_url")" + + _ret="$?" + if [ "$_ret" != "0" ]; then + _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret" + if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then + _err "Here is the curl dump log:" + _err "$(cat "$_CURL_DUMP")" + fi + fi + elif [ "$_ACME_WGET" ]; then + _WGET="$_ACME_WGET" + if [ "$HTTPS_INSECURE" ]; then + _WGET="$_WGET --no-check-certificate " + fi + _debug "_WGET" "$_WGET" + + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-file="${bodyfilename}" "$_post_url" 2>"$HTTP_HEADER")" + + _ret="$?" + if [ "$_ret" = "8" ]; then + _ret=0 + _debug "wget returned 8 as the server returned a 'Bad Request' response. Let's process the response later." + fi + if [ "$_ret" != "0" ]; then + _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret" + fi + if _contains "$_WGET" " -d "; then + # Demultiplex wget debug output + cat "$HTTP_HEADER" >&2 + _sed_i '/^[^ ][^ ]/d; /^ *$/d' "$HTTP_HEADER" + fi + # remove leading whitespaces from header to match curl format + _sed_i 's/^ //g' "$HTTP_HEADER" + else + _ret="$?" + _err "Neither curl nor wget have been found, cannot make $httpmethod request." + fi + _debug "_ret" "$_ret" + printf "%s" "$response" + return $_ret +} + +_zyxel_gs1900_trigger_reboot() { + # Trigger a reboot via the management reboot page in the web ui + reboot_page_html=$(_get "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi?cmd=5888" | tr -d '\n') + reboot_xss_value=$(printf "%s" "$reboot_page_html" | _egrep_o 'name="XSSID"\s*value="[^"]+"' | sed 's/^.*="\([^"]\{1,\}\)"$/\1/g') + _secure_debug2 "reboot_xss_value" "$reboot_xss_value" + + reboot_response_html=$(_post "XSSID=${reboot_xss_value}&cmd=5889&sysSubmit=Reboot" "${_zyxel_switch_base_uri}/cgi-bin/dispatcher.cgi" '' "POST" "application/x-www-form-urlencoded") + reboot_message=$(printf "%s" "$reboot_response_html" | tr -d '\t\r\n\v\f' | _egrep_o "Rebooting now...") + + if [ -z "$reboot_message" ]; then + _err "Failed to trigger switch reboot!" + return 1 + fi + + return 0 +} + +# password +_zyxel_gs1900_password_obfuscate() { + # Return the password obfuscated via the same method used by the + # switch's web UI login process + echo "$1" | awk '{ + encoded = ""; + password = $1; + allowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + len = length($1); + pwi = length($1); + + for (i=1; i <= (321 - pwi); i++) + { + if (0 == i % 5 && pwi > 0) + { + encoded = (encoded)(substr(password, pwi--, 1)); + } + else if (i == 123) + { + if (len < 10) + { + encoded = (encoded)(0); + } + else + { + encoded = (encoded)(int(len / 10)); + } + } + else if (i == 289) + { + encoded = (encoded)(len % 10) + } + else + { + encoded = (encoded)(substr(allowed, int(rand() * length(allowed)), 1)) + } + } + printf("%s", encoded); + }' +} + +# html label +_zyxel_html_table_lookup() { + # Look up a value in the html representing the status page of the switch + # when provided with the html of the page and the label (i.e. "Model Name:") + html="$1" + label=$(printf "%s" "$2" | tr -d ' ') + lookup_result=$(printf "%s" "$html" | tr -d "\t\r\n\v\f" | sed 's//\n/g' | sed 's/]*>//g' | tr -d ' ' | grep -i "$label" | sed "s/$label<\/td>\([^<]\{1,\}\)<\/td><\/tr>/\1/i") + printf "%s" "$lookup_result" + return 0 +} + +# html +_zyxel_gs1900_get_model() { + html="$1" + model_name=$(_zyxel_html_table_lookup "$html" "Model Name:") + printf "%s" "$model_name" +} + +# html +_zyxel_gs1900_get_firmware_version() { + html="$1" + firmware_version=$(_zyxel_html_table_lookup "$html" "Firmware Version:" | _egrep_o "V[^.]+.[^(]+") + printf "%s" "$firmware_version" +} + +# version_number +_zyxel_gs1900_parse_major_version() { + printf "%s" "$1" | sed 's/^V\([0-9]\{1,\}\).\{1,\}$/\1/gi' +} + +# version_number +_zyxel_gs1900_parse_minor_version() { + printf "%s" "$1" | sed 's/^.\{1,\}\.\([0-9]\{1,\}\)$/\1/gi' +} From e1d447847f0e3da3e213ae1b1a6c154bc72a40f0 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Fri, 25 Apr 2025 05:21:52 +0800 Subject: [PATCH 06/21] Spaceship: fix domain conf --- dnsapi/dns_spaceship.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index 501131b8..7e9fb167 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -133,11 +133,11 @@ _get_root() { domain="$1" # Check manual override - SPACESHIP_ROOT_DOMAIN="${SPACESHIP_ROOT_DOMAIN:-$(_readaccountconf_mutable SPACESHIP_ROOT_DOMAIN)}" + SPACESHIP_ROOT_DOMAIN="${SPACESHIP_ROOT_DOMAIN:-$(_readdomainconf SPACESHIP_ROOT_DOMAIN)}" if [ -n "$SPACESHIP_ROOT_DOMAIN" ]; then _domain="$SPACESHIP_ROOT_DOMAIN" _debug "Using manually specified or saved root domain: $_domain" - _saveaccountconf_mutable SPACESHIP_ROOT_DOMAIN "$SPACESHIP_ROOT_DOMAIN" + _savedomainconf SPACESHIP_ROOT_DOMAIN "$SPACESHIP_ROOT_DOMAIN" return 0 fi @@ -162,7 +162,7 @@ _get_root() { _debug "Root zone found: '$_domain'" # Save the detected root domain - _saveaccountconf_mutable SPACESHIP_ROOT_DOMAIN "$_domain" + _savedomainconf SPACESHIP_ROOT_DOMAIN "$_domain" _info "Root domain '$_domain' saved to configuration for future use." return 0 From d01aefd1eb17c53604604645798d77a9ace399d9 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Fri, 25 Apr 2025 05:24:05 +0800 Subject: [PATCH 07/21] Spaceship: i starts from 1 --- dnsapi/dns_spaceship.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index 7e9fb167..cc3f066f 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -143,7 +143,7 @@ _get_root() { _debug "Detecting root zone for '$domain'" - i=2 + i=1 p=1 while true; do _cutdomain=$(printf "%s" "$domain" | cut -d . -f "$i"-100) From b5e3883891e31b5082f5f7520c6ac68fad441eeb Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 26 Apr 2025 16:47:20 +0200 Subject: [PATCH 08/21] update --- .github/workflows/pr_dns.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_dns.yml b/.github/workflows/pr_dns.yml index 58630e8b..25096c7e 100644 --- a/.github/workflows/pr_dns.yml +++ b/.github/workflows/pr_dns.yml @@ -20,12 +20,26 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: `**Welcome** + READ ME !!!!! + + + Read me !!!!!! + + First thing: don't send PR to the master branch, please send to the dev branch instead. - Please make sure you've read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test). + + + Please read the [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test). + + Then reply on this message, otherwise, your code will not be reviewed or merged. + + Please also make sure to add/update the usage here: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2 - We look forward to reviewing your Pull request shortly ✨ + + 注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试. + ` }) From 2928d843393e839b538307ff8e04a01ca7ae738a Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Mon, 28 Apr 2025 00:04:49 +0800 Subject: [PATCH 09/21] Spaceship: replace debug with debug2 for detailed output in complex debugging --- dnsapi/dns_spaceship.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index cc3f066f..c6db9928 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -183,7 +183,7 @@ _spaceship_api_request() { url="$2" payload="$3" - _debug "Sending $method request to $url with payload $payload" + _debug2 "Sending $method request to $url with payload $payload" if [ "$method" = "GET" ]; then response="$(_get "$url")" else @@ -195,7 +195,7 @@ _spaceship_api_request() { return 1 fi - _debug "API response body: $response" + _debug2 "API response body: $response" if [ "$method" = "GET" ]; then if _contains "$(_head_n 1 <"$HTTP_HEADER")" '200'; then @@ -207,6 +207,6 @@ _spaceship_api_request() { fi fi - _debug "API response header: $HTTP_HEADER" + _debug2 "API response header: $HTTP_HEADER" return 1 } From e2d09231225a2d9cbb33d64d5a49f08a6284c060 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Mon, 28 Apr 2025 00:18:23 +0800 Subject: [PATCH 10/21] Spaceship: replace ~/.acme.sh with $LE_CONFIG_HOME for configurable paths --- dnsapi/dns_spaceship.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index c6db9928..264bdefc 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -114,7 +114,7 @@ _spaceship_init() { if [ -z "$SPACESHIP_API_KEY" ] || [ -z "$SPACESHIP_API_SECRET" ]; then _err "Spaceship API credentials are not set. Please set SPACESHIP_API_KEY and SPACESHIP_API_SECRET." - _err "Ensure ~/.acme.sh directory has restricted permissions (chmod 700 ~/.acme.sh) to protect credentials." + _err "Ensure \"$LE_CONFIG_HOME\" directory has restricted permissions (chmod 700 \"$LE_CONFIG_HOME\") to protect credentials." return 1 fi From 8b4d93cc14e3c1cf246840c5cd95409c10fd6836 Mon Sep 17 00:00:00 2001 From: Meo597 <197331664+Meo597@users.noreply.github.com> Date: Mon, 28 Apr 2025 00:32:46 +0800 Subject: [PATCH 11/21] Spaceship: fix doc --- dnsapi/dns_spaceship.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_spaceship.sh b/dnsapi/dns_spaceship.sh index 264bdefc..770e22cc 100644 --- a/dnsapi/dns_spaceship.sh +++ b/dnsapi/dns_spaceship.sh @@ -2,7 +2,7 @@ # shellcheck disable=SC2034 dns_spaceship_info='Spaceship.com Site: Spaceship.com -Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_spaceship +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_spaceship Options: SPACESHIP_API_KEY Spaceship API Key SPACESHIP_API_SECRET Spaceship API Secret From eb00852a714b3dfa0c556e4fb3806ade46392a11 Mon Sep 17 00:00:00 2001 From: neil Date: Thu, 1 May 2025 13:28:20 +0200 Subject: [PATCH 12/21] remove ocsp for letsencrypt server --- acme.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/acme.sh b/acme.sh index 4d75ab62..e9eb6b94 100755 --- a/acme.sh +++ b/acme.sh @@ -5504,6 +5504,13 @@ renew() { if [ -z "$Le_Keylength" ]; then Le_Keylength=2048 fi + if [ "$CA_LETSENCRYPT_V2" = "$Le_API" ]; then + #letsencrypt doesn't support ocsp anymore + if [ "$Le_OCSP_Staple" ]; then + export Le_OCSP_Staple="" + _cleardomainconf Le_OCSP_Staple + fi + fi issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" "$Le_ChallengeAlias" "$Le_Preferred_Chain" "$Le_Valid_From" "$Le_Valid_To" res="$?" if [ "$res" != "0" ]; then From 42aaf7c2a020c3aeb84031d31ff9006de6aef670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Vyb=C3=ADhal?= Date: Sun, 12 Jan 2025 16:41:00 +0100 Subject: [PATCH 13/21] dns_active24: rewrite for supporting new v2 API --- dnsapi/dns_active24.sh | 172 +++++++++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 57 deletions(-) diff --git a/dnsapi/dns_active24.sh b/dnsapi/dns_active24.sh index c56dd363..0f24c53a 100755 --- a/dnsapi/dns_active24.sh +++ b/dnsapi/dns_active24.sh @@ -1,17 +1,17 @@ #!/usr/bin/env sh # shellcheck disable=SC2034 -dns_active24_info='Active24.com -Site: Active24.com +dns_active24_info='Active24.cz +Site: Active24.cz Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_active24 Options: - ACTIVE24_Token API Token + Active24_ApiKey API Key. Called "Identifier" in the Active24 Admin + Active24_ApiSecret API Secret. Called "Secret key" in the Active24 Admin Issues: github.com/acmesh-official/acme.sh/issues/2059 -Author: Milan Pála ' -ACTIVE24_Api="https://api.active24.com" - -######## Public functions ##################### +Active24_Api="https://rest.active24.cz" +# export Active24_ApiKey=ak48l3h7-ak5d-qn4t-p8gc-b6fs8c3l +# export Active24_ApiSecret=ajvkeo3y82ndsu2smvxy3o36496dcascksldncsq # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" # Used to add txt record @@ -22,8 +22,8 @@ dns_active24_add() { _active24_init _info "Adding txt record" - if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then - if _contains "$response" "errors"; then + if _active24_rest POST "/v2/service/$_service_id/dns/record" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":300}"; then + if _contains "$response" "error"; then _err "Add txt record error." return 1 else @@ -31,6 +31,7 @@ dns_active24_add() { return 0 fi fi + _err "Add txt record error." return 1 } @@ -44,19 +45,25 @@ dns_active24_rm() { _active24_init _debug "Getting txt records" - _active24_rest GET "dns/$_domain/records/v1" + # The API needs to send data in body in order the filter to work + # TODO: web can also add content $txtvalue to filter and then get the id from response + _active24_rest GET "/v2/service/$_service_id/dns/record" "{\"page\":1,\"descending\":true,\"sortBy\":\"name\",\"rowsPerPage\":100,\"totalRecords\":0,\"filters\":{\"type\":[\"TXT\"],\"name\":\"${_sub_domain}\"}}" + #_active24_rest GET "/v2/service/$_service_id/dns/record?rowsPerPage=100" - if _contains "$response" "errors"; then + if _contains "$response" "error"; then _err "Error" return 1 fi - hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-) + # Note: it might never be more than one record actually, NEEDS more INVESTIGATION + record_ids=$(printf "%s" "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2) + _debug2 record_ids "$record_ids" - for hash_id in $hash_ids; do - _debug "Removing hash_id" "$hash_id" - if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then - if _contains "$response" "errors"; then + for redord_id in $record_ids; do + _debug "Removing record_id" "$redord_id" + _debug "txtvalue" "$txtvalue" + if _active24_rest DELETE "/v2/service/$_service_id/dns/record/$redord_id" ""; then + if _contains "$response" "error"; then _err "Unable to remove txt record." return 1 else @@ -70,21 +77,15 @@ dns_active24_rm() { return 1 } -#################### Private functions below ################################## -#_acme-challenge.www.domain.com -#returns -# _sub_domain=_acme-challenge.www -# _domain=domain.com -# _domain_id=sdjkglgdfewsdfg _get_root() { domain=$1 + i=1 + p=1 - if ! _active24_rest GET "dns/domains/v1"; then + if ! _active24_rest GET "/v1/user/self/service"; then return 1 fi - i=1 - p=1 while true; do h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) _debug "h" "$h" @@ -104,21 +105,98 @@ _get_root() { return 1 } -_active24_rest() { - m=$1 - ep="$2" - data="$3" - _debug "$ep" +_active24_init() { + Active24_ApiKey="${Active24_ApiKey:-$(_readaccountconf_mutable Active24_ApiKey)}" + Active24_ApiSecret="${Active24_ApiSecret:-$(_readaccountconf_mutable Active24_ApiSecret)}" + #Active24_ServiceId="${Active24_ServiceId:-$(_readaccountconf_mutable Active24_ServiceId)}" - export _H1="Authorization: Bearer $ACTIVE24_Token" - - if [ "$m" != "GET" ]; then - _debug "data" "$data" - response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")" - else - response="$(_get "$ACTIVE24_Api/$ep")" + if [ -z "$Active24_ApiKey" ] || [ -z "$Active24_ApiSecret" ]; then + Active24_ApiKey="" + Active24_ApiSecret="" + _err "You don't specify Active24 api key and ApiSecret yet." + _err "Please create your key and try again." + return 1 fi + #save the credentials to the account conf file. + _saveaccountconf_mutable Active24_ApiKey "$Active24_ApiKey" + _saveaccountconf_mutable Active24_ApiSecret "$Active24_ApiSecret" + + _debug "A24 API CHECK" + if ! _active24_rest GET "/v2/check"; then + _err "A24 API check failed with: $response" + return 1 + fi + + if ! echo "$response" | tr -d " " | grep \"verified\":true >/dev/null; then + _err "A24 API check failed with: $response" + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + _active24_get_service_id "$_domain" + _debug _service_id "$_service_id" +} + +_active24_get_service_id() { + _d=$1 + if ! _active24_rest GET "/v1/user/self/zone/${_d}"; then + return 1 + else + response=$(echo "$response" | _json_decode) + _service_id=$(echo "$response" | _egrep_o '"id" *: *[^,]+' | cut -d ':' -f 2) + fi +} + +_active24_rest() { + m=$1 + ep_qs=$2 # with query string + # ep=$2 + ep=$(printf "%s" "$ep_qs" | cut -d '?' -f1) # no query string + data="$3" + + _debug "A24 $ep" + _debug "A24 $Active24_ApiKey" + _debug "A24 $Active24_ApiSecret" + + timestamp=$(_time) + datez=$(date -u +"%Y%m%dT%H%M%SZ") + canonicalRequest="${m} ${ep} ${timestamp}" + signature=$(printf "%s" "$canonicalRequest" | _hmac sha1 "$(printf "%s" "$Active24_ApiSecret" | _hex_dump | tr -d " ")" hex) + authorization64="$(printf "%s:%s" "$Active24_ApiKey" "$signature" | _base64)" + + export _H1="Date: ${datez}" + export _H2="Accept: application/json" + export _H3="Content-Type: application/json" + export _H4="Authorization: Basic ${authorization64}" + + _debug2 H1 "$_H1" + _debug2 H2 "$_H2" + _debug2 H3 "$_H3" + _debug2 H4 "$_H4" + + # _sleep 1 + + if [ "$m" != "GET" ]; then + _debug2 "${m} $Active24_Api${ep_qs}" + _debug "data" "$data" + response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")" + else + if [ -z "$data" ]; then + _debug2 "GET $Active24_Api${ep_qs}" + response="$(_get "$Active24_Api${ep_qs}")" + else + _debug2 "GET $Active24_Api${ep_qs} with data: ${data}" + response="$(_post "$data" "$Active24_Api${ep_qs}" "" "$m" "application/json")" + fi + fi if [ "$?" != "0" ]; then _err "error $ep" return 1 @@ -126,23 +204,3 @@ _active24_rest() { _debug2 response "$response" return 0 } - -_active24_init() { - ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}" - if [ -z "$ACTIVE24_Token" ]; then - ACTIVE24_Token="" - _err "You didn't specify a Active24 api token yet." - _err "Please create the token and try again." - return 1 - fi - - _saveaccountconf_mutable ACTIVE24_Token "$ACTIVE24_Token" - - _debug "First detect the root zone" - if ! _get_root "$fulldomain"; then - _err "invalid domain" - return 1 - fi - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" -} From 184cb0b9a8ae73940514a7759173d678e8a7cbe8 Mon Sep 17 00:00:00 2001 From: Adrian Fedoreanu Date: Thu, 15 May 2025 16:01:24 +0200 Subject: [PATCH 14/21] dns_1984.hosting.sh: fix session cookie name --- dnsapi/dns_1984hosting.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_1984hosting.sh b/dnsapi/dns_1984hosting.sh index 906ea443..8d9676ac 100755 --- a/dnsapi/dns_1984hosting.sh +++ b/dnsapi/dns_1984hosting.sh @@ -128,7 +128,7 @@ _1984hosting_login() { _get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken" csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')" - sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')" + sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')" if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then _err "One or more cookies are empty: '$csrftoken', '$sessionid'." @@ -145,7 +145,7 @@ _1984hosting_login() { _debug2 response "$response" if _contains "$response" '"loggedin": true'; then - One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')" + One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')" One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')" export One984HOSTING_SESSIONID_COOKIE export One984HOSTING_CSRFTOKEN_COOKIE From b82f6801cd24f647510355f1818da2b1368c56a9 Mon Sep 17 00:00:00 2001 From: ymol-spraaklab Date: Fri, 16 May 2025 15:40:36 +0200 Subject: [PATCH 15/21] Set DNS Record TTL to 60 instead of 300 sec --- dnsapi/dns_transip.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_transip.sh b/dnsapi/dns_transip.sh index 2abbe34d..b3c5ed70 100644 --- a/dnsapi/dns_transip.sh +++ b/dnsapi/dns_transip.sh @@ -24,7 +24,7 @@ dns_transip_add() { _debug txtvalue="$txtvalue" _transip_setup "$fulldomain" || return 1 _info "Creating TXT record." - if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then + if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":60}}"; then _err "Could not add TXT record." return 1 fi @@ -38,7 +38,7 @@ dns_transip_rm() { _debug txtvalue="$txtvalue" _transip_setup "$fulldomain" || return 1 _info "Removing TXT record." - if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then + if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":60}}"; then _err "Could not remove TXT record $_sub_domain for $domain" return 1 fi From 99a4cf9e07f20c906fbd4f021b4bb6bd76f5c5dd Mon Sep 17 00:00:00 2001 From: Attackwave <51136146+Attackwave@users.noreply.github.com> Date: Fri, 16 May 2025 22:44:25 +0200 Subject: [PATCH 16/21] Quickfix TrueNAS 25.04 --- deploy/truenas_ws.sh | 125 +++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 47 deletions(-) diff --git a/deploy/truenas_ws.sh b/deploy/truenas_ws.sh index 940cde2e..bdc1b846 100644 --- a/deploy/truenas_ws.sh +++ b/deploy/truenas_ws.sh @@ -52,6 +52,39 @@ _ws_call() { return 0 } +# Upload certificate with webclient api +_ws_upload_cert() { + + /usr/bin/env python - < Date: Sat, 17 May 2025 21:28:26 +0200 Subject: [PATCH 17/21] fix pr --- .github/workflows/pr_dns.yml | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/.github/workflows/pr_dns.yml b/.github/workflows/pr_dns.yml index 25096c7e..50eb2adb 100644 --- a/.github/workflows/pr_dns.yml +++ b/.github/workflows/pr_dns.yml @@ -20,26 +20,14 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: `**Welcome** - READ ME !!!!! - - - Read me !!!!!! - - - First thing: don't send PR to the master branch, please send to the dev branch instead. - - - Please read the [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test). - - - Then reply on this message, otherwise, your code will not be reviewed or merged. - - - Please also make sure to add/update the usage here: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2 - - - 注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试. - + READ ME !!!!! + Read me !!!!!! + First thing: don't send PR to the master branch, please send to the dev branch instead. + Please read the [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide). + You MUST pass the [DNS-API-Test](../wiki/DNS-API-Test). + Then reply on this message, otherwise, your code will not be reviewed or merged. + Please also make sure to add/update the usage here: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2 + 注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试. ` }) From 4f5a70b80404288f8a5b591d541d964b591b5db9 Mon Sep 17 00:00:00 2001 From: Joe Bauser Date: Sat, 17 May 2025 21:25:39 -0400 Subject: [PATCH 18/21] Apply suggested fixes from shfmt diffs --- deploy/zyxel_gs1900.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/zyxel_gs1900.sh b/deploy/zyxel_gs1900.sh index 443a5b05..37cf6005 100644 --- a/deploy/zyxel_gs1900.sh +++ b/deploy/zyxel_gs1900.sh @@ -245,7 +245,7 @@ _zyxel_gs1900_should_update() { _debug2 "_remote_cert_serial" "$_remote_cert_serial" # Get our certificate serial number - _our_cert_serial=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial < "${_ccert}") + _our_cert_serial=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -serial <"${_ccert}") _debug2 "_our_cert_serial" "$_our_cert_serial" [ "${_remote_cert_serial}" != "${_our_cert_serial}" ] From f132010acb927065cef8294b4a772afe6301490f Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Mon, 19 May 2025 15:08:54 +0300 Subject: [PATCH 19/21] dns_edgecenter.sh: fix structural info --- dnsapi/dns_edgecenter.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_edgecenter.sh b/dnsapi/dns_edgecenter.sh index cdd150df..8f4ad171 100644 --- a/dnsapi/dns_edgecenter.sh +++ b/dnsapi/dns_edgecenter.sh @@ -1,13 +1,13 @@ #!/usr/bin/env sh # shellcheck disable=SC2034 - -# EdgeCenter DNS API integration for acme.sh -# Author: Konstantin Ruchev -dns_edgecenter_info='edgecenter DNS API -Site: https://edgecenter.ru -Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_edgecenter +dns_edgecenter_info='EdgeCenter.ru +Site: EdgeCenter.ru +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgecenter Options: - EDGECENTER_API_KEY auth APIKey' + EDGECENTER_API_KEY API Key +Issues: github.com/acmesh-official/acme.sh/issues/6313 +Author: Konstantin Ruchev +' EDGECENTER_API="https://api.edgecenter.ru" DOMAIN_TYPE= From 133ae8555a7114bf314d5a81be27f5e7a808e807 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Mon, 19 May 2025 15:15:46 +0300 Subject: [PATCH 20/21] dns_freemyip.sh: fix strutural info --- dnsapi/dns_freemyip.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_freemyip.sh b/dnsapi/dns_freemyip.sh index 0bad3809..d598a657 100644 --- a/dnsapi/dns_freemyip.sh +++ b/dnsapi/dns_freemyip.sh @@ -1,11 +1,11 @@ #!/usr/bin/env sh # shellcheck disable=SC2034 dns_freemyip_info='FreeMyIP.com -Site: freemyip.com +Site: FreeMyIP.com Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_freemyip Options: FREEMYIP_Token API Token -Issues: github.com/acmesh-official/acme.sh/issues/{XXXX} +Issues: github.com/acmesh-official/acme.sh/issues/6247 Author: Recolic Keghart , @Giova96 ' From 8241b078ced81178796ab76fad5b80baed44040f Mon Sep 17 00:00:00 2001 From: YANGJINZE <91786638+KincaidYang@users.noreply.github.com> Date: Fri, 23 May 2025 17:54:56 +0800 Subject: [PATCH 21/21] docs (dns_tencent) : update documentation links --- dnsapi/dns_tencent.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh index d82768b9..b148adc3 100644 --- a/dnsapi/dns_tencent.sh +++ b/dnsapi/dns_tencent.sh @@ -2,7 +2,7 @@ # shellcheck disable=SC2034 dns_tencent_info='Tencent.com Site: cloud.Tencent.com -Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_tencent +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_tencent Options: Tencent_SecretId Secret ID Tencent_SecretKey Secret Key