From 5d5c51d1609b0b3a7b3cea65ada0e4ef83d85d8f Mon Sep 17 00:00:00 2001 From: Tomas Pavlic Date: Mon, 9 Mar 2026 14:37:41 +0100 Subject: [PATCH 1/5] Add DNS hook for subreg.cz --- dnsapi/dns_subreg.sh | 219 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 dnsapi/dns_subreg.sh diff --git a/dnsapi/dns_subreg.sh b/dnsapi/dns_subreg.sh new file mode 100644 index 00000000..944a51ff --- /dev/null +++ b/dnsapi/dns_subreg.sh @@ -0,0 +1,219 @@ +#!/usr/bin/env sh +# shellcheck disable=SC2034 +dns_subreg_info='Subreg.cz +Site: subreg.cz +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_subreg +Options: + SUBREG_API_USERNAME API username + SUBREG_API_PASSWORD API password +Author: Tomas Pavlic <@tomaspavli> +' + +# Subreg SOAP API +# https://subreg.cz/manual/ + +SUBREG_API_URL="https://soap.subreg.cz/cmd.php" + +######## Public functions ##################### + +# Usage: dns_subreg_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_subreg_add() { + fulldomain=$1 + txtvalue=$2 + + SUBREG_API_USERNAME="${SUBREG_API_USERNAME:-$(_readaccountconf_mutable SUBREG_API_USERNAME)}" + SUBREG_API_PASSWORD="${SUBREG_API_PASSWORD:-$(_readaccountconf_mutable SUBREG_API_PASSWORD)}" + if [ -z "$SUBREG_API_USERNAME" ] || [ -z "$SUBREG_API_PASSWORD" ]; then + _err "SUBREG_API_USERNAME and SUBREG_API_PASSWORD are not set." + return 1 + fi + + _saveaccountconf_mutable SUBREG_API_USERNAME "$SUBREG_API_USERNAME" + _saveaccountconf_mutable SUBREG_API_PASSWORD "$SUBREG_API_PASSWORD" + + if ! _subreg_login; then + return 1 + fi + + if ! _get_root "$fulldomain"; then + _err "Cannot determine root domain for: $fulldomain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _subreg_soap "Add_DNS_Record" "$_domain$_sub_domainTXT$txtvalue0120" + if _subreg_ok; then + _record_id="$(_subreg_map_get record_id)" + _savedomainconf "SUBREG_RECORD_ID" "$_record_id" + return 0 + fi + _err "Failed to add TXT record." + _err "$response" + return 1 +} + +# Usage: dns_subreg_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_subreg_rm() { + fulldomain=$1 + txtvalue=$2 + + SUBREG_API_USERNAME="${SUBREG_API_USERNAME:-$(_readaccountconf_mutable SUBREG_API_USERNAME)}" + SUBREG_API_PASSWORD="${SUBREG_API_PASSWORD:-$(_readaccountconf_mutable SUBREG_API_PASSWORD)}" + if [ -z "$SUBREG_API_USERNAME" ] || [ -z "$SUBREG_API_PASSWORD" ]; then + _err "SUBREG_API_USERNAME and SUBREG_API_PASSWORD are not set." + return 1 + fi + + if ! _subreg_login; then + return 1 + fi + + if ! _get_root "$fulldomain"; then + _err "Cannot determine root domain for: $fulldomain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _record_id="$(_readdomainconf "SUBREG_RECORD_ID")" + if [ -z "$_record_id" ]; then + _err "Could not find saved record ID for $fulldomain" + return 1 + fi + _cleardomainconf "SUBREG_RECORD_ID" + + _debug "Deleting record ID: $_record_id" + _subreg_soap "Delete_DNS_Record" "$_domain$_record_id" + if _subreg_ok; then + return 0 + fi + _err "Failed to delete TXT record." + _err "$response" + return 1 +} + +#################### Private functions ##################### + +# Check if the current $response contains a successful status in the ns2:Map format: +# statusok +_subreg_ok() { + [ "$(_subreg_map_get status)" = "ok" ] +} + +# Extract the value for a given key from the ns2:Map response. +# Usage: _subreg_map_get keyname +# Reads from $response +_subreg_map_get() { + _key="$1" + echo "$response" | tr -d '\n\r' | \ + _egrep_o ">${_key}]*>[^<]*" | \ + sed 's/.*]*>//;s/<\/value>//' +} + +# Login and store session token in _subreg_ssid +_subreg_login() { + _debug "Logging in to Subreg API as $SUBREG_API_USERNAME" + _subreg_soap_noauth "Login" "$SUBREG_API_USERNAME$SUBREG_API_PASSWORD" + if ! _subreg_ok; then + _err "Subreg login failed." + _err "$response" + return 1 + fi + _subreg_ssid="$(_subreg_map_get ssid)" + if [ -z "$_subreg_ssid" ]; then + _err "Subreg login: could not extract session token (ssid)." + return 1 + fi + _debug "_subreg_ssid" "$_subreg_ssid" + return 0 +} + +# _get_root _acme-challenge.www.domain.com +# returns _sub_domain and _domain +_get_root() { + domain=$1 + i=1 + p=1 + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) + if [ -z "$h" ]; then + return 1 + fi + + _subreg_soap "Get_DNS_Zone" "$h" + + if _subreg_ok; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") + _domain="$h" + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done + _err "Unable to retrieve DNS zone matching domain: $domain" + return 1 +} + +# Send a SOAP request without authentication (used for Login) +# _subreg_soap_noauth command inner_xml +_subreg_soap_noauth() { + _cmd="$1" + _inner="$2" + + _soap_body=" + + + + + ${_inner} + + + +" + + export _H1="Content-Type: text/xml" + export _H2="SOAPAction: http://soap.subreg.cz/soap#${_cmd}" + response="$(_post "$_soap_body" "$SUBREG_API_URL" "" "POST" "text/xml")" + _debug2 "response" "$response" +} + +# Send an authenticated SOAP request (requires _subreg_ssid to be set) +# _subreg_soap command inner_xml +_subreg_soap() { + _cmd="$1" + _inner="$2" + + _soap_body=" + + + + + ${_subreg_ssid} + ${_inner} + + + +" + + export _H1="Content-Type: text/xml" + export _H2="SOAPAction: http://soap.subreg.cz/soap#${_cmd}" + response="$(_post "$_soap_body" "$SUBREG_API_URL" "" "POST" "text/xml")" + _debug2 "response" "$response" +} From 55f52485b5eda94fc827261cef75f4bf9c547b7e Mon Sep 17 00:00:00 2001 From: Tomas Pavlic Date: Mon, 9 Mar 2026 14:52:22 +0100 Subject: [PATCH 2/5] Fix formatting --- dnsapi/dns_subreg.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dnsapi/dns_subreg.sh b/dnsapi/dns_subreg.sh index 944a51ff..22d59786 100644 --- a/dnsapi/dns_subreg.sh +++ b/dnsapi/dns_subreg.sh @@ -108,9 +108,7 @@ _subreg_ok() { # Reads from $response _subreg_map_get() { _key="$1" - echo "$response" | tr -d '\n\r' | \ - _egrep_o ">${_key}]*>[^<]*" | \ - sed 's/.*]*>//;s/<\/value>//' + echo "$response" | tr -d '\n\r' | _egrep_o ">${_key}]*>[^<]*" | sed 's/.*]*>//;s/<\/value>//' } # Login and store session token in _subreg_ssid From 7b39f7b42b8286aff2cb0dcf0020baecb61f56b8 Mon Sep 17 00:00:00 2001 From: Tomas Pavlic Date: Mon, 9 Mar 2026 15:35:51 +0100 Subject: [PATCH 3/5] Use unique config key names --- dnsapi/dns_subreg.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_subreg.sh b/dnsapi/dns_subreg.sh index 22d59786..5e10b3d8 100644 --- a/dnsapi/dns_subreg.sh +++ b/dnsapi/dns_subreg.sh @@ -46,7 +46,7 @@ dns_subreg_add() { _subreg_soap "Add_DNS_Record" "$_domain$_sub_domainTXT$txtvalue0120" if _subreg_ok; then _record_id="$(_subreg_map_get record_id)" - _savedomainconf "SUBREG_RECORD_ID" "$_record_id" + _savedomainconf "$(_subreg_record_id_key "$txtvalue")" "$_record_id" return 0 fi _err "Failed to add TXT record." @@ -78,12 +78,12 @@ dns_subreg_rm() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" - _record_id="$(_readdomainconf "SUBREG_RECORD_ID")" + _record_id="$(_readdomainconf "$(_subreg_record_id_key "$txtvalue")")" if [ -z "$_record_id" ]; then _err "Could not find saved record ID for $fulldomain" return 1 fi - _cleardomainconf "SUBREG_RECORD_ID" + _cleardomainconf "$(_subreg_record_id_key "$txtvalue")" _debug "Deleting record ID: $_record_id" _subreg_soap "Delete_DNS_Record" "$_domain$_record_id" @@ -97,6 +97,12 @@ dns_subreg_rm() { #################### Private functions ##################### +# Build a domain-conf key for storing the record ID of a given TXT value. +# Base64url chars include '-' which is invalid in shell variable names, so replace with '_'. +_subreg_record_id_key() { + printf 'SUBREG_RECORD_ID_%s' "$(printf '%s' "$1" | tr -- '-' '_')" +} + # Check if the current $response contains a successful status in the ns2:Map format: # statusok _subreg_ok() { From 6670172ca97cf03c058425fed9525c5b78f5f74a Mon Sep 17 00:00:00 2001 From: Tomas Pavlic Date: Mon, 9 Mar 2026 20:48:56 +0100 Subject: [PATCH 4/5] Add link to Github issue --- dnsapi/dns_subreg.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_subreg.sh b/dnsapi/dns_subreg.sh index 5e10b3d8..fa1b1ee5 100644 --- a/dnsapi/dns_subreg.sh +++ b/dnsapi/dns_subreg.sh @@ -6,6 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_subreg Options: SUBREG_API_USERNAME API username SUBREG_API_PASSWORD API password +Issues: github.com/acmesh-official/acme.sh/issues/6835 Author: Tomas Pavlic <@tomaspavli> ' From 69b67ef05d003257cf62f96c1e37f77a4df36874 Mon Sep 17 00:00:00 2001 From: Tomas Pavlic Date: Mon, 9 Mar 2026 21:39:13 +0100 Subject: [PATCH 5/5] Add author github link --- dnsapi/dns_subreg.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_subreg.sh b/dnsapi/dns_subreg.sh index fa1b1ee5..5e1ada3c 100644 --- a/dnsapi/dns_subreg.sh +++ b/dnsapi/dns_subreg.sh @@ -7,7 +7,7 @@ Options: SUBREG_API_USERNAME API username SUBREG_API_PASSWORD API password Issues: github.com/acmesh-official/acme.sh/issues/6835 -Author: Tomas Pavlic <@tomaspavli> +Author: Tomas Pavlic ' # Subreg SOAP API