From 75ee17aeeb9560cf45b0193efa48f4f46bcdbaab Mon Sep 17 00:00:00 2001
From: asavin
Date: Tue, 25 Nov 2025 14:47:26 +0100
Subject: [PATCH 096/125] Remove unecessary base64 encoding
---
dnsapi/dns_efficientip.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dnsapi/dns_efficientip.sh b/dnsapi/dns_efficientip.sh
index f12a2a85..a485849a 100755
--- a/dnsapi/dns_efficientip.sh
+++ b/dnsapi/dns_efficientip.sh
@@ -121,7 +121,7 @@ dns_efficientip_rm() {
else
TS=$(date +%s)
Sig=$(printf "%b\n$TS\nDELETE\n${baseurlnObject}" "${EfficientIP_Token_Secret}" | _digest sha3-256 hex)
- EfficientIP_CredsEncoded=$(printf "%b:%b" "${EfficientIP_Token_Key}" "$Sig" | _base64)
+ EfficientIP_CredsEncoded=$(printf "%b:%b" "${EfficientIP_Token_Key}" "$Sig")
export _H2="Authorization: SDS ${EfficientIP_CredsEncoded}"
export _H3="X-SDS-TS: $TS"
fi
From 705fbcd570dfec12b9851cdd9b047020c60e5185 Mon Sep 17 00:00:00 2001
From: neil
Date: Thu, 27 Nov 2025 22:13:18 +0100
Subject: [PATCH 097/125] fix
https://github.com/acmesh-official/acme.sh/issues/6124#issuecomment-3586650156
---
Dockerfile | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index d8f8b265..88edc4a2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,6 +15,8 @@ RUN apk --no-cache add -f \
jq \
cronie
+ENV LE_WORKING_DIR=/acmebin
+
ENV LE_CONFIG_HOME=/acme.sh
ARG AUTO_UPGRADE=1
@@ -30,7 +32,7 @@ COPY ./notify /install_acme.sh/notify
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
-RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
+RUN ln -s $LE_WORKING_DIR/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
RUN for verb in help \
version \
@@ -64,7 +66,7 @@ RUN for verb in help \
set-default-ca \
set-default-chain \
; do \
- printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
+ printf -- "%b" "#!/usr/bin/env sh\n$LE_WORKING_DIR/acme.sh --${verb} --config-home $LE_CONFIG_HOME \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
; done
RUN printf "%b" '#!'"/usr/bin/env sh\n \
@@ -72,7 +74,7 @@ if [ \"\$1\" = \"daemon\" ]; then \n \
exec crond -n -s -m off \n \
else \n \
exec -- \"\$@\"\n \
-fi\n" >/entry.sh && chmod +x /entry.sh
+fi\n" >/entry.sh && chmod +x /entry.sh && chmod -R o+rwx $LE_WORKING_DIR && chmod -R o+rwx $LE_CONFIG_HOME
VOLUME /acme.sh
From c5566eafebaa04edd30074058e2eefba5b5bfc1e Mon Sep 17 00:00:00 2001
From: SunMar
Date: Fri, 28 Nov 2025 09:44:50 +0100
Subject: [PATCH 098/125] fix "dns_aws.sh: line 164: _error: command not found"
#6443
---
dnsapi/dns_aws.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh
index c88c9d9c..b76d69c2 100755
--- a/dnsapi/dns_aws.sh
+++ b/dnsapi/dns_aws.sh
@@ -161,7 +161,7 @@ _get_root() {
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100 | sed 's/\./\\./g')
_debug "Checking domain: $h"
if [ -z "$h" ]; then
- _error "invalid domain"
+ _err "invalid domain"
return 1
fi
From ac0df6bc885db5f67e2ccebecfacbedd011974f4 Mon Sep 17 00:00:00 2001
From: neil
Date: Sat, 29 Nov 2025 16:36:14 +0100
Subject: [PATCH 099/125] start 3.1.3
---
acme.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/acme.sh b/acme.sh
index 6578d414..da67fa14 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env sh
-VER=3.1.2
+VER=3.1.3
PROJECT_NAME="acme.sh"
From 5c6d8aacbeeb4063822064c27bdaf4a144975cb7 Mon Sep 17 00:00:00 2001
From: Stefan
Date: Sat, 29 Nov 2025 22:38:02 +0100
Subject: [PATCH 100/125] Add files via upload
---
dnsapi/dns_infoblox_uddi.sh | 220 ++++++++++++++++++++++++++++++++++++
1 file changed, 220 insertions(+)
create mode 100644 dnsapi/dns_infoblox_uddi.sh
diff --git a/dnsapi/dns_infoblox_uddi.sh b/dnsapi/dns_infoblox_uddi.sh
new file mode 100644
index 00000000..545ce41d
--- /dev/null
+++ b/dnsapi/dns_infoblox_uddi.sh
@@ -0,0 +1,220 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_infoblox_uddi_info='Infoblox UDDI
+Site: Infoblox.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_infoblox_uddi
+Options:
+ Infoblox_UDDI_Key API Key for Infoblox UDDI
+ Infoblox_Portal URL, e.g. "csp.infoblox.com" or "csp.eu.infoblox.com"
+Issues: github.com/acmesh-official/acme.sh/issues
+Author: Stefan Riegel
+'
+
+######## Public functions #####################
+
+#Usage: dns_infoblox_uddi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_infoblox_uddi_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ Infoblox_UDDI_Key="${Infoblox_UDDI_Key:-$(_readaccountconf_mutable Infoblox_UDDI_Key)}"
+ Infoblox_Portal="${Infoblox_Portal:-$(_readaccountconf_mutable Infoblox_Portal)}"
+
+ _info "Using Infoblox UDDI API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ if [ -z "$Infoblox_UDDI_Key" ] || [ -z "$Infoblox_Portal" ]; then
+ Infoblox_UDDI_Key=""
+ Infoblox_Portal=""
+ _err "You didn't specify the Infoblox UDDI key or server (Infoblox_UDDI_Key; Infoblox_Portal)."
+ _err "Please set them via EXPORT Infoblox_UDDI_Key=your_key, EXPORT Infoblox_Portal=csp.infoblox.com and try again."
+ return 1
+ fi
+
+ _saveaccountconf_mutable Infoblox_UDDI_Key "$Infoblox_UDDI_Key"
+ _saveaccountconf_mutable Infoblox_Portal "$Infoblox_Portal"
+
+ export _H1="Authorization: token $Infoblox_UDDI_Key"
+ export _H2="Content-Type: application/json"
+
+ zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
+ _debug "Fetching zones from: $zone_url"
+ zone_result="$(_get "$zone_url")"
+ _debug2 "zone_result: $zone_result"
+
+ if [ "$?" != "0" ]; then
+ _err "Error fetching zones from Infoblox API"
+ return 1
+ fi
+
+ fulldomain_no_acme=$(echo "$fulldomain" | sed 's/^_acme-challenge\.//')
+ _debug "Looking for zone matching domain: $fulldomain_no_acme"
+
+ zone_fqdn=""
+ temp_domain="$fulldomain_no_acme"
+
+ while [ -n "$temp_domain" ]; do
+ _debug "Checking if '$temp_domain' is a zone..."
+ if echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\"" || echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\.\""; then
+ zone_fqdn="$temp_domain"
+ _debug "Found matching zone: $zone_fqdn"
+ break
+ fi
+ temp_domain=$(echo "$temp_domain" | sed 's/^[^.]*\.//')
+ if ! echo "$temp_domain" | grep -q '\.'; then
+ break
+ fi
+ done
+
+ if [ -z "$zone_fqdn" ]; then
+ _err "Could not determine zone for domain $fulldomain"
+ _err "Available zones: $(echo "$zone_result" | _egrep_o '"fqdn":"[^"]*"' | sed 's/"fqdn":"//;s/"//')"
+ return 1
+ fi
+
+ zone_id=$(echo "$zone_result" | jq -r '(.results // .)[] | select(.fqdn == "'"$zone_fqdn"'" or .fqdn == "'"$zone_fqdn"'.") | .id' | head -1)
+
+ _debug "zone_id: $zone_id"
+
+ if [ -z "$zone_id" ]; then
+ _err "Could not find zone ID for $zone_fqdn"
+ _debug "Zone result: $zone_result"
+ return 1
+ fi
+
+ _debug "Extracting name_in_zone from fulldomain='$fulldomain' with zone_fqdn='$zone_fqdn'"
+ name_in_zone=$(echo "$fulldomain" | sed "s/\.$zone_fqdn\$//")
+ _debug "name_in_zone after removing zone: '$name_in_zone'"
+ name_in_zone=$(echo "$name_in_zone" | sed 's/\.$//')
+ _debug "name_in_zone final: '$name_in_zone'"
+
+ baseurl="https://$Infoblox_Portal/api/ddi/v1/dns/record"
+
+ body="{\"type\":\"TXT\",\"name_in_zone\":\"$name_in_zone\",\"zone\":\"$zone_id\",\"ttl\":120,\"inheritance_sources\":{\"ttl\":{\"action\":\"override\"}},\"rdata\":{\"text\":\"$txtvalue\"}}"
+
+ _debug "POST URL: $baseurl"
+ _debug "POST body: $body"
+ result="$(_post "$body" "$baseurl" "" "POST")"
+ _debug "POST result: $result"
+
+ if echo "$result" | grep -q '"id"'; then
+ record_id=$(echo "$result" | _egrep_o '"id":"[^"]*"' | head -1 | sed 's/"id":"\([^"]*\)"/\1/')
+ _info "Successfully created TXT record with ID: $record_id"
+ return 0
+ else
+ _err "Error encountered during record addition"
+ _err "Response: $result"
+ return 1
+ fi
+}
+
+#Usage: dns_infoblox_uddi_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_infoblox_uddi_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ Infoblox_UDDI_Key="${Infoblox_UDDI_Key:-$(_readaccountconf_mutable Infoblox_UDDI_Key)}"
+ Infoblox_Portal="${Infoblox_Portal:-$(_readaccountconf_mutable Infoblox_Portal)}"
+
+ if [ -z "$Infoblox_UDDI_Key" ] || [ -z "$Infoblox_Portal" ]; then
+ _err "Credentials not found"
+ return 1
+ fi
+
+ _info "Using Infoblox UDDI API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ export _H1="Authorization: token $Infoblox_UDDI_Key"
+ export _H2="Content-Type: application/json"
+
+ zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
+ _debug "Fetching zones from: $zone_url"
+ zone_result="$(_get "$zone_url")"
+ _debug2 "zone_result: $zone_result"
+
+ if [ "$?" != "0" ]; then
+ _err "Error fetching zones from Infoblox API"
+ return 1
+ fi
+
+ fulldomain_no_acme=$(echo "$fulldomain" | sed 's/^_acme-challenge\.//')
+ _debug "Looking for zone matching domain: $fulldomain_no_acme"
+
+ zone_fqdn=""
+ temp_domain="$fulldomain_no_acme"
+
+ while [ -n "$temp_domain" ]; do
+ _debug "Checking if '$temp_domain' is a zone..."
+ if echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\"" || echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\.\""; then
+ zone_fqdn="$temp_domain"
+ _debug "Found matching zone: $zone_fqdn"
+ break
+ fi
+ temp_domain=$(echo "$temp_domain" | sed 's/^[^.]*\.//')
+ if ! echo "$temp_domain" | grep -q '\.'; then
+ break
+ fi
+ done
+
+ if [ -z "$zone_fqdn" ]; then
+ _err "Could not determine zone for domain $fulldomain"
+ _err "Available zones: $(echo "$zone_result" | _egrep_o '"fqdn":"[^"]*"' | sed 's/"fqdn":"//;s/"//')"
+ return 1
+ fi
+
+ zone_id=$(echo "$zone_result" | jq -r '(.results // .)[] | select(.fqdn == "'"$zone_fqdn"'" or .fqdn == "'"$zone_fqdn"'.") | .id' | head -1)
+
+ _debug "zone_id: $zone_id"
+
+ if [ -z "$zone_id" ]; then
+ _err "Could not find zone ID for $zone_fqdn"
+ _debug "Zone result: $zone_result"
+ return 1
+ fi
+
+ name_in_zone=$(echo "$fulldomain" | sed "s/\.$zone_fqdn\$//" | sed 's/\.$//')
+ _debug "name_in_zone: $name_in_zone"
+
+ filter="type eq 'TXT' and name_in_zone eq '$name_in_zone' and zone eq '$zone_id'"
+ filter_encoded=$(_url_encode "$filter")
+ geturl="https://$Infoblox_Portal/api/ddi/v1/dns/record?_filter=$filter_encoded"
+ _debug "GET URL: $geturl"
+
+ result="$(_get "$geturl")"
+ _debug "GET result: $result"
+
+ if echo "$result" | grep -q '"results":'; then
+ record_count=$(echo "$result" | jq -r '.results | length')
+ _debug "Found $record_count result(s)"
+
+ record_id=$(echo "$result" | jq -r '.results[] | select(.rdata.text == "'"$txtvalue"'") | .id' | head -1)
+
+ if [ -n "$record_id" ]; then
+ record_uuid=$(echo "$record_id" | sed 's/.*\/\([a-f0-9-]*\)$/\1/')
+ _debug "Found record UUID: $record_uuid"
+
+ delurl="https://$Infoblox_Portal/api/ddi/v1/dns/record/$record_uuid"
+ _debug "DELETE URL: $delurl"
+ rmResult="$(_post "" "$delurl" "" "DELETE")"
+
+ if [ -z "$rmResult" ] || [ "$rmResult" = "{}" ]; then
+ _info "Successfully deleted the txt record"
+ return 0
+ else
+ _err "Error occurred during txt record delete"
+ _err "Response: $rmResult"
+ return 1
+ fi
+ else
+ _err "Record to delete didn't match an existing record (no matching txtvalue found)"
+ _debug "Looking for txtvalue: $txtvalue"
+ return 1
+ fi
+ else
+ _err "Record to delete didn't match an existing record (no results found)"
+ _debug "Response: $result"
+ return 1
+ fi
+}
From 657b7195d6427c0bc2110c609b0a27365361597f Mon Sep 17 00:00:00 2001
From: Stefan Riegel
Date: Sat, 29 Nov 2025 23:06:20 +0100
Subject: [PATCH 102/125] Fix Authorization header format
---
dnsapi/dns_infoblox_uddi.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dnsapi/dns_infoblox_uddi.sh b/dnsapi/dns_infoblox_uddi.sh
index 545ce41d..c49a7f82 100644
--- a/dnsapi/dns_infoblox_uddi.sh
+++ b/dnsapi/dns_infoblox_uddi.sh
@@ -35,7 +35,7 @@ dns_infoblox_uddi_add() {
_saveaccountconf_mutable Infoblox_UDDI_Key "$Infoblox_UDDI_Key"
_saveaccountconf_mutable Infoblox_Portal "$Infoblox_Portal"
- export _H1="Authorization: token $Infoblox_UDDI_Key"
+ export _H1="Authorization: Token $Infoblox_UDDI_Key"
export _H2="Content-Type: application/json"
zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
@@ -126,7 +126,7 @@ dns_infoblox_uddi_rm() {
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
- export _H1="Authorization: token $Infoblox_UDDI_Key"
+ export _H1="Authorization: Token $Infoblox_UDDI_Key"
export _H2="Content-Type: application/json"
zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
From eeb91de6a369e209558147f49dbe9a98b08a9a0e Mon Sep 17 00:00:00 2001
From: Stefan Riegel
Date: Sat, 29 Nov 2025 23:13:52 +0100
Subject: [PATCH 103/125] Replace jq with shell-based JSON parsing
---
dnsapi/dns_infoblox_uddi.sh | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/dnsapi/dns_infoblox_uddi.sh b/dnsapi/dns_infoblox_uddi.sh
index c49a7f82..674090be 100644
--- a/dnsapi/dns_infoblox_uddi.sh
+++ b/dnsapi/dns_infoblox_uddi.sh
@@ -73,7 +73,7 @@ dns_infoblox_uddi_add() {
return 1
fi
- zone_id=$(echo "$zone_result" | jq -r '(.results // .)[] | select(.fqdn == "'"$zone_fqdn"'" or .fqdn == "'"$zone_fqdn"'.") | .id' | head -1)
+ zone_id=$(echo "$zone_result" | _egrep_o '"id":"dns/auth_zone/[^"]*"' | _egrep_o 'dns/auth_zone/[^"]*' | _head_n 1)
_debug "zone_id: $zone_id"
@@ -164,7 +164,7 @@ dns_infoblox_uddi_rm() {
return 1
fi
- zone_id=$(echo "$zone_result" | jq -r '(.results // .)[] | select(.fqdn == "'"$zone_fqdn"'" or .fqdn == "'"$zone_fqdn"'.") | .id' | head -1)
+ zone_id=$(echo "$zone_result" | _egrep_o '"id":"dns/auth_zone/[^"]*"' | _egrep_o 'dns/auth_zone/[^"]*' | _head_n 1)
_debug "zone_id: $zone_id"
@@ -186,10 +186,8 @@ dns_infoblox_uddi_rm() {
_debug "GET result: $result"
if echo "$result" | grep -q '"results":'; then
- record_count=$(echo "$result" | jq -r '.results | length')
- _debug "Found $record_count result(s)"
-
- record_id=$(echo "$result" | jq -r '.results[] | select(.rdata.text == "'"$txtvalue"'") | .id' | head -1)
+ record_id=$(echo "$result" | _egrep_o '"id":"dns/record/[^"]*"' | _egrep_o 'dns/record/[^"]*' | _head_n 1)
+ _debug "Found record_id: $record_id"
if [ -n "$record_id" ]; then
record_uuid=$(echo "$record_id" | sed 's/.*\/\([a-f0-9-]*\)$/\1/')
From ca35e8c1189b2aa75cf022913154c10aaee30732 Mon Sep 17 00:00:00 2001
From: Stefan Riegel
Date: Sat, 29 Nov 2025 23:32:28 +0100
Subject: [PATCH 104/125] Fix zone_id extraction to query correct zone
---
dnsapi/dns_infoblox_uddi.sh | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/dnsapi/dns_infoblox_uddi.sh b/dnsapi/dns_infoblox_uddi.sh
index 674090be..8dfeab5f 100644
--- a/dnsapi/dns_infoblox_uddi.sh
+++ b/dnsapi/dns_infoblox_uddi.sh
@@ -73,7 +73,14 @@ dns_infoblox_uddi_add() {
return 1
fi
- zone_id=$(echo "$zone_result" | _egrep_o '"id":"dns/auth_zone/[^"]*"' | _egrep_o 'dns/auth_zone/[^"]*' | _head_n 1)
+ # Fetch exact zone_id for the matched fqdn using server-side filtering
+ filter="fqdn eq '$zone_fqdn.' or fqdn eq '$zone_fqdn'"
+ filter_encoded=$(_url_encode "$filter")
+ zone_query="$zone_url?_filter=$filter_encoded"
+ _debug "Fetching zone_id with filter: $zone_query"
+ zone_lookup="$(_get "$zone_query")"
+ _debug2 "zone_lookup: $zone_lookup"
+ zone_id=$(echo "$zone_lookup" | _egrep_o '"id":"dns/auth_zone/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
_debug "zone_id: $zone_id"
@@ -164,7 +171,14 @@ dns_infoblox_uddi_rm() {
return 1
fi
- zone_id=$(echo "$zone_result" | _egrep_o '"id":"dns/auth_zone/[^"]*"' | _egrep_o 'dns/auth_zone/[^"]*' | _head_n 1)
+ # Fetch exact zone_id for the matched fqdn using server-side filtering
+ filter="fqdn eq '$zone_fqdn.' or fqdn eq '$zone_fqdn'"
+ filter_encoded=$(_url_encode "$filter")
+ zone_query="$zone_url?_filter=$filter_encoded"
+ _debug "Fetching zone_id with filter: $zone_query"
+ zone_lookup="$(_get "$zone_query")"
+ _debug2 "zone_lookup: $zone_lookup"
+ zone_id=$(echo "$zone_lookup" | _egrep_o '"id":"dns/auth_zone/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
_debug "zone_id: $zone_id"
@@ -177,7 +191,7 @@ dns_infoblox_uddi_rm() {
name_in_zone=$(echo "$fulldomain" | sed "s/\.$zone_fqdn\$//" | sed 's/\.$//')
_debug "name_in_zone: $name_in_zone"
- filter="type eq 'TXT' and name_in_zone eq '$name_in_zone' and zone eq '$zone_id'"
+ filter="type eq 'TXT' and name_in_zone eq '$name_in_zone' and zone eq '$zone_id' and rdata.text eq '$txtvalue'"
filter_encoded=$(_url_encode "$filter")
geturl="https://$Infoblox_Portal/api/ddi/v1/dns/record?_filter=$filter_encoded"
_debug "GET URL: $geturl"
@@ -186,7 +200,7 @@ dns_infoblox_uddi_rm() {
_debug "GET result: $result"
if echo "$result" | grep -q '"results":'; then
- record_id=$(echo "$result" | _egrep_o '"id":"dns/record/[^"]*"' | _egrep_o 'dns/record/[^"]*' | _head_n 1)
+ record_id=$(echo "$result" | _egrep_o '"id":"dns/record/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
_debug "Found record_id: $record_id"
if [ -n "$record_id" ]; then
From 490b9e2d09d998cee6736176e8c7eefa08b6029b Mon Sep 17 00:00:00 2001
From: Stefan Riegel
Date: Sat, 29 Nov 2025 23:39:43 +0100
Subject: [PATCH 105/125] Clean up debug statements
---
dnsapi/dns_infoblox_uddi.sh | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/dnsapi/dns_infoblox_uddi.sh b/dnsapi/dns_infoblox_uddi.sh
index 8dfeab5f..ebf4484c 100644
--- a/dnsapi/dns_infoblox_uddi.sh
+++ b/dnsapi/dns_infoblox_uddi.sh
@@ -39,7 +39,6 @@ dns_infoblox_uddi_add() {
export _H2="Content-Type: application/json"
zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
- _debug "Fetching zones from: $zone_url"
zone_result="$(_get "$zone_url")"
_debug2 "zone_result: $zone_result"
@@ -77,12 +76,11 @@ dns_infoblox_uddi_add() {
filter="fqdn eq '$zone_fqdn.' or fqdn eq '$zone_fqdn'"
filter_encoded=$(_url_encode "$filter")
zone_query="$zone_url?_filter=$filter_encoded"
- _debug "Fetching zone_id with filter: $zone_query"
zone_lookup="$(_get "$zone_query")"
_debug2 "zone_lookup: $zone_lookup"
zone_id=$(echo "$zone_lookup" | _egrep_o '"id":"dns/auth_zone/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
- _debug "zone_id: $zone_id"
+ _debug zone_id "$zone_id"
if [ -z "$zone_id" ]; then
_err "Could not find zone ID for $zone_fqdn"
@@ -90,20 +88,16 @@ dns_infoblox_uddi_add() {
return 1
fi
- _debug "Extracting name_in_zone from fulldomain='$fulldomain' with zone_fqdn='$zone_fqdn'"
name_in_zone=$(echo "$fulldomain" | sed "s/\.$zone_fqdn\$//")
- _debug "name_in_zone after removing zone: '$name_in_zone'"
name_in_zone=$(echo "$name_in_zone" | sed 's/\.$//')
- _debug "name_in_zone final: '$name_in_zone'"
+ _debug name_in_zone "$name_in_zone"
baseurl="https://$Infoblox_Portal/api/ddi/v1/dns/record"
body="{\"type\":\"TXT\",\"name_in_zone\":\"$name_in_zone\",\"zone\":\"$zone_id\",\"ttl\":120,\"inheritance_sources\":{\"ttl\":{\"action\":\"override\"}},\"rdata\":{\"text\":\"$txtvalue\"}}"
- _debug "POST URL: $baseurl"
- _debug "POST body: $body"
result="$(_post "$body" "$baseurl" "" "POST")"
- _debug "POST result: $result"
+ _debug2 result "$result"
if echo "$result" | grep -q '"id"'; then
record_id=$(echo "$result" | _egrep_o '"id":"[^"]*"' | head -1 | sed 's/"id":"\([^"]*\)"/\1/')
@@ -137,7 +131,6 @@ dns_infoblox_uddi_rm() {
export _H2="Content-Type: application/json"
zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
- _debug "Fetching zones from: $zone_url"
zone_result="$(_get "$zone_url")"
_debug2 "zone_result: $zone_result"
@@ -175,12 +168,11 @@ dns_infoblox_uddi_rm() {
filter="fqdn eq '$zone_fqdn.' or fqdn eq '$zone_fqdn'"
filter_encoded=$(_url_encode "$filter")
zone_query="$zone_url?_filter=$filter_encoded"
- _debug "Fetching zone_id with filter: $zone_query"
zone_lookup="$(_get "$zone_query")"
_debug2 "zone_lookup: $zone_lookup"
zone_id=$(echo "$zone_lookup" | _egrep_o '"id":"dns/auth_zone/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
- _debug "zone_id: $zone_id"
+ _debug zone_id "$zone_id"
if [ -z "$zone_id" ]; then
_err "Could not find zone ID for $zone_fqdn"
@@ -189,15 +181,14 @@ dns_infoblox_uddi_rm() {
fi
name_in_zone=$(echo "$fulldomain" | sed "s/\.$zone_fqdn\$//" | sed 's/\.$//')
- _debug "name_in_zone: $name_in_zone"
+ _debug name_in_zone "$name_in_zone"
filter="type eq 'TXT' and name_in_zone eq '$name_in_zone' and zone eq '$zone_id' and rdata.text eq '$txtvalue'"
filter_encoded=$(_url_encode "$filter")
geturl="https://$Infoblox_Portal/api/ddi/v1/dns/record?_filter=$filter_encoded"
- _debug "GET URL: $geturl"
result="$(_get "$geturl")"
- _debug "GET result: $result"
+ _debug2 result "$result"
if echo "$result" | grep -q '"results":'; then
record_id=$(echo "$result" | _egrep_o '"id":"dns/record/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
@@ -205,10 +196,9 @@ dns_infoblox_uddi_rm() {
if [ -n "$record_id" ]; then
record_uuid=$(echo "$record_id" | sed 's/.*\/\([a-f0-9-]*\)$/\1/')
- _debug "Found record UUID: $record_uuid"
+ _debug record_uuid "$record_uuid"
delurl="https://$Infoblox_Portal/api/ddi/v1/dns/record/$record_uuid"
- _debug "DELETE URL: $delurl"
rmResult="$(_post "" "$delurl" "" "DELETE")"
if [ -z "$rmResult" ] || [ "$rmResult" = "{}" ]; then
@@ -221,7 +211,6 @@ dns_infoblox_uddi_rm() {
fi
else
_err "Record to delete didn't match an existing record (no matching txtvalue found)"
- _debug "Looking for txtvalue: $txtvalue"
return 1
fi
else
From 890ab4a7bbfc9e950ba0ae103796e066442c25ee Mon Sep 17 00:00:00 2001
From: Stefan Riegel
Date: Sun, 30 Nov 2025 00:48:15 +0100
Subject: [PATCH 106/125] Refactor dns_infoblox_uddi.sh: Fix zone detection and
add wildcard cert support
- Added _get_root() helper function for proper zone detection
- Fixed zone ID extraction to match dns/auth_zone/* pattern
- Added _infoblox_rest() wrapper for API calls with proper auth
- Improved error handling for authentication failures
- Added support for wildcard certificates (multiple TXT records)
- Filter by exact txtvalue when deleting records
- Follow acme.sh best practices and conventions
Tested with:
- Standard domain certificates
- Wildcard certificates (*.domain.com)
- Multiple subdomains
- Staging and production Let's Encrypt
---
dnsapi/dns_infoblox_uddi.sh | 289 +++++++++++++++++++-----------------
1 file changed, 156 insertions(+), 133 deletions(-)
diff --git a/dnsapi/dns_infoblox_uddi.sh b/dnsapi/dns_infoblox_uddi.sh
index ebf4484c..54cfe47b 100644
--- a/dnsapi/dns_infoblox_uddi.sh
+++ b/dnsapi/dns_infoblox_uddi.sh
@@ -10,6 +10,8 @@ Issues: github.com/acmesh-official/acme.sh/issues
Author: Stefan Riegel
'
+Infoblox_UDDI_Api="https://"
+
######## Public functions #####################
#Usage: dns_infoblox_uddi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@@ -38,76 +40,42 @@ dns_infoblox_uddi_add() {
export _H1="Authorization: Token $Infoblox_UDDI_Key"
export _H2="Content-Type: application/json"
- zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
- zone_result="$(_get "$zone_url")"
- _debug2 "zone_result: $zone_result"
-
- if [ "$?" != "0" ]; then
- _err "Error fetching zones from Infoblox API"
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
return 1
fi
-
- fulldomain_no_acme=$(echo "$fulldomain" | sed 's/^_acme-challenge\.//')
- _debug "Looking for zone matching domain: $fulldomain_no_acme"
-
- zone_fqdn=""
- temp_domain="$fulldomain_no_acme"
-
- while [ -n "$temp_domain" ]; do
- _debug "Checking if '$temp_domain' is a zone..."
- if echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\"" || echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\.\""; then
- zone_fqdn="$temp_domain"
- _debug "Found matching zone: $zone_fqdn"
- break
- fi
- temp_domain=$(echo "$temp_domain" | sed 's/^[^.]*\.//')
- if ! echo "$temp_domain" | grep -q '\.'; then
- break
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting existing txt records"
+ _infoblox_rest GET "dns/record?_filter=type%20eq%20'TXT'%20and%20name_in_zone%20eq%20'$_sub_domain'%20and%20zone%20eq%20'$_domain_id'"
+
+ _info "Adding record"
+ body="{\"type\":\"TXT\",\"name_in_zone\":\"$_sub_domain\",\"zone\":\"$_domain_id\",\"ttl\":120,\"inheritance_sources\":{\"ttl\":{\"action\":\"override\"}},\"rdata\":{\"text\":\"$txtvalue\"}}"
+
+ if _infoblox_rest POST "dns/record" "$body"; then
+ if _contains "$response" "$txtvalue"; then
+ _info "Added, OK"
+ return 0
+ elif _contains "$response" '"error"'; then
+ # Check if record already exists
+ if _contains "$response" "already exists" || _contains "$response" "duplicate"; then
+ _info "Already exists, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ _err "Response: $response"
+ return 1
+ fi
+ else
+ _info "Added, OK"
+ return 0
fi
- done
-
- if [ -z "$zone_fqdn" ]; then
- _err "Could not determine zone for domain $fulldomain"
- _err "Available zones: $(echo "$zone_result" | _egrep_o '"fqdn":"[^"]*"' | sed 's/"fqdn":"//;s/"//')"
- return 1
- fi
-
- # Fetch exact zone_id for the matched fqdn using server-side filtering
- filter="fqdn eq '$zone_fqdn.' or fqdn eq '$zone_fqdn'"
- filter_encoded=$(_url_encode "$filter")
- zone_query="$zone_url?_filter=$filter_encoded"
- zone_lookup="$(_get "$zone_query")"
- _debug2 "zone_lookup: $zone_lookup"
- zone_id=$(echo "$zone_lookup" | _egrep_o '"id":"dns/auth_zone/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
-
- _debug zone_id "$zone_id"
-
- if [ -z "$zone_id" ]; then
- _err "Could not find zone ID for $zone_fqdn"
- _debug "Zone result: $zone_result"
- return 1
- fi
-
- name_in_zone=$(echo "$fulldomain" | sed "s/\.$zone_fqdn\$//")
- name_in_zone=$(echo "$name_in_zone" | sed 's/\.$//')
- _debug name_in_zone "$name_in_zone"
-
- baseurl="https://$Infoblox_Portal/api/ddi/v1/dns/record"
-
- body="{\"type\":\"TXT\",\"name_in_zone\":\"$name_in_zone\",\"zone\":\"$zone_id\",\"ttl\":120,\"inheritance_sources\":{\"ttl\":{\"action\":\"override\"}},\"rdata\":{\"text\":\"$txtvalue\"}}"
-
- result="$(_post "$body" "$baseurl" "" "POST")"
- _debug2 result "$result"
-
- if echo "$result" | grep -q '"id"'; then
- record_id=$(echo "$result" | _egrep_o '"id":"[^"]*"' | head -1 | sed 's/"id":"\([^"]*\)"/\1/')
- _info "Successfully created TXT record with ID: $record_id"
- return 0
- else
- _err "Error encountered during record addition"
- _err "Response: $result"
- return 1
fi
+ _err "Add txt record error."
+ return 1
}
#Usage: dns_infoblox_uddi_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@@ -130,92 +98,147 @@ dns_infoblox_uddi_rm() {
export _H1="Authorization: Token $Infoblox_UDDI_Key"
export _H2="Content-Type: application/json"
- zone_url="https://$Infoblox_Portal/api/ddi/v1/dns/auth_zone"
- zone_result="$(_get "$zone_url")"
- _debug2 "zone_result: $zone_result"
-
- if [ "$?" != "0" ]; then
- _err "Error fetching zones from Infoblox API"
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
return 1
fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
- fulldomain_no_acme=$(echo "$fulldomain" | sed 's/^_acme-challenge\.//')
- _debug "Looking for zone matching domain: $fulldomain_no_acme"
+ _debug "Getting txt records to delete"
+ # Filter by txtvalue to support wildcard certs (multiple TXT records)
+ filter="type%20eq%20'TXT'%20and%20name_in_zone%20eq%20'$_sub_domain'%20and%20zone%20eq%20'$_domain_id'%20and%20rdata.text%20eq%20'$txtvalue'"
+ _infoblox_rest GET "dns/record?_filter=$filter"
- zone_fqdn=""
- temp_domain="$fulldomain_no_acme"
+ if ! _contains "$response" '"results"'; then
+ _info "Don't need to remove, record not found."
+ return 0
+ fi
- while [ -n "$temp_domain" ]; do
- _debug "Checking if '$temp_domain' is a zone..."
- if echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\"" || echo "$zone_result" | grep -q "\"fqdn\":\"$temp_domain\.\""; then
- zone_fqdn="$temp_domain"
- _debug "Found matching zone: $zone_fqdn"
- break
- fi
- temp_domain=$(echo "$temp_domain" | sed 's/^[^.]*\.//')
- if ! echo "$temp_domain" | grep -q '\.'; then
- break
- fi
- done
+ record_id=$(echo "$response" | _egrep_o '"id":[[:space:]]*"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
+ _debug "record_id" "$record_id"
- if [ -z "$zone_fqdn" ]; then
- _err "Could not determine zone for domain $fulldomain"
- _err "Available zones: $(echo "$zone_result" | _egrep_o '"fqdn":"[^"]*"' | sed 's/"fqdn":"//;s/"//')"
- return 1
+ if [ -z "$record_id" ]; then
+ _info "Don't need to remove, record not found."
+ return 0
fi
- # Fetch exact zone_id for the matched fqdn using server-side filtering
- filter="fqdn eq '$zone_fqdn.' or fqdn eq '$zone_fqdn'"
- filter_encoded=$(_url_encode "$filter")
- zone_query="$zone_url?_filter=$filter_encoded"
- zone_lookup="$(_get "$zone_query")"
- _debug2 "zone_lookup: $zone_lookup"
- zone_id=$(echo "$zone_lookup" | _egrep_o '"id":"dns/auth_zone/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
-
- _debug zone_id "$zone_id"
+ # Extract UUID from the full record ID (format: dns/record/uuid)
+ record_uuid=$(echo "$record_id" | sed 's|.*/||')
+ _debug "record_uuid" "$record_uuid"
- if [ -z "$zone_id" ]; then
- _err "Could not find zone ID for $zone_fqdn"
- _debug "Zone result: $zone_result"
+ if ! _infoblox_rest DELETE "dns/record/$record_uuid"; then
+ _err "Delete record error."
return 1
fi
- name_in_zone=$(echo "$fulldomain" | sed "s/\.$zone_fqdn\$//" | sed 's/\.$//')
- _debug name_in_zone "$name_in_zone"
-
- filter="type eq 'TXT' and name_in_zone eq '$name_in_zone' and zone eq '$zone_id' and rdata.text eq '$txtvalue'"
- filter_encoded=$(_url_encode "$filter")
- geturl="https://$Infoblox_Portal/api/ddi/v1/dns/record?_filter=$filter_encoded"
-
- result="$(_get "$geturl")"
- _debug2 result "$result"
-
- if echo "$result" | grep -q '"results":'; then
- record_id=$(echo "$result" | _egrep_o '"id":"dns/record/[^\"]*"' | _head_n 1 | sed 's/.*"id":"\([^\"]*\)".*/\1/')
- _debug "Found record_id: $record_id"
+ _info "Removed record successfully"
+ return 0
+}
- if [ -n "$record_id" ]; then
- record_uuid=$(echo "$record_id" | sed 's/.*\/\([a-f0-9-]*\)$/\1/')
- _debug record_uuid "$record_uuid"
+#################### Private functions below ##################################
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=dns/auth_zone/xxxx-xxxx
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+
+ # Remove _acme-challenge prefix if present
+ domain_no_acme=$(echo "$domain" | sed 's/^_acme-challenge\.//')
+
+ while true; do
+ h=$(printf "%s" "$domain_no_acme" | cut -d . -f "$i"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ # not valid
+ return 1
+ fi
- delurl="https://$Infoblox_Portal/api/ddi/v1/dns/record/$record_uuid"
- rmResult="$(_post "" "$delurl" "" "DELETE")"
+ # Query for the zone with both trailing dot and without
+ filter="fqdn%20eq%20'$h.'%20or%20fqdn%20eq%20'$h'"
+ if ! _infoblox_rest GET "dns/auth_zone?_filter=$filter"; then
+ # API error - don't continue if we get auth errors
+ if _contains "$response" "401" || _contains "$response" "Authorization"; then
+ _err "Authentication failed. Please check your Infoblox_UDDI_Key."
+ return 1
+ fi
+ # For other errors, continue to parent domain
+ p=$i
+ i=$((i + 1))
+ continue
+ fi
- if [ -z "$rmResult" ] || [ "$rmResult" = "{}" ]; then
- _info "Successfully deleted the txt record"
+ # Check if response contains results (even if empty)
+ if _contains "$response" '"results"'; then
+ # Extract zone ID - must match the pattern dns/auth_zone/...
+ zone_id=$(echo "$response" | _egrep_o '"id":[[:space:]]*"dns/auth_zone/[^"]*"' | _head_n 1 | cut -d '"' -f 4)
+ if [ -n "$zone_id" ]; then
+ # Found the zone
+ _domain="$h"
+ _domain_id="$zone_id"
+
+ # Calculate subdomain
+ if [ "$_domain" = "$domain" ]; then
+ _sub_domain=""
+ else
+ _cutlength=$((${#domain} - ${#_domain} - 1))
+ _sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
+ fi
+
return 0
- else
- _err "Error occurred during txt record delete"
- _err "Response: $rmResult"
- return 1
fi
- else
- _err "Record to delete didn't match an existing record (no matching txtvalue found)"
- return 1
fi
+
+ p=$i
+ i=$((i + 1))
+ done
+
+ return 1
+}
+
+# _infoblox_rest GET "dns/record?_filter=..."
+# _infoblox_rest POST "dns/record" "{json body}"
+# _infoblox_rest DELETE "dns/record/uuid"
+_infoblox_rest() {
+ method=$1
+ ep="$2"
+ data="$3"
+
+ _debug "$ep"
+
+ # Ensure credentials are available (when called from _get_root)
+ Infoblox_UDDI_Key="${Infoblox_UDDI_Key:-$(_readaccountconf_mutable Infoblox_UDDI_Key)}"
+ Infoblox_Portal="${Infoblox_Portal:-$(_readaccountconf_mutable Infoblox_Portal)}"
+
+ Infoblox_UDDI_Api="https://$Infoblox_Portal/api/ddi/v1"
+ export _H1="Authorization: Token $Infoblox_UDDI_Key"
+ export _H2="Content-Type: application/json"
+
+ # Debug (masked)
+ _tok_len=$(printf "%s" "$Infoblox_UDDI_Key" | wc -c | tr -d ' \n')
+ _debug2 "Auth header set" "Token len=${_tok_len} on $Infoblox_Portal"
+
+ if [ "$method" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$Infoblox_UDDI_Api/$ep" "" "$method")"
else
- _err "Record to delete didn't match an existing record (no results found)"
- _debug "Response: $result"
+ response="$(_get "$Infoblox_UDDI_Api/$ep")"
+ fi
+
+ _ret="$?"
+ _debug2 response "$response"
+
+ if [ "$_ret" != "0" ]; then
+ _err "Error: $ep"
return 1
fi
+
+ return 0
}
From 36b8ca2bc07d0e46dd65fc8d8365d9ca1797d786 Mon Sep 17 00:00:00 2001
From: Stefan Riegel
Date: Sun, 30 Nov 2025 00:49:36 +0100
Subject: [PATCH 107/125] Fix shfmt formatting: Remove trailing whitespace
---
dnsapi/dns_infoblox_uddi.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dnsapi/dns_infoblox_uddi.sh b/dnsapi/dns_infoblox_uddi.sh
index 54cfe47b..4b15088a 100644
--- a/dnsapi/dns_infoblox_uddi.sh
+++ b/dnsapi/dns_infoblox_uddi.sh
@@ -183,7 +183,7 @@ _get_root() {
# Found the zone
_domain="$h"
_domain_id="$zone_id"
-
+
# Calculate subdomain
if [ "$_domain" = "$domain" ]; then
_sub_domain=""
@@ -191,7 +191,7 @@ _get_root() {
_cutlength=$((${#domain} - ${#_domain} - 1))
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
fi
-
+
return 0
fi
fi
From bee01c938a234021d4aa8d6bcb2cb6d421262573 Mon Sep 17 00:00:00 2001
From: neil
Date: Fri, 5 Dec 2025 21:46:05 +0100
Subject: [PATCH 108/125] add comment
---
.github/workflows/wiki-monitor.yml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/wiki-monitor.yml b/.github/workflows/wiki-monitor.yml
index b0332775..a79d70a4 100644
--- a/.github/workflows/wiki-monitor.yml
+++ b/.github/workflows/wiki-monitor.yml
@@ -22,6 +22,7 @@ jobs:
page_sha=$(jq -r '.pages[0].sha' "$GITHUB_EVENT_PATH")
page_url=$(jq -r '.pages[0].html_url' "$GITHUB_EVENT_PATH")
page_action=$(jq -r '.pages[0].action' "$GITHUB_EVENT_PATH")
+ page_summary=$(jq -r '.pages[0].summary' "$GITHUB_EVENT_PATH")
now="$(date '+%Y-%m-%d %H:%M:%S')"
cd wiki
@@ -35,9 +36,11 @@ jobs:
{
echo "Wiki edited"
echo -n "User: "
- echo "[$actor]($sender_url)"
+ echo "@$actor [$actor]($sender_url)"
echo "Time: $now"
echo "Page: [$page_name]($page_url) (Action: $page_action)"
+ echo "Comment: $page_summary"
+ echo "[Click here to Revert](${page_url}/_history)"
echo ""
echo "----"
echo "### diff:"
From 45cb36f6d909dc5ed5b5534088aed088b9c3e55f Mon Sep 17 00:00:00 2001
From: neil
Date: Fri, 5 Dec 2025 22:31:45 +0100
Subject: [PATCH 109/125] fix
https://github.com/acmesh-official/acme.sh/issues/6246#issuecomment-3610998032
---
dnsapi/dns_ali.sh | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh
index 53a82f91..cbee773e 100755
--- a/dnsapi/dns_ali.sh
+++ b/dnsapi/dns_ali.sh
@@ -97,9 +97,10 @@ _ali_rest() {
}
_ali_nonce() {
- #_head_n 1 /dev/null && return 0
+ fi
+ printf "%s" "$(date +%s)$$$(date +%N)" | _digest sha256 hex | cut -c 1-32
}
_timestamp() {
From 3b2c2b16b2472c986f16a00eb24f27456f57a318 Mon Sep 17 00:00:00 2001
From: neil
Date: Sat, 6 Dec 2025 11:23:28 +0100
Subject: [PATCH 110/125] minor
---
dnsapi/dns_ali.sh | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh
index cbee773e..90196c69 100755
--- a/dnsapi/dns_ali.sh
+++ b/dnsapi/dns_ali.sh
@@ -103,7 +103,7 @@ _ali_nonce() {
printf "%s" "$(date +%s)$$$(date +%N)" | _digest sha256 hex | cut -c 1-32
}
-_timestamp() {
+_ali_timestamp() {
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
}
@@ -151,7 +151,7 @@ _check_exist_query() {
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
- query=$query'&Timestamp='$(_timestamp)
+ query=$query'&Timestamp='$(_ali_timestamp)
query=$query'&TypeKeyWord=TXT'
query=$query'&Version=2015-01-09'
}
@@ -167,7 +167,7 @@ _add_record_query() {
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
- query=$query'&Timestamp='$(_timestamp)
+ query=$query'&Timestamp='$(_ali_timestamp)
query=$query'&Type=TXT'
query=$query'&Value='$3
query=$query'&Version=2015-01-09'
@@ -183,7 +183,7 @@ _delete_record_query() {
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
- query=$query'&Timestamp='$(_timestamp)
+ query=$query'&Timestamp='$(_ali_timestamp)
query=$query'&Version=2015-01-09'
}
@@ -197,7 +197,7 @@ _describe_records_query() {
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
- query=$query'&Timestamp='$(_timestamp)
+ query=$query'&Timestamp='$(_ali_timestamp)
query=$query'&Version=2015-01-09'
}
From 4965c704d7ea39675bc4131b04264611dd6f1aaa Mon Sep 17 00:00:00 2001
From: ufozone
Date: Sun, 7 Dec 2025 15:07:50 +0100
Subject: [PATCH 111/125] Initial commit for mgw-media.de
---
dnsapi/dns_mgwm.sh | 112 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+)
create mode 100644 dnsapi/dns_mgwm.sh
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
new file mode 100644
index 00000000..7715b645
--- /dev/null
+++ b/dnsapi/dns_mgwm.sh
@@ -0,0 +1,112 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+
+# DNS provider information for acme.sh
+dns_mgwm_info='mgw-media.de
+Site: mgw-media.de
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mgwm
+Options:
+ MGWM_CUSTOMER Your customer number
+ MGWM_API_HASH Your API Hash
+Issues: github.com/acmesh-official/acme.sh
+'
+
+# Base URL for the mgw-media.de API
+MGWM_API_BASE="https://api.mgw-media.de/record"
+
+######## Public functions #####################
+# This function is called by acme.sh to add a TXT record.
+dns_mgwm_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using mgw-media.de DNS API for domain $fulldomain"
+ _debug "fulldomain: $fulldomain"
+ _debug "txtvalue: $txtvalue"
+
+ # Call private function to load and save environment variables and set up the Basic Auth Header.
+ if ! _mgwm_init_env; then
+ return 1
+ fi
+
+ # Construct the API URL for adding a record.
+ _add_url="${MGWM_API_BASE}/add/${fulldomain}/txt/${txtvalue}"
+ _debug "Calling MGWM ADD URL: ${_add_url}"
+
+ # Execute the HTTP GET request with the Authorization Header.
+ # The 5th parameter of _get is where acme.sh expects custom HTTP headers like Authorization.
+ response="$(_get "" "$_add_url" "" "GET" "$_H1")"
+ _debug "MGWM add response: $response"
+
+ # Check the API response for success. The API returns "OK" on success.
+ if [ "$response" = "OK" ]; then
+ _info "TXT record for $fulldomain successfully added via MGWM API."
+ _sleep 10 # Wait briefly for DNS propagation, a common practice in DNS-01 hooks.
+ return 0
+ else
+ _err "mgwm_add: Failed to add TXT record for $fulldomain. Unexpected API Response: '$response'"
+ return 1
+ fi
+}
+
+# This function is called by acme.sh to remove a TXT record after validation.
+dns_mgwm_rm() {
+ fulldomain=$1
+ txtvalue=$2 # This txtvalue is now used to identify the specific record to be removed.
+
+ _info "Removing TXT record for $fulldomain using mgw-media.de DNS API"
+ _debug "fulldomain: $fulldomain"
+ _debug "txtvalue: $txtvalue"
+
+ # Call private function to load and save environment variables and set up the Basic Auth Header.
+ if ! _mgwm_init_env; then
+ return 1
+ fi
+
+ # Construct the API URL for removing a record.
+ # To delete a specific record by its value (as required by ACME v2 for multiple TXT records),
+ # the txtvalue must be part of the URL, similar to the add action.
+ _rm_url="${MGWM_API_BASE}/rm/${fulldomain}/txt/${txtvalue}"
+ _debug "Calling MGWM RM URL: ${_rm_url}"
+
+ # Execute the HTTP GET request with the Authorization Header.
+ response="$(_get "" "$_rm_url" "" "GET" "$_H1")"
+ _debug "MGWM rm response: $response"
+
+ # Check the API response for success. The API returns "OK" on success.
+ if [ "$response" = "OK" ]; then
+ _info "TXT record for $fulldomain successfully removed via MGWM API."
+ return 0
+ else
+ _err "mgwm_rm: Failed to remove TXT record for $fulldomain. Unexpected API Response: '$response'"
+ return 1
+ fi
+}
+
+#################### Private functions below ##################################
+
+# _mgwm_init_env() loads the mgw-media.de API credentials (customer number and hash)
+# from environment variables or acme.sh's configuration, saves them, and
+# prepares the global _H1 variable for Basic Authorization header.
+_mgwm_init_env() {
+ # Load credentials from environment or acme.sh config
+ MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
+ MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
+
+ # Check if credentials are set
+ if [ -z "$MGWM_CUSTOMER" ] || [ -z "$MGWM_API_HASH" ]; then
+ _err "You didn't specify one or more of MGWM_CUSTOMER or MGWM_API_HASH."
+ _err "Please check these environment variables and try again."
+ return 1
+ fi
+
+ # Save credentials for automatic renewal and future calls
+ _saveaccountconf_mutable MGWM_CUSTOMER "$MGWM_CUSTOMER"
+ _saveaccountconf_mutable MGWM_API_HASH "$MGWM_API_HASH"
+
+ # Create the Basic Auth Header. acme.sh's _base64 function is used for encoding.
+ _credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
+ export _H1="Authorization: Basic $_credentials"
+ _debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
+ return 0
+}
From daf7f7c268cc33170e40321ee5b02d96fc2b9960 Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 17:29:24 +0100
Subject: [PATCH 112/125] Refactor dns_mgwm.sh for better API integration
Refactor DNS API script to improve credential handling and update API endpoint.
---
dnsapi/dns_mgwm.sh | 99 +++++++++++++++++++++++++---------------------
1 file changed, 54 insertions(+), 45 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index 7715b645..b3e21726 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -2,17 +2,18 @@
# shellcheck disable=SC2034
# DNS provider information for acme.sh
-dns_mgwm_info='mgw-media.de
+dns_mgwm_info='MGW-MEDIA.DE
Site: mgw-media.de
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mgwm
Options:
- MGWM_CUSTOMER Your customer number
- MGWM_API_HASH Your API Hash
+ MGWM_CUSTOMER Your customer number (username for Basic Auth).
+ MGWM_API_HASH Your API Hash (password for Basic Auth).
Issues: github.com/acmesh-official/acme.sh
+Author: (Your Name or generated by AI)
'
-# Base URL for the mgw-media.de API
-MGWM_API_BASE="https://api.mgw-media.de/record"
+# Base endpoint for the MGW-MEDIA.DE API (parameters will be added as query strings)
+MGWM_API_ENDPOINT="https://api.mgw-media.de/record"
######## Public functions #####################
# This function is called by acme.sh to add a TXT record.
@@ -24,16 +25,32 @@ dns_mgwm_add() {
_debug "fulldomain: $fulldomain"
_debug "txtvalue: $txtvalue"
- # Call private function to load and save environment variables and set up the Basic Auth Header.
- if ! _mgwm_init_env; then
+ # Load credentials from environment or acme.sh config
+ MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
+ MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
+
+ # Check if credentials are set
+ if [ -z "$MGWM_CUSTOMER" ] || [ -z "$MGWM_API_HASH" ]; then
+ _err "You didn't specify one or more of MGWM_CUSTOMER or MGWM_API_HASH."
+ _err "Please check these environment variables and try again."
return 1
fi
- # Construct the API URL for adding a record.
- _add_url="${MGWM_API_BASE}/add/${fulldomain}/txt/${txtvalue}"
+ # Save credentials for automatic renewal and future calls
+ _saveaccountconf_mutable MGWM_CUSTOMER "$MGWM_CUSTOMER"
+ _saveaccountconf_mutable MGWM_API_HASH "$MGWM_API_HASH"
+
+ # Create the Basic Auth Header directly in this function's scope
+ _credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
+ # Export _H1 so _get function can pick it up
+ export _H1="Authorization: Basic $_credentials"
+ _debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
+
+ # Construct the API URL for adding a record with query parameters
+ _add_url="${MGWM_API_ENDPOINT}?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
_debug "Calling MGWM ADD URL: ${_add_url}"
- # Execute the HTTP GET request with the Authorization Header.
+ # Execute the HTTP GET request with the Authorization Header (_H1)
# The 5th parameter of _get is where acme.sh expects custom HTTP headers like Authorization.
response="$(_get "" "$_add_url" "" "GET" "$_H1")"
_debug "MGWM add response: $response"
@@ -52,24 +69,39 @@ dns_mgwm_add() {
# This function is called by acme.sh to remove a TXT record after validation.
dns_mgwm_rm() {
fulldomain=$1
- txtvalue=$2 # This txtvalue is now used to identify the specific record to be removed.
+ txtvalue=$2 # This value is not used by the RM API in this case.
_info "Removing TXT record for $fulldomain using mgw-media.de DNS API"
_debug "fulldomain: $fulldomain"
- _debug "txtvalue: $txtvalue"
+ _debug "txtvalue: $txtvalue" # Still logging for completeness, but not used in URL
- # Call private function to load and save environment variables and set up the Basic Auth Header.
- if ! _mgwm_init_env; then
+ # Load credentials from environment or acme.sh config
+ MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
+ MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
+
+ # Check if credentials are set
+ if [ -z "$MGWM_CUSTOMER" ] || [ -z "$MGWM_API_HASH" ]; then
+ _err "You didn't specify one or more of MGWM_CUSTOMER or MGWM_API_HASH."
+ _err "Please check these environment variables and try again."
return 1
fi
- # Construct the API URL for removing a record.
- # To delete a specific record by its value (as required by ACME v2 for multiple TXT records),
- # the txtvalue must be part of the URL, similar to the add action.
- _rm_url="${MGWM_API_BASE}/rm/${fulldomain}/txt/${txtvalue}"
+ # Save credentials (important for future renewals if not saved by add function)
+ _saveaccountconf_mutable MGWM_CUSTOMER "$MGWM_CUSTOMER"
+ _saveaccountconf_mutable MGWM_API_HASH "$MGWM_API_HASH"
+
+ # Create the Basic Auth Header directly in this function's scope
+ _credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
+ # Export _H1 so _get function can pick it up
+ export _H1="Authorization: Basic $_credentials"
+ _debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
+
+ # Construct the API URL for removing a record with query parameters
+ # The RM API from mgw-media.de does not expect a 'content' parameter.
+ _rm_url="${MGWM_API_ENDPOINT}?action=rm&fulldomain=${fulldomain}&type=txt"
_debug "Calling MGWM RM URL: ${_rm_url}"
- # Execute the HTTP GET request with the Authorization Header.
+ # Execute the HTTP GET request with the Authorization Header (_H1)
response="$(_get "" "$_rm_url" "" "GET" "$_H1")"
_debug "MGWM rm response: $response"
@@ -84,29 +116,6 @@ dns_mgwm_rm() {
}
#################### Private functions below ##################################
-
-# _mgwm_init_env() loads the mgw-media.de API credentials (customer number and hash)
-# from environment variables or acme.sh's configuration, saves them, and
-# prepares the global _H1 variable for Basic Authorization header.
-_mgwm_init_env() {
- # Load credentials from environment or acme.sh config
- MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
- MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
-
- # Check if credentials are set
- if [ -z "$MGWM_CUSTOMER" ] || [ -z "$MGWM_API_HASH" ]; then
- _err "You didn't specify one or more of MGWM_CUSTOMER or MGWM_API_HASH."
- _err "Please check these environment variables and try again."
- return 1
- fi
-
- # Save credentials for automatic renewal and future calls
- _saveaccountconf_mutable MGWM_CUSTOMER "$MGWM_CUSTOMER"
- _saveaccountconf_mutable MGWM_API_HASH "$MGWM_API_HASH"
-
- # Create the Basic Auth Header. acme.sh's _base64 function is used for encoding.
- _credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
- export _H1="Authorization: Basic $_credentials"
- _debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
- return 0
-}
+# The _mgwm_init_env function has been inlined into dns_mgwm_add and dns_mgwm_rm
+# to ensure credentials and the Authorization header are set correctly within
+# each function's sub-shell context.
From 11eaad1fa742d4d55e7a3bc17675b581e2281b59 Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 17:32:30 +0100
Subject: [PATCH 113/125] Update API URLs to include .php extension
---
dnsapi/dns_mgwm.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index b3e21726..42c6d93a 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -47,7 +47,7 @@ dns_mgwm_add() {
_debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
# Construct the API URL for adding a record with query parameters
- _add_url="${MGWM_API_ENDPOINT}?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
+ _add_url="${MGWM_API_ENDPOINT}.php?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
_debug "Calling MGWM ADD URL: ${_add_url}"
# Execute the HTTP GET request with the Authorization Header (_H1)
@@ -98,7 +98,7 @@ dns_mgwm_rm() {
# Construct the API URL for removing a record with query parameters
# The RM API from mgw-media.de does not expect a 'content' parameter.
- _rm_url="${MGWM_API_ENDPOINT}?action=rm&fulldomain=${fulldomain}&type=txt"
+ _rm_url="${MGWM_API_ENDPOINT}.php?action=rm&fulldomain=${fulldomain}&type=txt"
_debug "Calling MGWM RM URL: ${_rm_url}"
# Execute the HTTP GET request with the Authorization Header (_H1)
From e94c6be4a1c877a06afcc0c1b4530b864b777bd5 Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 17:41:27 +0100
Subject: [PATCH 114/125] Update MGWM API endpoint to IPv4
---
dnsapi/dns_mgwm.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index 42c6d93a..a3aabb53 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -13,7 +13,7 @@ Author: (Your Name or generated by AI)
'
# Base endpoint for the MGW-MEDIA.DE API (parameters will be added as query strings)
-MGWM_API_ENDPOINT="https://api.mgw-media.de/record"
+MGWM_API_ENDPOINT="https://ipv4.api.mgw-media.de/record"
######## Public functions #####################
# This function is called by acme.sh to add a TXT record.
From 2ba615555cf214edc447e4435bb20ee30b4340a9 Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 17:59:04 +0100
Subject: [PATCH 115/125] Refactor dns_mgwm.sh for improved API interaction
Refactor MGWM API script to improve clarity and functionality. Update API endpoint and streamline credential handling.
---
dnsapi/dns_mgwm.sh | 108 +++++++++++++++++++++++----------------------
1 file changed, 55 insertions(+), 53 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index a3aabb53..c02b7a6d 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -9,11 +9,12 @@ Options:
MGWM_CUSTOMER Your customer number (username for Basic Auth).
MGWM_API_HASH Your API Hash (password for Basic Auth).
Issues: github.com/acmesh-official/acme.sh
-Author: (Your Name or generated by AI)
+Author: Generated by AI (with user input)
'
-# Base endpoint for the MGW-MEDIA.DE API (parameters will be added as query strings)
-MGWM_API_ENDPOINT="https://ipv4.api.mgw-media.de/record"
+# Direct endpoint for the PHP script with query parameters
+# This variable replaces MGWM_API_BASE when using query-parameter-based URLs directly.
+MGWM_API_ENDPOINT="https://api.mgw-media.de/record.php"
######## Public functions #####################
# This function is called by acme.sh to add a TXT record.
@@ -25,34 +26,20 @@ dns_mgwm_add() {
_debug "fulldomain: $fulldomain"
_debug "txtvalue: $txtvalue"
- # Load credentials from environment or acme.sh config
- MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
- MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
-
- # Check if credentials are set
- if [ -z "$MGWM_CUSTOMER" ] || [ -z "$MGWM_API_HASH" ]; then
- _err "You didn't specify one or more of MGWM_CUSTOMER or MGWM_API_HASH."
- _err "Please check these environment variables and try again."
+ # Call private function to load and save environment variables and set up the Basic Auth Header.
+ if ! _mgwm_init_env; then
return 1
fi
- # Save credentials for automatic renewal and future calls
- _saveaccountconf_mutable MGWM_CUSTOMER "$MGWM_CUSTOMER"
- _saveaccountconf_mutable MGWM_API_HASH "$MGWM_API_HASH"
-
- # Create the Basic Auth Header directly in this function's scope
- _credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
- # Export _H1 so _get function can pick it up
- export _H1="Authorization: Basic $_credentials"
- _debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
-
- # Construct the API URL for adding a record with query parameters
- _add_url="${MGWM_API_ENDPOINT}.php?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
+ # Construct the API URL using query parameters.
+ # This targets the record.php script directly, passing action, fulldomain, type, and content.
+ _add_url="${MGWM_API_ENDPOINT}?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
_debug "Calling MGWM ADD URL: ${_add_url}"
- # Execute the HTTP GET request with the Authorization Header (_H1)
- # The 5th parameter of _get is where acme.sh expects custom HTTP headers like Authorization.
- response="$(_get "" "$_add_url" "" "GET" "$_H1")"
+ # Execute the HTTP GET request.
+ # Correct parameters for _get(): url="$1", onlyheader="$2", t="$3"
+ # The Authorization Header (_H1) is automatically picked up by _get() from the environment.
+ response="$(_get "$_add_url" "" "")" # <-- KORRIGIERTER AUFRUF VON _get()
_debug "MGWM add response: $response"
# Check the API response for success. The API returns "OK" on success.
@@ -69,12 +56,44 @@ dns_mgwm_add() {
# This function is called by acme.sh to remove a TXT record after validation.
dns_mgwm_rm() {
fulldomain=$1
- txtvalue=$2 # This value is not used by the RM API in this case.
+ txtvalue=$2 # This txtvalue is now used to identify the specific record to be removed.
_info "Removing TXT record for $fulldomain using mgw-media.de DNS API"
_debug "fulldomain: $fulldomain"
- _debug "txtvalue: $txtvalue" # Still logging for completeness, but not used in URL
+ _debug "txtvalue: $txtvalue"
+
+ # Call private function to load and save environment variables and set up the Basic Auth Header.
+ if ! _mgwm_init_env; then
+ return 1
+ fi
+ # Construct the API URL for removing a record.
+ # This targets the record.php script directly, passing action, fulldomain, type, and content.
+ _rm_url="${MGWM_API_ENDPOINT}?action=rm&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
+ _debug "Calling MGWM RM URL: ${_rm_url}"
+
+ # Execute the HTTP GET request.
+ # Correct parameters for _get(): url="$1", onlyheader="$2", t="$3"
+ # The Authorization Header (_H1) is automatically picked up by _get() from the environment.
+ response="$(_get "$_rm_url" "" "")" # <-- KORRIGIERTER AUFRUF VON _get()
+ _debug "MGWM rm response: $response"
+
+ # Check the API response for success. The API returns "OK" on success.
+ if [ "$response" = "OK" ]; then
+ _info "TXT record for $fulldomain successfully removed via MGWM API."
+ return 0
+ else
+ _err "mgwm_rm: Failed to remove TXT record for $fulldomain. Unexpected API Response: '$response'"
+ return 1
+ fi
+}
+
+#################### Private functions below ##################################
+
+# _mgwm_init_env() loads the mgw-media.de API credentials (customer number and hash)
+# from environment variables or acme.sh's configuration, saves them, and
+# prepares the global _H1 variable for Basic Authorization header.
+_mgwm_init_env() {
# Load credentials from environment or acme.sh config
MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
@@ -86,36 +105,19 @@ dns_mgwm_rm() {
return 1
fi
- # Save credentials (important for future renewals if not saved by add function)
+ # Save credentials for automatic renewal and future calls
_saveaccountconf_mutable MGWM_CUSTOMER "$MGWM_CUSTOMER"
_saveaccountconf_mutable MGWM_API_HASH "$MGWM_API_HASH"
- # Create the Basic Auth Header directly in this function's scope
+ # Create the Basic Auth Header. acme.sh's _base64 function is used for encoding.
_credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
- # Export _H1 so _get function can pick it up
export _H1="Authorization: Basic $_credentials"
_debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
-
- # Construct the API URL for removing a record with query parameters
- # The RM API from mgw-media.de does not expect a 'content' parameter.
- _rm_url="${MGWM_API_ENDPOINT}.php?action=rm&fulldomain=${fulldomain}&type=txt"
- _debug "Calling MGWM RM URL: ${_rm_url}"
-
- # Execute the HTTP GET request with the Authorization Header (_H1)
- response="$(_get "" "$_rm_url" "" "GET" "$_H1")"
- _debug "MGWM rm response: $response"
-
- # Check the API response for success. The API returns "OK" on success.
- if [ "$response" = "OK" ]; then
- _info "TXT record for $fulldomain successfully removed via MGWM API."
- return 0
- else
- _err "mgwm_rm: Failed to remove TXT record for $fulldomain. Unexpected API Response: '$response'"
- return 1
- fi
+ return 0
}
-#################### Private functions below ##################################
-# The _mgwm_init_env function has been inlined into dns_mgwm_add and dns_mgwm_rm
-# to ensure credentials and the Authorization header are set correctly within
-# each function's sub-shell context.
+# The _get_root function, often found in other acme.sh DNS API scripts,
+# is not necessary for the MGW-MEDIA.DE API.
+# The MGW-MEDIA.DE API directly accepts the complete FQDN (fulldomain)
+# in its URL path and handles the extraction of the subdomain and root domain internally.
+# Therefore, no custom _get_root implementation is needed here.
From 546c2d47d5b6a740eb7cbf874e81529106a1e66d Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 18:11:42 +0100
Subject: [PATCH 116/125] Refactor DNS API for mgw-media.de
Updated DNS API script for mgw-media.de to use new base URL and improved API request structure.
---
dnsapi/dns_mgwm.sh | 43 +++++++++++++++++--------------------------
1 file changed, 17 insertions(+), 26 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index c02b7a6d..f38c184e 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -2,19 +2,17 @@
# shellcheck disable=SC2034
# DNS provider information for acme.sh
-dns_mgwm_info='MGW-MEDIA.DE
+dns_mgwm_info='mgw-media.de
Site: mgw-media.de
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mgwm
Options:
- MGWM_CUSTOMER Your customer number (username for Basic Auth).
- MGWM_API_HASH Your API Hash (password for Basic Auth).
+ MGWM_CUSTOMER Your customer number
+ MGWM_API_HASH Your API Hash
Issues: github.com/acmesh-official/acme.sh
-Author: Generated by AI (with user input)
'
-# Direct endpoint for the PHP script with query parameters
-# This variable replaces MGWM_API_BASE when using query-parameter-based URLs directly.
-MGWM_API_ENDPOINT="https://api.mgw-media.de/record.php"
+# Base URL for the mgw-media.de API
+MGWM_API_BASE="https://api.mgw-media.de/record"
######## Public functions #####################
# This function is called by acme.sh to add a TXT record.
@@ -31,15 +29,14 @@ dns_mgwm_add() {
return 1
fi
- # Construct the API URL using query parameters.
- # This targets the record.php script directly, passing action, fulldomain, type, and content.
- _add_url="${MGWM_API_ENDPOINT}?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
+ # Construct the API URL for adding a record.
+ #_add_url="${MGWM_API_BASE}/add/${fulldomain}/txt/${txtvalue}"
+ _add_url="${MGWM_API_BASE}.php?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
_debug "Calling MGWM ADD URL: ${_add_url}"
- # Execute the HTTP GET request.
- # Correct parameters for _get(): url="$1", onlyheader="$2", t="$3"
- # The Authorization Header (_H1) is automatically picked up by _get() from the environment.
- response="$(_get "$_add_url" "" "")" # <-- KORRIGIERTER AUFRUF VON _get()
+ # Execute the HTTP GET request with the Authorization Header.
+ # The 5th parameter of _get is where acme.sh expects custom HTTP headers like Authorization.
+ response="$(_get "$_add_url")"
_debug "MGWM add response: $response"
# Check the API response for success. The API returns "OK" on success.
@@ -68,14 +65,14 @@ dns_mgwm_rm() {
fi
# Construct the API URL for removing a record.
- # This targets the record.php script directly, passing action, fulldomain, type, and content.
- _rm_url="${MGWM_API_ENDPOINT}?action=rm&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
+ # To delete a specific record by its value (as required by ACME v2 for multiple TXT records),
+ # the txtvalue must be part of the URL, similar to the add action.
+ #_rm_url="${MGWM_API_BASE}/rm/${fulldomain}/txt/${txtvalue}"
+ _rm_url="${MGWM_API_BASE}.php?action=rm&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
_debug "Calling MGWM RM URL: ${_rm_url}"
- # Execute the HTTP GET request.
- # Correct parameters for _get(): url="$1", onlyheader="$2", t="$3"
- # The Authorization Header (_H1) is automatically picked up by _get() from the environment.
- response="$(_get "$_rm_url" "" "")" # <-- KORRIGIERTER AUFRUF VON _get()
+ # Execute the HTTP GET request with the Authorization Header.
+ response="$(_get "$_rm_url")"
_debug "MGWM rm response: $response"
# Check the API response for success. The API returns "OK" on success.
@@ -115,9 +112,3 @@ _mgwm_init_env() {
_debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
return 0
}
-
-# The _get_root function, often found in other acme.sh DNS API scripts,
-# is not necessary for the MGW-MEDIA.DE API.
-# The MGW-MEDIA.DE API directly accepts the complete FQDN (fulldomain)
-# in its URL path and handles the extraction of the subdomain and root domain internally.
-# Therefore, no custom _get_root implementation is needed here.
From d8722c46d9b8ed938122ef54ec89e440878263ac Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 18:26:29 +0100
Subject: [PATCH 117/125] Consolidate API request logic in dns_mgwm.sh
Refactor DNS API functions to use a unified request handler.
---
dnsapi/dns_mgwm.sh | 100 ++++++++++++++++++++++-----------------------
1 file changed, 49 insertions(+), 51 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index f38c184e..ca48584c 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -1,6 +1,5 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
-
# DNS provider information for acme.sh
dns_mgwm_info='mgw-media.de
Site: mgw-media.de
@@ -10,87 +9,66 @@ Options:
MGWM_API_HASH Your API Hash
Issues: github.com/acmesh-official/acme.sh
'
-
# Base URL for the mgw-media.de API
MGWM_API_BASE="https://api.mgw-media.de/record"
-
######## Public functions #####################
# This function is called by acme.sh to add a TXT record.
dns_mgwm_add() {
fulldomain=$1
txtvalue=$2
-
- _info "Using mgw-media.de DNS API for domain $fulldomain"
+ _info "Using mgw-media.de DNS API for domain $fulldomain (add record)"
_debug "fulldomain: $fulldomain"
_debug "txtvalue: $txtvalue"
- # Call private function to load and save environment variables and set up the Basic Auth Header.
- if ! _mgwm_init_env; then
- return 1
- fi
-
- # Construct the API URL for adding a record.
- #_add_url="${MGWM_API_BASE}/add/${fulldomain}/txt/${txtvalue}"
- _add_url="${MGWM_API_BASE}.php?action=add&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
- _debug "Calling MGWM ADD URL: ${_add_url}"
-
- # Execute the HTTP GET request with the Authorization Header.
- # The 5th parameter of _get is where acme.sh expects custom HTTP headers like Authorization.
- response="$(_get "$_add_url")"
- _debug "MGWM add response: $response"
-
- # Check the API response for success. The API returns "OK" on success.
- if [ "$response" = "OK" ]; then
+ # Call the new private function to handle the API request.
+ # The 'add' action, fulldomain, type 'txt' and txtvalue are passed.
+ if _mgwm_perform_api_request "add" "$fulldomain" "txt" "$txtvalue"; then
_info "TXT record for $fulldomain successfully added via MGWM API."
_sleep 10 # Wait briefly for DNS propagation, a common practice in DNS-01 hooks.
return 0
else
- _err "mgwm_add: Failed to add TXT record for $fulldomain. Unexpected API Response: '$response'"
+ # Error message already logged by _mgwm_perform_api_request, but a specific one here helps.
+ _err "mgwm_add: Failed to add TXT record for $fulldomain."
return 1
fi
}
-
# This function is called by acme.sh to remove a TXT record after validation.
dns_mgwm_rm() {
fulldomain=$1
txtvalue=$2 # This txtvalue is now used to identify the specific record to be removed.
-
- _info "Removing TXT record for $fulldomain using mgw-media.de DNS API"
+ _info "Removing TXT record for $fulldomain using mgw-media.de DNS API (remove record)"
_debug "fulldomain: $fulldomain"
_debug "txtvalue: $txtvalue"
- # Call private function to load and save environment variables and set up the Basic Auth Header.
- if ! _mgwm_init_env; then
- return 1
- fi
-
- # Construct the API URL for removing a record.
- # To delete a specific record by its value (as required by ACME v2 for multiple TXT records),
- # the txtvalue must be part of the URL, similar to the add action.
- #_rm_url="${MGWM_API_BASE}/rm/${fulldomain}/txt/${txtvalue}"
- _rm_url="${MGWM_API_BASE}.php?action=rm&fulldomain=${fulldomain}&type=txt&content=${txtvalue}"
- _debug "Calling MGWM RM URL: ${_rm_url}"
-
- # Execute the HTTP GET request with the Authorization Header.
- response="$(_get "$_rm_url")"
- _debug "MGWM rm response: $response"
-
- # Check the API response for success. The API returns "OK" on success.
- if [ "$response" = "OK" ]; then
+ # Call the new private function to handle the API request.
+ # The 'rm' action, fulldomain, type 'txt' and txtvalue are passed.
+ if _mgwm_perform_api_request "rm" "$fulldomain" "txt" "$txtvalue"; then
_info "TXT record for $fulldomain successfully removed via MGWM API."
return 0
else
- _err "mgwm_rm: Failed to remove TXT record for $fulldomain. Unexpected API Response: '$response'"
+ # Error message already logged by _mgwm_perform_api_request, but a specific one here helps.
+ _err "mgwm_rm: Failed to remove TXT record for $fulldomain."
return 1
fi
}
-
#################### Private functions below ##################################
-# _mgwm_init_env() loads the mgw-media.de API credentials (customer number and hash)
-# from environment variables or acme.sh's configuration, saves them, and
-# prepares the global _H1 variable for Basic Authorization header.
-_mgwm_init_env() {
+# _mgwm_perform_api_request() encapsulates the API call logic, including
+# loading credentials, setting the Authorization header, and executing the request.
+# Arguments:
+# $1: action (e.g., "add", "rm")
+# $2: fulldomain
+# $3: type (e.g., "txt")
+# $4: content (the txtvalue)
+_mgwm_perform_api_request() {
+ _action="$1"
+ _fulldomain="$2"
+ _type="$3"
+ _content="$4"
+
+ _debug "Calling _mgwm_perform_api_request for action: $_action, domain: $_fulldomain, type: $_type, content: $_content"
+
+ # --- Start of _mgwm_init_env logic (now embedded here) ---
# Load credentials from environment or acme.sh config
MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
@@ -110,5 +88,25 @@ _mgwm_init_env() {
_credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
export _H1="Authorization: Basic $_credentials"
_debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
- return 0
+ # --- End of _mgwm_init_env logic ---
+
+ # Construct the API URL based on the action and provided parameters.
+ _request_url="${MGWM_API_BASE}.php?action=${_action}&fulldomain=${_fulldomain}&type=${_type}&content=${_content}"
+ _debug "Constructed MGWM API URL for action '$_action': ${_request_url}"
+
+ # Execute the HTTP GET request with the Authorization Header.
+ # The 5th parameter of _get is where acme.sh expects custom HTTP headers like Authorization.
+ response="$(_get "$_request_url")"
+ _debug "MGWM API response for action '$_action': $response"
+
+ # Check the API response for success. The API returns "OK" on success.
+ if [ "$response" = "OK" ]; then
+ _info "MGWM API action '$_action' for record '$_fulldomain' successful."
+ return 0
+ else
+ _err "mgwm_perform_api_request: Failed API action '$_action' for record '$_fulldomain'. Unexpected API Response: '$response'"
+ return 1
+ fi
}
+
+# The original _mgwm_init_env function is now removed as its logic is integrated into _mgwm_perform_api_request.
From 503ca1e9c277721c095aee8b86a6f35c18498ff1 Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 18:34:01 +0100
Subject: [PATCH 118/125] Change MGWM_API_BASE to use IP address
---
dnsapi/dns_mgwm.sh | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index ca48584c..5fa6c216 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -10,7 +10,8 @@ Options:
Issues: github.com/acmesh-official/acme.sh
'
# Base URL for the mgw-media.de API
-MGWM_API_BASE="https://api.mgw-media.de/record"
+#MGWM_API_BASE="https://api.mgw-media.de/record"
+MGWM_API_BASE="http://217.114.220.70/record"
######## Public functions #####################
# This function is called by acme.sh to add a TXT record.
dns_mgwm_add() {
@@ -88,6 +89,7 @@ _mgwm_perform_api_request() {
_credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
export _H1="Authorization: Basic $_credentials"
_debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
+ export _H2="Host: api.mgw-media.de"
# --- End of _mgwm_init_env logic ---
# Construct the API URL based on the action and provided parameters.
From 95da407de86f37d021aa07d44010072c8da01327 Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 19:02:51 +0100
Subject: [PATCH 119/125] Refactor DNS API script to use new request function
---
dnsapi/dns_mgwm.sh | 38 +++++++++++++++++---------------------
1 file changed, 17 insertions(+), 21 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index 5fa6c216..bb0cb650 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -10,9 +10,10 @@ Options:
Issues: github.com/acmesh-official/acme.sh
'
# Base URL for the mgw-media.de API
-#MGWM_API_BASE="https://api.mgw-media.de/record"
-MGWM_API_BASE="http://217.114.220.70/record"
+MGWM_API_BASE="https://api.mgw-media.de/record"
+
######## Public functions #####################
+
# This function is called by acme.sh to add a TXT record.
dns_mgwm_add() {
fulldomain=$1
@@ -23,12 +24,12 @@ dns_mgwm_add() {
# Call the new private function to handle the API request.
# The 'add' action, fulldomain, type 'txt' and txtvalue are passed.
- if _mgwm_perform_api_request "add" "$fulldomain" "txt" "$txtvalue"; then
- _info "TXT record for $fulldomain successfully added via MGWM API."
+ if _mgwm_request "add" "$fulldomain" "txt" "$txtvalue"; then
+ _info "TXT record for $fulldomain successfully added via mgw-media.de API."
_sleep 10 # Wait briefly for DNS propagation, a common practice in DNS-01 hooks.
return 0
else
- # Error message already logged by _mgwm_perform_api_request, but a specific one here helps.
+ # Error message already logged by _mgwm_request, but a specific one here helps.
_err "mgwm_add: Failed to add TXT record for $fulldomain."
return 1
fi
@@ -43,33 +44,32 @@ dns_mgwm_rm() {
# Call the new private function to handle the API request.
# The 'rm' action, fulldomain, type 'txt' and txtvalue are passed.
- if _mgwm_perform_api_request "rm" "$fulldomain" "txt" "$txtvalue"; then
- _info "TXT record for $fulldomain successfully removed via MGWM API."
+ if _mgwm_request "rm" "$fulldomain" "txt" "$txtvalue"; then
+ _info "TXT record for $fulldomain successfully removed via mgw-media.de API."
return 0
else
- # Error message already logged by _mgwm_perform_api_request, but a specific one here helps.
+ # Error message already logged by _mgwm_request, but a specific one here helps.
_err "mgwm_rm: Failed to remove TXT record for $fulldomain."
return 1
fi
}
#################### Private functions below ##################################
-# _mgwm_perform_api_request() encapsulates the API call logic, including
+# _mgwm_request() encapsulates the API call logic, including
# loading credentials, setting the Authorization header, and executing the request.
# Arguments:
# $1: action (e.g., "add", "rm")
# $2: fulldomain
# $3: type (e.g., "txt")
# $4: content (the txtvalue)
-_mgwm_perform_api_request() {
+_mgwm_request() {
_action="$1"
_fulldomain="$2"
_type="$3"
_content="$4"
- _debug "Calling _mgwm_perform_api_request for action: $_action, domain: $_fulldomain, type: $_type, content: $_content"
+ _debug "Calling _mgwm_request for action: $_action, domain: $_fulldomain, type: $_type, content: $_content"
- # --- Start of _mgwm_init_env logic (now embedded here) ---
# Load credentials from environment or acme.sh config
MGWM_CUSTOMER="${MGWM_CUSTOMER:-$(_readaccountconf_mutable MGWM_CUSTOMER)}"
MGWM_API_HASH="${MGWM_API_HASH:-$(_readaccountconf_mutable MGWM_API_HASH)}"
@@ -89,26 +89,22 @@ _mgwm_perform_api_request() {
_credentials="$(printf "%s:%s" "$MGWM_CUSTOMER" "$MGWM_API_HASH" | _base64)"
export _H1="Authorization: Basic $_credentials"
_debug "Set Authorization Header: Basic " # Log debug message without sensitive credentials
- export _H2="Host: api.mgw-media.de"
- # --- End of _mgwm_init_env logic ---
# Construct the API URL based on the action and provided parameters.
- _request_url="${MGWM_API_BASE}.php?action=${_action}&fulldomain=${_fulldomain}&type=${_type}&content=${_content}"
- _debug "Constructed MGWM API URL for action '$_action': ${_request_url}"
+ _request_url="${MGWM_API_BASE}/${_action}/${_fulldomain}/${_type}/${_content}"
+ _debug "Constructed mgw-media.de API URL for action '$_action': ${_request_url}"
# Execute the HTTP GET request with the Authorization Header.
# The 5th parameter of _get is where acme.sh expects custom HTTP headers like Authorization.
response="$(_get "$_request_url")"
- _debug "MGWM API response for action '$_action': $response"
+ _debug "mgw-media.de API response for action '$_action': $response"
# Check the API response for success. The API returns "OK" on success.
if [ "$response" = "OK" ]; then
- _info "MGWM API action '$_action' for record '$_fulldomain' successful."
+ _info "mgw-media.de API action '$_action' for record '$_fulldomain' successful."
return 0
else
- _err "mgwm_perform_api_request: Failed API action '$_action' for record '$_fulldomain'. Unexpected API Response: '$response'"
+ _err "Failed mgw-media.de API action '$_action' for record '$_fulldomain'. Unexpected API Response: '$response'"
return 1
fi
}
-
-# The original _mgwm_init_env function is now removed as its logic is integrated into _mgwm_perform_api_request.
From 0d2955b48d51846bc86ed4259d679569e80dccf5 Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 20:11:40 +0100
Subject: [PATCH 120/125] Update documentation links in dns_mgwm.sh
---
dnsapi/dns_mgwm.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index bb0cb650..28f1342d 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -3,11 +3,11 @@
# DNS provider information for acme.sh
dns_mgwm_info='mgw-media.de
Site: mgw-media.de
-Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mgwm
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_mgwm
Options:
MGWM_CUSTOMER Your customer number
MGWM_API_HASH Your API Hash
-Issues: github.com/acmesh-official/acme.sh
+Issues: github.com/acmesh-official/acme.sh/issues/6669
'
# Base URL for the mgw-media.de API
MGWM_API_BASE="https://api.mgw-media.de/record"
From f142f37064c6d0ea837a77c4a08499a38d0cbeaa Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Sun, 7 Dec 2025 20:11:56 +0100
Subject: [PATCH 121/125] Remove DNS provider information comment
Removed comment about DNS provider information.
---
dnsapi/dns_mgwm.sh | 1 -
1 file changed, 1 deletion(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index 28f1342d..618e90e2 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -1,6 +1,5 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
-# DNS provider information for acme.sh
dns_mgwm_info='mgw-media.de
Site: mgw-media.de
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_mgwm
From 329dab9a67b48b43e31ab3b8ebb7acf511399b0a Mon Sep 17 00:00:00 2001
From: Gilles Filippini
Date: Sun, 16 Nov 2025 15:07:15 +0100
Subject: [PATCH 122/125] Use '_mutable' functions for authentication variables
Fixes #6081.
---
dnsapi/dns_gandi_livedns.sh | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh
index 0516fee9..aaef07bf 100644
--- a/dnsapi/dns_gandi_livedns.sh
+++ b/dnsapi/dns_gandi_livedns.sh
@@ -23,6 +23,8 @@ dns_gandi_livedns_add() {
fulldomain=$1
txtvalue=$2
+ GANDI_LIVEDNS_KEY="${GANDI_LIVEDNS_KEY:-$(_readaccountconf_mutable GANDI_LIVEDNS_KEY)}"
+ GANDI_LIVEDNS_TOKEN="${GANDI_LIVEDNS_TOKEN:-$(_readaccountconf_mutable GANDI_LIVEDNS_TOKEN)}"
if [ -z "$GANDI_LIVEDNS_KEY" ] && [ -z "$GANDI_LIVEDNS_TOKEN" ]; then
_err "No Token or API key (deprecated) specified for Gandi LiveDNS."
_err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively"
@@ -31,11 +33,11 @@ dns_gandi_livedns_add() {
# Keep only one secret in configuration
if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
- _saveaccountconf GANDI_LIVEDNS_TOKEN "$GANDI_LIVEDNS_TOKEN"
- _clearaccountconf GANDI_LIVEDNS_KEY
+ _saveaccountconf_mutable GANDI_LIVEDNS_TOKEN "$GANDI_LIVEDNS_TOKEN"
+ _clearaccountconf_mutable GANDI_LIVEDNS_KEY
elif [ -n "$GANDI_LIVEDNS_KEY" ]; then
- _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
- _clearaccountconf GANDI_LIVEDNS_TOKEN
+ _saveaccountconf_mutable GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+ _clearaccountconf_mutable GANDI_LIVEDNS_TOKEN
fi
_debug "First detect the root zone"
From ad3783170e70653033f88de7ead7c44c990c9f9d Mon Sep 17 00:00:00 2001
From: "Markus G." <29913712+ufozone@users.noreply.github.com>
Date: Mon, 8 Dec 2025 19:31:40 +0100
Subject: [PATCH 123/125] Fix formatting issues in dns_mgwm.sh script
---
dnsapi/dns_mgwm.sh | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/dnsapi/dns_mgwm.sh b/dnsapi/dns_mgwm.sh
index 618e90e2..57679127 100644
--- a/dnsapi/dns_mgwm.sh
+++ b/dnsapi/dns_mgwm.sh
@@ -24,13 +24,13 @@ dns_mgwm_add() {
# Call the new private function to handle the API request.
# The 'add' action, fulldomain, type 'txt' and txtvalue are passed.
if _mgwm_request "add" "$fulldomain" "txt" "$txtvalue"; then
- _info "TXT record for $fulldomain successfully added via mgw-media.de API."
- _sleep 10 # Wait briefly for DNS propagation, a common practice in DNS-01 hooks.
- return 0
+ _info "TXT record for $fulldomain successfully added via mgw-media.de API."
+ _sleep 10 # Wait briefly for DNS propagation, a common practice in DNS-01 hooks.
+ return 0
else
- # Error message already logged by _mgwm_request, but a specific one here helps.
- _err "mgwm_add: Failed to add TXT record for $fulldomain."
- return 1
+ # Error message already logged by _mgwm_request, but a specific one here helps.
+ _err "mgwm_add: Failed to add TXT record for $fulldomain."
+ return 1
fi
}
# This function is called by acme.sh to remove a TXT record after validation.
@@ -44,12 +44,12 @@ dns_mgwm_rm() {
# Call the new private function to handle the API request.
# The 'rm' action, fulldomain, type 'txt' and txtvalue are passed.
if _mgwm_request "rm" "$fulldomain" "txt" "$txtvalue"; then
- _info "TXT record for $fulldomain successfully removed via mgw-media.de API."
- return 0
+ _info "TXT record for $fulldomain successfully removed via mgw-media.de API."
+ return 0
else
- # Error message already logged by _mgwm_request, but a specific one here helps.
- _err "mgwm_rm: Failed to remove TXT record for $fulldomain."
- return 1
+ # Error message already logged by _mgwm_request, but a specific one here helps.
+ _err "mgwm_rm: Failed to remove TXT record for $fulldomain."
+ return 1
fi
}
#################### Private functions below ##################################
@@ -100,10 +100,10 @@ _mgwm_request() {
# Check the API response for success. The API returns "OK" on success.
if [ "$response" = "OK" ]; then
- _info "mgw-media.de API action '$_action' for record '$_fulldomain' successful."
- return 0
+ _info "mgw-media.de API action '$_action' for record '$_fulldomain' successful."
+ return 0
else
- _err "Failed mgw-media.de API action '$_action' for record '$_fulldomain'. Unexpected API Response: '$response'"
- return 1
+ _err "Failed mgw-media.de API action '$_action' for record '$_fulldomain'. Unexpected API Response: '$response'"
+ return 1
fi
}
From e8708a748903f7d52ac07b3281755de5345dacd5 Mon Sep 17 00:00:00 2001
From: neil
Date: Mon, 8 Dec 2025 21:12:49 +0100
Subject: [PATCH 124/125] fix solaris
---
.github/workflows/DNS.yml | 4 +++-
.github/workflows/Solaris.yml | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index be9d3aae..ccce2ff6 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -441,7 +441,9 @@ jobs:
with:
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
copyback: false
- prepare: pkgutil -y -i socat
+ prepare: |
+ pkgutil -U
+ pkgutil -y -i socat
run: |
pkg set-mediator -v -I default@1.1 openssl
export PATH=/usr/gnu/bin:$PATH
diff --git a/.github/workflows/Solaris.yml b/.github/workflows/Solaris.yml
index 95bcd8d1..0ba3d2eb 100644
--- a/.github/workflows/Solaris.yml
+++ b/.github/workflows/Solaris.yml
@@ -66,7 +66,9 @@ jobs:
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
- prepare: pkgutil -y -i socat curl wget
+ prepare: |
+ pkgutil -U
+ pkgutil -y -i socat curl wget
copyback: false
run: |
cd ../acmetest \
From 1413aa332bb48d6cb60a4ef7114fe0be57f3e303 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivan=20Miri=C4=87?=
<1009277+imiric@users.noreply.github.com>
Date: Fri, 5 Dec 2025 15:12:27 +0100
Subject: [PATCH 125/125] fix: update Exoscale DNS script
This updates the Exoscale DNS script to work with v2 of their API.
---
dnsapi/dns_exoscale.sh | 226 ++++++++++++++++++++++++-----------------
1 file changed, 132 insertions(+), 94 deletions(-)
mode change 100755 => 100644 dnsapi/dns_exoscale.sh
diff --git a/dnsapi/dns_exoscale.sh b/dnsapi/dns_exoscale.sh
old mode 100755
new mode 100644
index 6898ce38..ddd526a4
--- a/dnsapi/dns_exoscale.sh
+++ b/dnsapi/dns_exoscale.sh
@@ -8,9 +8,9 @@ Options:
EXOSCALE_SECRET_KEY API Secret key
'
-EXOSCALE_API=https://api.exoscale.com/dns/v1
+EXOSCALE_API="https://api-ch-gva-2.exoscale.com/v2"
-######## Public functions #####################
+######## Public functions ########
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
@@ -18,159 +18,197 @@ dns_exoscale_add() {
fulldomain=$1
txtvalue=$2
- if ! _checkAuth; then
+ _debug "Using Exoscale DNS v2 API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ if ! _check_auth; then
return 1
fi
- _debug "First detect the root zone"
- if ! _get_root "$fulldomain"; then
- _err "invalid domain"
+ root_domain_id=$(_get_root_domain_id "$fulldomain")
+ if [ -z "$root_domain_id" ]; then
+ _err "Unable to determine root domain ID for $fulldomain"
return 1
fi
+ _debug root_domain_id "$root_domain_id"
- _debug _sub_domain "$_sub_domain"
- _debug _domain "$_domain"
+ # Always get the subdomain part first
+ sub_domain=$(_get_sub_domain "$fulldomain" "$root_domain_id")
+ _debug sub_domain "$sub_domain"
- _info "Adding record"
- if _exoscale_rest POST "domains/$_domain_id/records" "{\"record\":{\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}}" "$_domain_token"; then
- if _contains "$response" "$txtvalue"; then
- _info "Added, OK"
- return 0
- fi
+ # Build the record name properly
+ if [ -z "$sub_domain" ]; then
+ record_name="_acme-challenge"
+ else
+ record_name="_acme-challenge.$sub_domain"
fi
- _err "Add txt record error."
- return 1
+ payload=$(printf '{"name":"%s","type":"TXT","content":"%s","ttl":120}' "$record_name" "$txtvalue")
+ _debug payload "$payload"
+
+ response=$(_exoscale_rest POST "/dns-domain/${root_domain_id}/record" "$payload")
+ if _contains "$response" "\"id\""; then
+ _info "TXT record added successfully."
+ return 0
+ else
+ _err "Error adding TXT record: $response"
+ return 1
+ fi
}
-# Usage: fulldomain txtvalue
-# Used to remove the txt record after validation
dns_exoscale_rm() {
fulldomain=$1
- txtvalue=$2
- if ! _checkAuth; then
+ _debug "Using Exoscale DNS v2 API for removal"
+ _debug fulldomain "$fulldomain"
+
+ if ! _check_auth; then
return 1
fi
- _debug "First detect the root zone"
- if ! _get_root "$fulldomain"; then
- _err "invalid domain"
+ root_domain_id=$(_get_root_domain_id "$fulldomain")
+ if [ -z "$root_domain_id" ]; then
+ _err "Unable to determine root domain ID for $fulldomain"
return 1
fi
- _debug _sub_domain "$_sub_domain"
- _debug _domain "$_domain"
-
- _debug "Getting txt records"
- _exoscale_rest GET "domains/${_domain_id}/records?type=TXT&name=$_sub_domain" "" "$_domain_token"
- if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
- _record_id=$(echo "$response" | tr '{' "\n" | grep "\"content\":\"$txtvalue\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
+ record_name="_acme-challenge"
+ sub_domain=$(_get_sub_domain "$fulldomain" "$root_domain_id")
+ if [ -n "$sub_domain" ]; then
+ record_name="_acme-challenge.$sub_domain"
fi
- if [ -z "$_record_id" ]; then
- _err "Can not get record id to remove."
+ record_id=$(_find_record_id "$root_domain_id" "$record_name")
+ if [ -z "$record_id" ]; then
+ _err "TXT record not found for deletion."
return 1
fi
- _debug "Deleting record $_record_id"
-
- if ! _exoscale_rest DELETE "domains/$_domain_id/records/$_record_id" "" "$_domain_token"; then
- _err "Delete record error."
+ response=$(_exoscale_rest DELETE "/dns-domain/$root_domain_id/record/$record_id")
+ if _contains "$response" "\"state\":\"success\""; then
+ _info "TXT record deleted successfully."
+ return 0
+ else
+ _err "Error deleting TXT record: $response"
return 1
fi
-
- return 0
}
-#################### Private functions below ##################################
+######## Private helpers ########
-_checkAuth() {
+_check_auth() {
EXOSCALE_API_KEY="${EXOSCALE_API_KEY:-$(_readaccountconf_mutable EXOSCALE_API_KEY)}"
EXOSCALE_SECRET_KEY="${EXOSCALE_SECRET_KEY:-$(_readaccountconf_mutable EXOSCALE_SECRET_KEY)}"
-
if [ -z "$EXOSCALE_API_KEY" ] || [ -z "$EXOSCALE_SECRET_KEY" ]; then
- EXOSCALE_API_KEY=""
- EXOSCALE_SECRET_KEY=""
- _err "You don't specify Exoscale application key and application secret yet."
- _err "Please create you key and try again."
+ _err "EXOSCALE_API_KEY and EXOSCALE_SECRET_KEY must be set."
return 1
fi
-
_saveaccountconf_mutable EXOSCALE_API_KEY "$EXOSCALE_API_KEY"
_saveaccountconf_mutable EXOSCALE_SECRET_KEY "$EXOSCALE_SECRET_KEY"
-
return 0
}
-#_acme-challenge.www.domain.com
-#returns
-# _sub_domain=_acme-challenge.www
-# _domain=domain.com
-# _domain_id=sdjkglgdfewsdfg
-# _domain_token=sdjkglgdfewsdfg
-_get_root() {
-
- if ! _exoscale_rest GET "domains"; then
- return 1
- fi
-
+_get_root_domain_id() {
domain=$1
- i=2
- p=1
+ i=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
- _debug h "$h"
- if [ -z "$h" ]; then
- #not valid
- return 1
- fi
-
- if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
- _domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
- _domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
- if [ "$_domain_token" ] && [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
- _domain=$h
- return 0
+ candidate=$(printf "%s" "$domain" | cut -d . -f "${i}-100")
+ [ -z "$candidate" ] && return 1
+ _debug "Trying root domain candidate: $candidate"
+ domains=$(_exoscale_rest GET "/dns-domain")
+ # Extract from dns-domains array
+ result=$(echo "$domains" | _egrep_o '"dns-domains":\[.*\]' | _egrep_o '\{"id":"[^"]*","created-at":"[^"]*","unicode-name":"[^"]*"\}' | while read -r item; do
+ name=$(echo "$item" | _egrep_o '"unicode-name":"[^"]*"' | cut -d'"' -f4)
+ id=$(echo "$item" | _egrep_o '"id":"[^"]*"' | cut -d'"' -f4)
+ if [ "$name" = "$candidate" ]; then
+ echo "$id"
+ break
fi
- return 1
+ done)
+ if [ -n "$result" ]; then
+ echo "$result"
+ return 0
fi
- p=$i
i=$(_math "$i" + 1)
done
- return 1
}
-# returns response
+_get_sub_domain() {
+ fulldomain=$1
+ root_id=$2
+ root_info=$(_exoscale_rest GET "/dns-domain/$root_id")
+ _debug root_info "$root_info"
+ root_name=$(echo "$root_info" | _egrep_o "\"unicode-name\":\"[^\"]*\"" | cut -d\" -f4)
+ sub=${fulldomain%%."$root_name"}
+
+ if [ "$sub" = "_acme-challenge" ]; then
+ echo ""
+ else
+ # Remove _acme-challenge. prefix to get the actual subdomain
+ echo "${sub#_acme-challenge.}"
+ fi
+}
+
+_find_record_id() {
+ root_id=$1
+ name=$2
+ records=$(_exoscale_rest GET "/dns-domain/$root_id/record")
+
+ # Convert search name to lowercase for case-insensitive matching
+ name_lower=$(echo "$name" | tr '[:upper:]' '[:lower:]')
+
+ echo "$records" | _egrep_o '\{[^}]*"name":"[^"]*"[^}]*\}' | while read -r record; do
+ record_name=$(echo "$record" | _egrep_o '"name":"[^"]*"' | cut -d'"' -f4)
+ record_name_lower=$(echo "$record_name" | tr '[:upper:]' '[:lower:]')
+ if [ "$record_name_lower" = "$name_lower" ]; then
+ echo "$record" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d'"' -f4
+ break
+ fi
+ done
+}
+
+_exoscale_sign() {
+ k=$1
+ shift
+ hex_key=$(printf %b "$k" | _hex_dump | tr -d ' ')
+ printf %s "$@" | _hmac sha256 "$hex_key"
+}
+
_exoscale_rest() {
method=$1
- path="$2"
- data="$3"
- token="$4"
- request_url="$EXOSCALE_API/$path"
- _debug "$path"
+ path=$2
+ data=$3
- export _H1="Accept: application/json"
+ url="${EXOSCALE_API}${path}"
+ expiration=$(_math "$(date +%s)" + 300) # 5m from now
- if [ "$token" ]; then
- export _H2="X-DNS-Domain-Token: $token"
- else
- export _H2="X-DNS-Token: $EXOSCALE_API_KEY:$EXOSCALE_SECRET_KEY"
- fi
+ # Build the message with the actual body or empty line
+ message=$(printf "%s %s\n%s\n\n\n%s" "$method" "/v2$path" "$data" "$expiration")
+ signature=$(_exoscale_sign "$EXOSCALE_SECRET_KEY" "$message" | _base64)
+ auth="EXO2-HMAC-SHA256 credential=${EXOSCALE_API_KEY},expires=${expiration},signature=${signature}"
+
+ _debug "API request: $method $url"
+ _debug "Signed message: [$message]"
+ _debug "Authorization header: [$auth]"
+
+ export _H1="Accept: application/json"
+ export _H2="Authorization: ${auth}"
if [ "$data" ] || [ "$method" = "DELETE" ]; then
export _H3="Content-Type: application/json"
_debug data "$data"
- response="$(_post "$data" "$request_url" "" "$method")"
+ response="$(_post "$data" "$url" "" "$method")"
else
- response="$(_get "$request_url" "" "" "$method")"
+ response="$(_get "$url" "" "" "$method")"
fi
- if [ "$?" != "0" ]; then
- _err "error $request_url"
+ # shellcheck disable=SC2181
+ if [ "$?" -ne 0 ]; then
+ _err "error $url"
return 1
fi
_debug2 response "$response"
+ echo "$response"
return 0
}