mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2025-12-24 20:22:08 +08:00
Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dba4be8065 | ||
|
|
a670c07caf | ||
|
|
09cc2bdfa5 | ||
|
|
cc897cab4c | ||
|
|
7ba9597928 | ||
|
|
d2d862420e | ||
|
|
fd6a14de8a | ||
|
|
0eb40c6ce6 | ||
|
|
e321b3c75c | ||
|
|
03d8d3bc1b | ||
|
|
f85de2b0d3 | ||
|
|
5fb42b7339 | ||
|
|
1b2630dc0d | ||
|
|
3fb4c313ec | ||
|
|
65892453be | ||
|
|
cba0ff8321 | ||
|
|
b6523c2301 | ||
|
|
e92d0a7492 | ||
|
|
0e5aab346f | ||
|
|
a5ad15be02 | ||
|
|
27ebf09c5c | ||
|
|
1c65c04b54 | ||
|
|
987882ea37 | ||
|
|
e3b1bccb6a | ||
|
|
85ff92170b | ||
|
|
7ac8c6c75b | ||
|
|
6004e7f5cd | ||
|
|
4a7e5d0720 | ||
|
|
b4042d5ccb | ||
|
|
1413aa332b | ||
|
|
37cf431e80 | ||
|
|
0c9d2dafe3 | ||
|
|
e8708a7489 | ||
|
|
79592c700f | ||
|
|
ad3783170e | ||
|
|
329dab9a67 | ||
|
|
f142f37064 | ||
|
|
0d2955b48d | ||
|
|
95da407de8 | ||
|
|
503ca1e9c2 | ||
|
|
d8722c46d9 | ||
|
|
546c2d47d5 | ||
|
|
2ba615555c | ||
|
|
e94c6be4a1 | ||
|
|
11eaad1fa7 | ||
|
|
daf7f7c268 | ||
|
|
4965c704d7 | ||
|
|
b7fe7a40ba | ||
|
|
3b2c2b16b2 | ||
|
|
45cb36f6d9 | ||
|
|
70e965fd55 | ||
|
|
bee01c938a | ||
|
|
5cbae50ec1 | ||
|
|
51b4fa0080 | ||
|
|
64a6ea68fa | ||
|
|
d97b4477b2 | ||
|
|
b8e394e76a | ||
|
|
671d542898 | ||
|
|
9980ad0fef | ||
|
|
004deaeea1 | ||
|
|
36b8ca2bc0 | ||
|
|
890ab4a7bb | ||
|
|
490b9e2d09 | ||
|
|
ca35e8c118 | ||
|
|
eeb91de6a3 | ||
|
|
657b7195d6 | ||
|
|
054a73f297 | ||
|
|
5c6d8aacbe | ||
|
|
ac0df6bc88 | ||
|
|
f007b46c1b | ||
|
|
c5566eafeb | ||
|
|
75ee17aeeb | ||
|
|
ad2cb507a4 | ||
|
|
dca23a98f1 | ||
|
|
8cb9713493 | ||
|
|
ef76831d37 | ||
|
|
c4671272c0 | ||
|
|
86f2584162 | ||
|
|
020c52e583 | ||
|
|
eda8614754 | ||
|
|
ca08ce4262 | ||
|
|
eb22a84db4 | ||
|
|
d66dd99621 | ||
|
|
5a085f2514 | ||
|
|
e79ee7fb74 | ||
|
|
718ff3a5f5 | ||
|
|
a2e52dadb9 | ||
|
|
ca4cb018d0 | ||
|
|
c2762d3b6f | ||
|
|
947e872850 | ||
|
|
3baa5e145f | ||
|
|
67fd35127c | ||
|
|
c421e2ddfc | ||
|
|
42febe97b5 | ||
|
|
74ca0fb763 | ||
|
|
13631ea2de | ||
|
|
a1eee5923a | ||
|
|
af92bbca2a | ||
|
|
c9287071e3 | ||
|
|
292026288a | ||
|
|
1f056998f3 | ||
|
|
4d7cb7de5f | ||
|
|
67855f21d4 | ||
|
|
8484565e95 | ||
|
|
f7d8abe8ea | ||
|
|
8ca90297e7 | ||
|
|
e08f9080c2 | ||
|
|
419738fbd5 | ||
|
|
7f1423dd6f | ||
|
|
9f09dcd18c | ||
|
|
91081ade3c | ||
|
|
4d933c23a8 | ||
|
|
f29bfd995d | ||
|
|
30d5d1aea9 | ||
|
|
7a0450a7f4 | ||
|
|
5bb09f469f | ||
|
|
90e9d8ff52 | ||
|
|
59a43ce5d1 | ||
|
|
5bc01aa251 | ||
|
|
9eeb979c7b | ||
|
|
eabd7592fe | ||
|
|
e089a3d8a1 | ||
|
|
7560375502 | ||
|
|
1f77b89266 | ||
|
|
7c610124d9 | ||
|
|
a0c5ef4e6f | ||
|
|
218934e767 | ||
|
|
c6a9825c0a |
4
.github/workflows/DNS.yml
vendored
4
.github/workflows/DNS.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/Solaris.yml
vendored
4
.github/workflows/Solaris.yml
vendored
@@ -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 \
|
||||
|
||||
5
.github/workflows/wiki-monitor.yml
vendored
5
.github/workflows/wiki-monitor.yml
vendored
@@ -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:"
|
||||
|
||||
6
acme.sh
6
acme.sh
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
VER=3.1.2
|
||||
VER=3.1.3
|
||||
|
||||
PROJECT_NAME="acme.sh"
|
||||
|
||||
@@ -1031,7 +1031,7 @@ _digest() {
|
||||
|
||||
outputhex="$2"
|
||||
|
||||
if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
|
||||
if [ "$alg" = "sha3-256" ] || [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
|
||||
if [ "$outputhex" ]; then
|
||||
${ACME_OPENSSL_BIN:-openssl} dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
|
||||
else
|
||||
@@ -5840,7 +5840,7 @@ list() {
|
||||
if [ -z "$_domain" ]; then
|
||||
printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Profile${_sep}CA${_sep}Created${_sep}Renew"
|
||||
fi
|
||||
for di in "${CERT_HOME}"/*.*/; do
|
||||
for di in "${CERT_HOME}"/{*.*,*:*}/; do
|
||||
d=$(basename "$di")
|
||||
_debug d "$d"
|
||||
(
|
||||
|
||||
@@ -83,6 +83,6 @@ _set_cdn_domain_ssl_certificate_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=2018-05-10'
|
||||
}
|
||||
|
||||
@@ -83,6 +83,6 @@ _set_dcdn_domain_ssl_certificate_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=2018-01-15'
|
||||
}
|
||||
|
||||
@@ -97,12 +97,13 @@ _ali_rest() {
|
||||
}
|
||||
|
||||
_ali_nonce() {
|
||||
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
|
||||
#Not so good...
|
||||
date +"%s%N" | sed 's/%N//g'
|
||||
if [ "$ACME_OPENSSL_BIN" ]; then
|
||||
"$ACME_OPENSSL_BIN" rand -hex 16 2>/dev/null && return 0
|
||||
fi
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -150,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'
|
||||
}
|
||||
@@ -166,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'
|
||||
@@ -182,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'
|
||||
}
|
||||
|
||||
@@ -196,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'
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
139
dnsapi/dns_efficientip.sh
Executable file
139
dnsapi/dns_efficientip.sh
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_efficientip_info='efficientip.com
|
||||
Site: https://efficientip.com/
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_efficientip
|
||||
Options:
|
||||
EfficientIP_Creds HTTP Basic Authentication credentials. E.g. "username:password"
|
||||
EfficientIP_Server EfficientIP SOLIDserver Management IP address or FQDN.
|
||||
EfficientIP_DNS_Name Name of the DNS smart or server hosting the zone. Optional.
|
||||
EfficientIP_View Name of the DNS view hosting the zone. Optional.
|
||||
OptionsAlt:
|
||||
EfficientIP_Token_Key Alternative API token key, prefered over basic authentication.
|
||||
EfficientIP_Token_Secret Alternative API token secret, required when using a token key.
|
||||
EfficientIP_Server EfficientIP SOLIDserver Management IP address or FQDN.
|
||||
EfficientIP_DNS_Name Name of the DNS smart or server hosting the zone. Optional.
|
||||
EfficientIP_View Name of the DNS view hosting the zone. Optional.
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/6325
|
||||
Author: EfficientIP-Labs <contact@efficientip.com>
|
||||
'
|
||||
|
||||
dns_efficientip_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
_info "Using EfficientIP API"
|
||||
_debug fulldomain "$fulldomain"
|
||||
_debug txtvalue "$txtvalue"
|
||||
|
||||
if { [ -z "${EfficientIP_Creds}" ] && { [ -z "${EfficientIP_Token_Key}" ] || [ -z "${EfficientIP_Token_Secret}" ]; }; } || [ -z "${EfficientIP_Server}" ]; then
|
||||
EfficientIP_Creds=""
|
||||
EfficientIP_Token_Key=""
|
||||
EfficientIP_Token_Secret=""
|
||||
EfficientIP_Server=""
|
||||
_err "You didn't specify any EfficientIP credentials or token or server (EfficientIP_Creds; EfficientIP_Token_Key; EfficientIP_Token_Secret; EfficientIP_Server)."
|
||||
_err "Please set them via EXPORT EfficientIP_Creds=username:password or EXPORT EfficientIP_server=ip/hostname"
|
||||
_err "or if you want to use Token instead EXPORT EfficientIP_Token_Key=yourkey"
|
||||
_err "and EXPORT EfficientIP_Token_Secret=yoursecret"
|
||||
_err "then try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -z "${EfficientIP_DNS_Name}" ]; then
|
||||
EfficientIP_DNS_Name=""
|
||||
fi
|
||||
|
||||
EfficientIP_DNSNameEncoded=$(printf "%b" "${EfficientIP_DNS_Name}" | _url_encode)
|
||||
|
||||
if [ -z "${EfficientIP_View}" ]; then
|
||||
EfficientIP_View=""
|
||||
fi
|
||||
|
||||
EfficientIP_ViewEncoded=$(printf "%b" "${EfficientIP_View}" | _url_encode)
|
||||
|
||||
_saveaccountconf EfficientIP_Creds "${EfficientIP_Creds}"
|
||||
_saveaccountconf EfficientIP_Token_Key "${EfficientIP_Token_Key}"
|
||||
_saveaccountconf EfficientIP_Token_Secret "${EfficientIP_Token_Secret}"
|
||||
_saveaccountconf EfficientIP_Server "${EfficientIP_Server}"
|
||||
_saveaccountconf EfficientIP_DNS_Name "${EfficientIP_DNS_Name}"
|
||||
_saveaccountconf EfficientIP_View "${EfficientIP_View}"
|
||||
|
||||
export _H1="Accept-Language:en-US"
|
||||
baseurlnObject="https://${EfficientIP_Server}/rest/dns_rr_add?rr_type=TXT&rr_ttl=300&rr_name=${fulldomain}&rr_value1=${txtvalue}"
|
||||
|
||||
if [ "${EfficientIP_DNSNameEncoded}" != "" ]; then
|
||||
baseurlnObject="${baseurlnObject}&dns_name=${EfficientIP_DNSNameEncoded}"
|
||||
fi
|
||||
|
||||
if [ "${EfficientIP_ViewEncoded}" != "" ]; then
|
||||
baseurlnObject="${baseurlnObject}&dnsview_name=${EfficientIP_ViewEncoded}"
|
||||
fi
|
||||
|
||||
if [ -z "${EfficientIP_Token_Secret}" ] || [ -z "${EfficientIP_Token_Key}" ]; then
|
||||
EfficientIP_CredsEncoded=$(printf "%b" "${EfficientIP_Creds}" | _base64)
|
||||
export _H2="Authorization: Basic ${EfficientIP_CredsEncoded}"
|
||||
else
|
||||
TS=$(date +%s)
|
||||
Sig=$(printf "%b\n$TS\nPOST\n$baseurlnObject" "${EfficientIP_Token_Secret}" | _digest sha3-256 hex)
|
||||
EfficientIP_CredsEncoded=$(printf "%b:%b" "${EfficientIP_Token_Key}" "$Sig")
|
||||
export _H2="Authorization: SDS ${EfficientIP_CredsEncoded}"
|
||||
export _H3="X-SDS-TS: ${TS}"
|
||||
fi
|
||||
|
||||
result="$(_post "" "${baseurlnObject}" "" "POST")"
|
||||
|
||||
if [ "$(echo "${result}" | _egrep_o "ret_oid")" ]; then
|
||||
_info "DNS record successfully created"
|
||||
return 0
|
||||
else
|
||||
_err "Error creating DNS record"
|
||||
_err "${result}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
dns_efficientip_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
_info "Using EfficientIP API"
|
||||
_debug fulldomain "${fulldomain}"
|
||||
_debug txtvalue "${txtvalue}"
|
||||
|
||||
EfficientIP_ViewEncoded=$(printf "%b" "${EfficientIP_View}" | _url_encode)
|
||||
EfficientIP_DNSNameEncoded=$(printf "%b" "${EfficientIP_DNS_Name}" | _url_encode)
|
||||
EfficientIP_CredsEncoded=$(printf "%b" "${EfficientIP_Creds}" | _base64)
|
||||
|
||||
export _H1="Accept-Language:en-US"
|
||||
|
||||
baseurlnObject="https://${EfficientIP_Server}/rest/dns_rr_delete?rr_type=TXT&rr_name=$fulldomain&rr_value1=$txtvalue"
|
||||
if [ "${EfficientIP_DNSNameEncoded}" != "" ]; then
|
||||
baseurlnObject="${baseurlnObject}&dns_name=${EfficientIP_DNSNameEncoded}"
|
||||
fi
|
||||
|
||||
if [ "${EfficientIP_ViewEncoded}" != "" ]; then
|
||||
baseurlnObject="${baseurlnObject}&dnsview_name=${EfficientIP_ViewEncoded}"
|
||||
fi
|
||||
|
||||
if [ -z "$EfficientIP_Token_Secret" ] || [ -z "$EfficientIP_Token_Key" ]; then
|
||||
EfficientIP_CredsEncoded=$(printf "%b" "${EfficientIP_Creds}" | _base64)
|
||||
export _H2="Authorization: Basic $EfficientIP_CredsEncoded"
|
||||
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")
|
||||
export _H2="Authorization: SDS ${EfficientIP_CredsEncoded}"
|
||||
export _H3="X-SDS-TS: $TS"
|
||||
fi
|
||||
|
||||
result="$(_post "" "${baseurlnObject}" "" "DELETE")"
|
||||
|
||||
if [ "$(echo "${result}" | _egrep_o "ret_oid")" ]; then
|
||||
_info "DNS Record successfully deleted"
|
||||
return 0
|
||||
else
|
||||
_err "Error deleting DNS record"
|
||||
_err "${result}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
226
dnsapi/dns_exoscale.sh
Executable file → Normal file
226
dnsapi/dns_exoscale.sh
Executable file → Normal file
@@ -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
|
||||
|
||||
url="${EXOSCALE_API}${path}"
|
||||
expiration=$(_math "$(date +%s)" + 300) # 5m from now
|
||||
|
||||
# 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"
|
||||
|
||||
if [ "$token" ]; then
|
||||
export _H2="X-DNS-Domain-Token: $token"
|
||||
else
|
||||
export _H2="X-DNS-Token: $EXOSCALE_API_KEY:$EXOSCALE_SECRET_KEY"
|
||||
fi
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
501
dnsapi/dns_hostup.sh
Normal file
501
dnsapi/dns_hostup.sh
Normal file
@@ -0,0 +1,501 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034,SC2154
|
||||
|
||||
dns_hostup_info='HostUp DNS
|
||||
Site: hostup.se
|
||||
Docs: https://developer.hostup.se/
|
||||
Options:
|
||||
HOSTUP_API_KEY Required. HostUp API key with read:dns + write:dns + read:domains scopes.
|
||||
HOSTUP_API_BASE Optional. Override API base URL (default: https://cloud.hostup.se/api).
|
||||
HOSTUP_TTL Optional. TTL for TXT records (default: 60 seconds).
|
||||
HOSTUP_ZONE_ID Optional. Force a specific zone ID (skip auto-detection).
|
||||
Author: HostUp (https://cloud.hostup.se/contact/en)
|
||||
'
|
||||
|
||||
HOSTUP_API_BASE_DEFAULT="https://cloud.hostup.se/api"
|
||||
HOSTUP_DEFAULT_TTL=60
|
||||
|
||||
# Public: add TXT record
|
||||
# Usage: dns_hostup_add _acme-challenge.example.com "txt-value"
|
||||
dns_hostup_add() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_info "Using HostUp DNS API"
|
||||
|
||||
if ! _hostup_init; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _hostup_detect_zone "$fulldomain"; then
|
||||
_err "Unable to determine HostUp zone for $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
record_name="$(_hostup_record_name "$fulldomain" "$HOSTUP_ZONE_DOMAIN")"
|
||||
record_name="$(_hostup_sanitize_name "$record_name")"
|
||||
record_value="$(_hostup_json_escape "$txtvalue")"
|
||||
|
||||
ttl="${HOSTUP_TTL:-$HOSTUP_DEFAULT_TTL}"
|
||||
|
||||
_debug "zone_id" "$HOSTUP_ZONE_ID"
|
||||
_debug "zone_domain" "$HOSTUP_ZONE_DOMAIN"
|
||||
_debug "record_name" "$record_name"
|
||||
_debug "ttl" "$ttl"
|
||||
|
||||
request_body="{\"name\":\"$record_name\",\"type\":\"TXT\",\"value\":\"$record_value\",\"ttl\":$ttl}"
|
||||
|
||||
if ! _hostup_rest "POST" "/dns/zones/$HOSTUP_ZONE_ID/records" "$request_body"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$_hostup_response" '"success":true'; then
|
||||
_err "HostUp DNS API: failed to create TXT record for $fulldomain"
|
||||
_debug2 "_hostup_response" "$_hostup_response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
record_id="$(_hostup_extract_record_id "$_hostup_response")"
|
||||
if [ -n "$record_id" ]; then
|
||||
_hostup_save_record_id "$HOSTUP_ZONE_ID" "$fulldomain" "$record_id"
|
||||
_debug "hostup_saved_record_id" "$record_id"
|
||||
fi
|
||||
|
||||
_info "Added TXT record for $fulldomain"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Public: remove TXT record
|
||||
# Usage: dns_hostup_rm _acme-challenge.example.com "txt-value"
|
||||
dns_hostup_rm() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_info "Using HostUp DNS API"
|
||||
|
||||
if ! _hostup_init; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _hostup_detect_zone "$fulldomain"; then
|
||||
_err "Unable to determine HostUp zone for $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
record_name_fqdn="$(_hostup_fqdn "$fulldomain")"
|
||||
record_value="$txtvalue"
|
||||
|
||||
record_id_cached="$(_hostup_get_saved_record_id "$HOSTUP_ZONE_ID" "$fulldomain")"
|
||||
if [ -n "$record_id_cached" ]; then
|
||||
_debug "hostup_record_id_cached" "$record_id_cached"
|
||||
if _hostup_delete_record_by_id "$HOSTUP_ZONE_ID" "$record_id_cached"; then
|
||||
_info "Deleted TXT record $record_id_cached"
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain"
|
||||
HOSTUP_ZONE_ID=""
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! _hostup_find_record "$HOSTUP_ZONE_ID" "$record_name_fqdn" "$record_value"; then
|
||||
_info "TXT record not found for $record_name_fqdn. Skipping removal."
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_debug "Deleting record" "$HOSTUP_RECORD_ID"
|
||||
|
||||
if ! _hostup_delete_record_by_id "$HOSTUP_ZONE_ID" "$HOSTUP_RECORD_ID"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Deleted TXT record $HOSTUP_RECORD_ID"
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain"
|
||||
HOSTUP_ZONE_ID=""
|
||||
return 0
|
||||
}
|
||||
|
||||
##########################
|
||||
# Private helper methods #
|
||||
##########################
|
||||
|
||||
_hostup_init() {
|
||||
HOSTUP_API_KEY="${HOSTUP_API_KEY:-$(_readaccountconf_mutable HOSTUP_API_KEY)}"
|
||||
HOSTUP_API_BASE="${HOSTUP_API_BASE:-$(_readaccountconf_mutable HOSTUP_API_BASE)}"
|
||||
HOSTUP_TTL="${HOSTUP_TTL:-$(_readaccountconf_mutable HOSTUP_TTL)}"
|
||||
HOSTUP_ZONE_ID="${HOSTUP_ZONE_ID:-$(_readaccountconf_mutable HOSTUP_ZONE_ID)}"
|
||||
|
||||
if [ -z "$HOSTUP_API_BASE" ]; then
|
||||
HOSTUP_API_BASE="$HOSTUP_API_BASE_DEFAULT"
|
||||
fi
|
||||
|
||||
if [ -z "$HOSTUP_API_KEY" ]; then
|
||||
HOSTUP_API_KEY=""
|
||||
_err "HOSTUP_API_KEY is not set."
|
||||
_err "Please export your HostUp API key with read:dns and write:dns scopes."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable HOSTUP_API_KEY "$HOSTUP_API_KEY"
|
||||
_saveaccountconf_mutable HOSTUP_API_BASE "$HOSTUP_API_BASE"
|
||||
|
||||
if [ -n "$HOSTUP_TTL" ]; then
|
||||
_saveaccountconf_mutable HOSTUP_TTL "$HOSTUP_TTL"
|
||||
fi
|
||||
|
||||
if [ -n "$HOSTUP_ZONE_ID" ]; then
|
||||
_saveaccountconf_mutable HOSTUP_ZONE_ID "$HOSTUP_ZONE_ID"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_hostup_detect_zone() {
|
||||
fulldomain="$1"
|
||||
|
||||
if [ -n "$HOSTUP_ZONE_ID" ] && [ -n "$HOSTUP_ZONE_DOMAIN" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
HOSTUP_ZONE_DOMAIN=""
|
||||
_debug "hostup_full_domain" "$fulldomain"
|
||||
|
||||
if [ -n "$HOSTUP_ZONE_ID" ] && [ -z "$HOSTUP_ZONE_DOMAIN" ]; then
|
||||
# Attempt to fetch domain name for provided zone ID
|
||||
if _hostup_fetch_zone_details "$HOSTUP_ZONE_ID"; then
|
||||
return 0
|
||||
fi
|
||||
HOSTUP_ZONE_ID=""
|
||||
fi
|
||||
|
||||
if ! _hostup_load_zones; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_domain_candidate="$(printf "%s" "$fulldomain" | _lower_case)"
|
||||
_debug "hostup_initial_candidate" "$_domain_candidate"
|
||||
|
||||
while [ -n "$_domain_candidate" ]; do
|
||||
_debug "hostup_zone_candidate" "$_domain_candidate"
|
||||
if _hostup_lookup_zone "$_domain_candidate"; then
|
||||
HOSTUP_ZONE_DOMAIN="$_lookup_zone_domain"
|
||||
HOSTUP_ZONE_ID="$_lookup_zone_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
case "$_domain_candidate" in
|
||||
*.*) ;;
|
||||
*) break ;;
|
||||
esac
|
||||
|
||||
_domain_candidate="${_domain_candidate#*.}"
|
||||
done
|
||||
|
||||
HOSTUP_ZONE_ID=""
|
||||
return 1
|
||||
}
|
||||
|
||||
_hostup_record_name() {
|
||||
fulldomain="$1"
|
||||
zonedomain="$2"
|
||||
|
||||
# Remove trailing dot, if any
|
||||
fulldomain="${fulldomain%.}"
|
||||
zonedomain="${zonedomain%.}"
|
||||
|
||||
if [ "$fulldomain" = "$zonedomain" ]; then
|
||||
printf "%s" "@"
|
||||
return 0
|
||||
fi
|
||||
|
||||
suffix=".$zonedomain"
|
||||
case "$fulldomain" in
|
||||
*"$suffix")
|
||||
printf "%s" "${fulldomain%"$suffix"}"
|
||||
;;
|
||||
*)
|
||||
# Domain not within zone, fall back to full host
|
||||
printf "%s" "$fulldomain"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_hostup_sanitize_name() {
|
||||
name="$1"
|
||||
|
||||
if [ -z "$name" ] || [ "$name" = "." ]; then
|
||||
printf "%s" "@"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Remove any trailing dot
|
||||
name="${name%.}"
|
||||
printf "%s" "$name"
|
||||
}
|
||||
|
||||
_hostup_fqdn() {
|
||||
domain="$1"
|
||||
printf "%s" "${domain%.}"
|
||||
}
|
||||
|
||||
_hostup_fetch_zone_details() {
|
||||
zone_id="$1"
|
||||
|
||||
if ! _hostup_rest "GET" "/dns/zones/$zone_id/records" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
zonedomain="$(printf "%s" "$_hostup_response" | _egrep_o '"domain":"[^"]*"' | sed -n '1p' | cut -d ':' -f 2 | tr -d '"')"
|
||||
if [ -n "$zonedomain" ]; then
|
||||
HOSTUP_ZONE_DOMAIN="$zonedomain"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_hostup_load_zones() {
|
||||
if ! _hostup_rest "GET" "/dns/zones" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
HOSTUP_ZONES_CACHE=""
|
||||
data="$(printf "%s" "$_hostup_response" | tr '{' '\n')"
|
||||
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
*'"domain_id"'*'"domain"'*)
|
||||
zone_id="$(printf "%s" "$line" | _hostup_json_extract "domain_id")"
|
||||
zone_domain="$(printf "%s" "$line" | _hostup_json_extract "domain")"
|
||||
if [ -n "$zone_id" ] && [ -n "$zone_domain" ]; then
|
||||
HOSTUP_ZONES_CACHE="${HOSTUP_ZONES_CACHE}${zone_domain}|${zone_id}
|
||||
"
|
||||
_debug "hostup_zone_loaded" "$zone_domain|$zone_id"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done <<EOF
|
||||
$data
|
||||
EOF
|
||||
|
||||
if [ -z "$HOSTUP_ZONES_CACHE" ]; then
|
||||
_err "HostUp DNS API: no zones returned for the current API key."
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_hostup_lookup_zone() {
|
||||
lookup_domain="$1"
|
||||
_lookup_zone_id=""
|
||||
_lookup_zone_domain=""
|
||||
|
||||
while IFS='|' read -r domain zone_id; do
|
||||
[ -z "$domain" ] && continue
|
||||
if [ "$domain" = "$lookup_domain" ]; then
|
||||
_lookup_zone_domain="$domain"
|
||||
_lookup_zone_id="$zone_id"
|
||||
HOSTUP_ZONE_DOMAIN="$domain"
|
||||
HOSTUP_ZONE_ID="$zone_id"
|
||||
return 0
|
||||
fi
|
||||
done <<EOF
|
||||
$HOSTUP_ZONES_CACHE
|
||||
EOF
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_hostup_find_record() {
|
||||
zone_id="$1"
|
||||
fqdn="$2"
|
||||
txtvalue="$3"
|
||||
|
||||
if ! _hostup_rest "GET" "/dns/zones/$zone_id/records" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
HOSTUP_RECORD_ID=""
|
||||
records="$(printf "%s" "$_hostup_response" | tr '{' '\n')"
|
||||
|
||||
while IFS= read -r line; do
|
||||
# Normalize line to make TXT value matching reliable
|
||||
line_clean="$(printf "%s" "$line" | tr -d '\r\n')"
|
||||
line_value_clean="$(printf "%s" "$line_clean" | sed 's/\\"//g')"
|
||||
|
||||
case "$line_clean" in
|
||||
*'"type":"TXT"'*'"name"'*'"value"'*)
|
||||
name_value="$(_hostup_json_extract "name" "$line_clean")"
|
||||
record_value="$(_hostup_json_extract "value" "$line_value_clean")"
|
||||
|
||||
_debug "hostup_record_raw" "$record_value"
|
||||
if [ "${record_value#\"}" != "$record_value" ] && [ "${record_value%\"}" != "$record_value" ]; then
|
||||
record_value="${record_value#\"}"
|
||||
record_value="${record_value%\"}"
|
||||
fi
|
||||
if [ "${record_value#\'}" != "$record_value" ] && [ "${record_value%\'}" != "$record_value" ]; then
|
||||
record_value="${record_value#\'}"
|
||||
record_value="${record_value%\'}"
|
||||
fi
|
||||
record_value="$(printf "%s" "$record_value" | tr -d '\r\n')"
|
||||
_debug "hostup_record_value" "$record_value"
|
||||
|
||||
if [ "$name_value" = "$fqdn" ] && [ "$record_value" = "$txtvalue" ]; then
|
||||
record_id="$(_hostup_json_extract "id" "$line_clean")"
|
||||
if [ -n "$record_id" ]; then
|
||||
HOSTUP_RECORD_ID="$record_id"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done <<EOF
|
||||
$records
|
||||
EOF
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_hostup_json_extract() {
|
||||
key="$1"
|
||||
input="${2:-$line}"
|
||||
|
||||
# First try to extract quoted values (strings)
|
||||
quoted_match="$(printf "%s" "$input" | _egrep_o "\"$key\":\"[^\"]*\"" | _head_n 1)"
|
||||
if [ -n "$quoted_match" ]; then
|
||||
printf "%s" "$quoted_match" |
|
||||
cut -d : -f2- |
|
||||
sed 's/^"//' |
|
||||
sed 's/"$//' |
|
||||
sed 's/\\"/"/g'
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback for unquoted values (e.g., numeric IDs)
|
||||
unquoted_match="$(printf "%s" "$input" | _egrep_o "\"$key\":[^,}]*" | _head_n 1)"
|
||||
if [ -n "$unquoted_match" ]; then
|
||||
printf "%s" "$unquoted_match" |
|
||||
cut -d : -f2- |
|
||||
tr -d '", ' |
|
||||
tr -d '\r\n'
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_hostup_json_escape() {
|
||||
printf "%s" "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
|
||||
}
|
||||
|
||||
_hostup_record_key() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
safe_zone="$(printf "%s" "$zone_id" | sed 's/[^A-Za-z0-9]/_/g')"
|
||||
safe_domain="$(printf "%s" "$domain" | _lower_case | sed 's/[^a-z0-9]/_/g')"
|
||||
printf "%s_%s" "$safe_zone" "$safe_domain"
|
||||
}
|
||||
|
||||
_hostup_save_record_id() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
record_id="$3"
|
||||
key="$(_hostup_record_key "$zone_id" "$domain")"
|
||||
_saveaccountconf_mutable "HOSTUP_RECORD_$key" "$record_id"
|
||||
}
|
||||
|
||||
_hostup_get_saved_record_id() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
key="$(_hostup_record_key "$zone_id" "$domain")"
|
||||
_readaccountconf_mutable "HOSTUP_RECORD_$key"
|
||||
}
|
||||
|
||||
_hostup_clear_record_id() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
key="$(_hostup_record_key "$zone_id" "$domain")"
|
||||
_clearaccountconf_mutable "HOSTUP_RECORD_$key"
|
||||
}
|
||||
|
||||
_hostup_extract_record_id() {
|
||||
record_id="$(_hostup_json_extract "id" "$1")"
|
||||
if [ -n "$record_id" ]; then
|
||||
printf "%s" "$record_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "%s" "$1" | _egrep_o '"id":[0-9]+' | _head_n 1 | cut -d: -f2
|
||||
}
|
||||
|
||||
_hostup_delete_record_by_id() {
|
||||
zone_id="$1"
|
||||
record_id="$2"
|
||||
|
||||
if ! _hostup_rest "DELETE" "/dns/zones/$zone_id/records/$record_id" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$_hostup_response" '"success":true'; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_hostup_rest() {
|
||||
method="$1"
|
||||
route="$2"
|
||||
data="$3"
|
||||
|
||||
_hostup_response=""
|
||||
|
||||
export _H1="Authorization: Bearer $HOSTUP_API_KEY"
|
||||
export _H2="Content-Type: application/json"
|
||||
export _H3="Accept: application/json"
|
||||
|
||||
if [ "$method" = "GET" ]; then
|
||||
_hostup_response="$(_get "$HOSTUP_API_BASE$route")"
|
||||
else
|
||||
_hostup_response="$(_post "$data" "$HOSTUP_API_BASE$route" "" "$method" "application/json")"
|
||||
fi
|
||||
|
||||
ret="$?"
|
||||
|
||||
unset _H1
|
||||
unset _H2
|
||||
unset _H3
|
||||
|
||||
if [ "$ret" != "0" ]; then
|
||||
_err "HTTP request failed for $route"
|
||||
return 1
|
||||
fi
|
||||
|
||||
http_status="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
|
||||
_debug2 "HTTP status" "$http_status"
|
||||
_debug2 "_hostup_response" "$_hostup_response"
|
||||
|
||||
case "$http_status" in
|
||||
200 | 201 | 204) return 0 ;;
|
||||
401)
|
||||
_err "HostUp API returned 401 Unauthorized. Check HOSTUP_API_KEY scopes and IP restrictions."
|
||||
return 1
|
||||
;;
|
||||
403)
|
||||
_err "HostUp API returned 403 Forbidden. The API key lacks required DNS scopes."
|
||||
return 1
|
||||
;;
|
||||
404)
|
||||
_err "HostUp API returned 404 Not Found for $route"
|
||||
return 1
|
||||
;;
|
||||
429)
|
||||
_err "HostUp API rate limit exceeded. Please retry later."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
_err "HostUp API request failed with status $http_status"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
244
dnsapi/dns_infoblox_uddi.sh
Normal file
244
dnsapi/dns_infoblox_uddi.sh
Normal file
@@ -0,0 +1,244 @@
|
||||
#!/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
|
||||
'
|
||||
|
||||
Infoblox_UDDI_Api="https://"
|
||||
|
||||
######## 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"
|
||||
|
||||
_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"
|
||||
|
||||
_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
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
}
|
||||
|
||||
#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"
|
||||
|
||||
_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"
|
||||
|
||||
_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"
|
||||
|
||||
if ! _contains "$response" '"results"'; then
|
||||
_info "Don't need to remove, record not found."
|
||||
return 0
|
||||
fi
|
||||
|
||||
record_id=$(echo "$response" | _egrep_o '"id":[[:space:]]*"[^"]*"' | _head_n 1 | cut -d '"' -f 4)
|
||||
_debug "record_id" "$record_id"
|
||||
|
||||
if [ -z "$record_id" ]; then
|
||||
_info "Don't need to remove, record not found."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Extract UUID from the full record ID (format: dns/record/uuid)
|
||||
record_uuid=$(echo "$record_id" | sed 's|.*/||')
|
||||
_debug "record_uuid" "$record_uuid"
|
||||
|
||||
if ! _infoblox_rest DELETE "dns/record/$record_uuid"; then
|
||||
_err "Delete record error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Removed record successfully"
|
||||
return 0
|
||||
}
|
||||
|
||||
#################### 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
fi
|
||||
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
|
||||
response="$(_get "$Infoblox_UDDI_Api/$ep")"
|
||||
fi
|
||||
|
||||
_ret="$?"
|
||||
_debug2 response "$response"
|
||||
|
||||
if [ "$_ret" != "0" ]; then
|
||||
_err "Error: $ep"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -6,6 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_inwx
|
||||
Options:
|
||||
INWX_User Username
|
||||
INWX_Password Password
|
||||
INWX_Shared_Secret 2 Factor Authentication Shared Secret (optional requires oathtool)
|
||||
'
|
||||
|
||||
# Dependencies:
|
||||
@@ -110,11 +111,17 @@ dns_inwx_rm() {
|
||||
<string>%s</string>
|
||||
</value>
|
||||
</member>
|
||||
<member>
|
||||
<name>content</name>
|
||||
<value>
|
||||
<string>%s</string>
|
||||
</value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>' "$_domain" "$_sub_domain")
|
||||
</methodCall>' "$_domain" "$_sub_domain" "$txtvalue")
|
||||
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
|
||||
|
||||
if ! _contains "$response" "Command completed successfully"; then
|
||||
@@ -125,7 +132,7 @@ dns_inwx_rm() {
|
||||
if ! printf "%s" "$response" | grep "count" >/dev/null; then
|
||||
_info "Do not need to delete record"
|
||||
else
|
||||
_record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+')
|
||||
_record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><string>[0-9]+' | _egrep_o '[0-9]+')
|
||||
_info "Deleting record"
|
||||
_inwx_delete_record "$_record_id"
|
||||
fi
|
||||
@@ -324,7 +331,7 @@ _inwx_delete_record() {
|
||||
<member>
|
||||
<name>id</name>
|
||||
<value>
|
||||
<int>%s</int>
|
||||
<string>%s</string>
|
||||
</value>
|
||||
</member>
|
||||
</struct>
|
||||
@@ -362,7 +369,7 @@ _inwx_update_record() {
|
||||
<member>
|
||||
<name>id</name>
|
||||
<value>
|
||||
<int>%s</int>
|
||||
<string>%s</string>
|
||||
</value>
|
||||
</member>
|
||||
</struct>
|
||||
|
||||
109
dnsapi/dns_mgwm.sh
Normal file
109
dnsapi/dns_mgwm.sh
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_mgwm_info='mgw-media.de
|
||||
Site: mgw-media.de
|
||||
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/6669
|
||||
'
|
||||
# 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 (add record)"
|
||||
_debug "fulldomain: $fulldomain"
|
||||
_debug "txtvalue: $txtvalue"
|
||||
|
||||
# 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
|
||||
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
|
||||
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 (remove record)"
|
||||
_debug "fulldomain: $fulldomain"
|
||||
_debug "txtvalue: $txtvalue"
|
||||
|
||||
# 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
|
||||
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
|
||||
fi
|
||||
}
|
||||
#################### Private functions below ##################################
|
||||
|
||||
# _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_request() {
|
||||
_action="$1"
|
||||
_fulldomain="$2"
|
||||
_type="$3"
|
||||
_content="$4"
|
||||
|
||||
_debug "Calling _mgwm_request for action: $_action, domain: $_fulldomain, type: $_type, content: $_content"
|
||||
|
||||
# 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 <credentials_encoded>" # Log debug message without sensitive credentials
|
||||
|
||||
# Construct the API URL based on the action and provided parameters.
|
||||
_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 "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 "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
|
||||
fi
|
||||
}
|
||||
@@ -4,8 +4,8 @@ dns_omglol_info='omg.lol
|
||||
Site: omg.lol
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_omglol
|
||||
Options:
|
||||
OMG_ApiKey API Key. This is accessible from the bottom of the account page at https://home.omg.lol/account
|
||||
OMG_Address Address. This is your omg.lol address, without the preceding @ - you can see your list on your dashboard at https://home.omg.lol/dashboard
|
||||
OMG_ApiKey - API Key. This is accessible from the bottom of the account page at https://home.omg.lol/account
|
||||
OMG_Address - Address. This is your omg.lol address, without the preceding @ - you can see your list on your dashboard at https://home.omg.lol/dashboard
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/5299
|
||||
Author: @Kholin <kholin+acme.omglolapi@omg.lol>
|
||||
'
|
||||
@@ -35,7 +35,7 @@ dns_omglol_add() {
|
||||
_debug "omg.lol Address" "$OMG_Address"
|
||||
|
||||
omg_validate "$OMG_ApiKey" "$OMG_Address" "$fulldomain"
|
||||
if [ ! $? ]; then
|
||||
if [ 1 = $? ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -67,7 +67,7 @@ dns_omglol_rm() {
|
||||
_debug "omg.lol Address" "$OMG_Address"
|
||||
|
||||
omg_validate "$OMG_ApiKey" "$OMG_Address" "$fulldomain"
|
||||
if [ ! $? ]; then
|
||||
if [ 1 = $? ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -100,18 +100,49 @@ omg_validate() {
|
||||
fi
|
||||
|
||||
_endswith "$fulldomain" "omg.lol"
|
||||
if [ ! $? ]; then
|
||||
if [ 1 = $? ]; then
|
||||
_err "Domain name requested is not under omg.lol"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_endswith "$fulldomain" "$omg_address.omg.lol"
|
||||
if [ ! $? ]; then
|
||||
if [ 1 = $? ]; then
|
||||
_err "Domain name is not a subdomain of provided omg.lol address $omg_address"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "Required environment parameters are all present"
|
||||
omg_testconnect "$omg_apikey" "$omg_address"
|
||||
if [ 1 = $? ]; then
|
||||
_err "Authentication to omg.lol for address $omg_address using provided API key failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "Required environment parameters are all present and validated"
|
||||
}
|
||||
|
||||
# Validate that the address and API key are both correct and associated to each other
|
||||
omg_testconnect() {
|
||||
omg_apikey=$1
|
||||
omg_address=$2
|
||||
|
||||
_debug2 "Function" "omg_testconnect"
|
||||
_secure_debug2 "omg.lol API key" "$omg_apikey"
|
||||
_debug2 "omg.lol Address" "$omg_address"
|
||||
|
||||
authheader="$(_createAuthHeader "$omg_apikey")"
|
||||
export _H1="$authheader"
|
||||
endpoint="https://api.omg.lol/address/$omg_address/info"
|
||||
_debug2 "Endpoint for validation" "$endpoint"
|
||||
|
||||
response=$(_get "$endpoint" "" 30)
|
||||
|
||||
_jsonResponseCheck "$response" "status_code" 200
|
||||
if [ 1 = $? ]; then
|
||||
_debug2 "Failed to query omg.lol for $omg_address with provided API key"
|
||||
_secure_debug2 "API Key" "omg_apikey"
|
||||
_secure_debug3 "Raw response" "$response"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Add (or modify) an entry for a new ACME query
|
||||
|
||||
319
dnsapi/dns_sotoon.sh
Normal file
319
dnsapi/dns_sotoon.sh
Normal file
@@ -0,0 +1,319 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_sotoon_info='Sotoon.ir
|
||||
Site: Sotoon.ir
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_sotoon
|
||||
Options:
|
||||
Sotoon_Token API Token
|
||||
Sotoon_WorkspaceUUID Workspace UUID
|
||||
Sotoon_WorkspaceName Workspace Name
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/6656
|
||||
Author: Erfan Gholizade
|
||||
'
|
||||
|
||||
SOTOON_API_URL="https://api.sotoon.ir/delivery/v2/global"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Adding the txt record for validation.
|
||||
#Usage: dns_sotoon_add fulldomain TXT_record
|
||||
#Usage: dns_sotoon_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_sotoon_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_info_sotoon "Using Sotoon"
|
||||
|
||||
Sotoon_Token="${Sotoon_Token:-$(_readaccountconf_mutable Sotoon_Token)}"
|
||||
Sotoon_WorkspaceUUID="${Sotoon_WorkspaceUUID:-$(_readaccountconf_mutable Sotoon_WorkspaceUUID)}"
|
||||
Sotoon_WorkspaceName="${Sotoon_WorkspaceName:-$(_readaccountconf_mutable Sotoon_WorkspaceName)}"
|
||||
|
||||
if [ -z "$Sotoon_Token" ]; then
|
||||
_err_sotoon "You didn't specify \"Sotoon_Token\" token yet."
|
||||
_err_sotoon "You can get yours from here https://ocean.sotoon.ir/profile/tokens"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$Sotoon_WorkspaceUUID" ]; then
|
||||
_err_sotoon "You didn't specify \"Sotoon_WorkspaceUUID\" Workspace UUID yet."
|
||||
_err_sotoon "You can get yours from here https://ocean.sotoon.ir/profile/workspaces"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$Sotoon_WorkspaceName" ]; then
|
||||
_err_sotoon "You didn't specify \"Sotoon_WorkspaceName\" Workspace Name yet."
|
||||
_err_sotoon "You can get yours from here https://ocean.sotoon.ir/profile/workspaces"
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the info to the account conf file.
|
||||
_saveaccountconf_mutable Sotoon_Token "$Sotoon_Token"
|
||||
_saveaccountconf_mutable Sotoon_WorkspaceUUID "$Sotoon_WorkspaceUUID"
|
||||
_saveaccountconf_mutable Sotoon_WorkspaceName "$Sotoon_WorkspaceName"
|
||||
|
||||
_debug_sotoon "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err_sotoon "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info_sotoon "Adding record"
|
||||
|
||||
_debug_sotoon _domain_id "$_domain_id"
|
||||
_debug_sotoon _sub_domain "$_sub_domain"
|
||||
_debug_sotoon _domain "$_domain"
|
||||
|
||||
# First, GET the current domain zone to check for existing TXT records
|
||||
# This is needed for wildcard certs which require multiple TXT values
|
||||
_info_sotoon "Checking for existing TXT records"
|
||||
if ! _sotoon_rest GET "$_domain_id"; then
|
||||
_err_sotoon "Failed to get domain zone"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if there are existing TXT records for this subdomain
|
||||
_existing_txt=""
|
||||
if _contains "$response" "\"$_sub_domain\""; then
|
||||
_debug_sotoon "Found existing records for $_sub_domain"
|
||||
# Extract existing TXT values from the response
|
||||
# The format is: "_acme-challenge":[{"TXT":"value1","type":"TXT","ttl":10},{"TXT":"value2",...}]
|
||||
_existing_txt=$(echo "$response" | _egrep_o "\"$_sub_domain\":\[[^]]*\]" | sed "s/\"$_sub_domain\"://")
|
||||
_debug_sotoon "Existing TXT records: $_existing_txt"
|
||||
fi
|
||||
|
||||
# Build the new record entry
|
||||
_new_record="{\"TXT\":\"$txtvalue\",\"type\":\"TXT\",\"ttl\":120}"
|
||||
|
||||
# If there are existing records, append to them; otherwise create new array
|
||||
if [ -n "$_existing_txt" ] && [ "$_existing_txt" != "[]" ] && [ "$_existing_txt" != "null" ]; then
|
||||
# Check if this exact TXT value already exists (avoid duplicates)
|
||||
if _contains "$_existing_txt" "\"$txtvalue\""; then
|
||||
_info_sotoon "TXT record already exists, skipping"
|
||||
return 0
|
||||
fi
|
||||
# Remove the closing bracket and append new record
|
||||
_combined_records="$(echo "$_existing_txt" | sed 's/]$//'),$_new_record]"
|
||||
_debug_sotoon "Combined records: $_combined_records"
|
||||
else
|
||||
# No existing records, create new array
|
||||
_combined_records="[$_new_record]"
|
||||
fi
|
||||
|
||||
# Prepare the DNS record data in Kubernetes CRD format
|
||||
_dns_record="{\"spec\":{\"records\":{\"$_sub_domain\":$_combined_records}}}"
|
||||
|
||||
_debug_sotoon "DNS record payload: $_dns_record"
|
||||
|
||||
# Use PATCH to update/add the record to the domain zone
|
||||
_info_sotoon "Updating domain zone $_domain_id with TXT record"
|
||||
if _sotoon_rest PATCH "$_domain_id" "$_dns_record"; then
|
||||
if _contains "$response" "$txtvalue" || _contains "$response" "\"$_sub_domain\""; then
|
||||
_info_sotoon "Added, OK"
|
||||
return 0
|
||||
else
|
||||
_debug_sotoon "Response: $response"
|
||||
_err_sotoon "Add txt record error."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
_err_sotoon "Add txt record error."
|
||||
return 1
|
||||
}
|
||||
|
||||
#Remove the txt record after validation.
|
||||
#Usage: dns_sotoon_rm fulldomain TXT_record
|
||||
#Usage: dns_sotoon_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_sotoon_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_info_sotoon "Using Sotoon"
|
||||
_debug_sotoon fulldomain "$fulldomain"
|
||||
_debug_sotoon txtvalue "$txtvalue"
|
||||
|
||||
Sotoon_Token="${Sotoon_Token:-$(_readaccountconf_mutable Sotoon_Token)}"
|
||||
Sotoon_WorkspaceUUID="${Sotoon_WorkspaceUUID:-$(_readaccountconf_mutable Sotoon_WorkspaceUUID)}"
|
||||
Sotoon_WorkspaceName="${Sotoon_WorkspaceName:-$(_readaccountconf_mutable Sotoon_WorkspaceName)}"
|
||||
|
||||
_debug_sotoon "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err_sotoon "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug_sotoon _domain_id "$_domain_id"
|
||||
_debug_sotoon _sub_domain "$_sub_domain"
|
||||
_debug_sotoon _domain "$_domain"
|
||||
|
||||
_info_sotoon "Removing TXT record"
|
||||
|
||||
# First, GET the current domain zone to check for existing TXT records
|
||||
if ! _sotoon_rest GET "$_domain_id"; then
|
||||
_err_sotoon "Failed to get domain zone"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if there are existing TXT records for this subdomain
|
||||
_existing_txt=""
|
||||
if _contains "$response" "\"$_sub_domain\""; then
|
||||
_debug_sotoon "Found existing records for $_sub_domain"
|
||||
_existing_txt=$(echo "$response" | _egrep_o "\"$_sub_domain\":\[[^]]*\]" | sed "s/\"$_sub_domain\"://")
|
||||
_debug_sotoon "Existing TXT records: $_existing_txt"
|
||||
fi
|
||||
|
||||
# If no existing records, nothing to remove
|
||||
if [ -z "$_existing_txt" ] || [ "$_existing_txt" = "[]" ] || [ "$_existing_txt" = "null" ]; then
|
||||
_info_sotoon "No TXT records found, nothing to remove"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Remove the specific TXT value from the array
|
||||
# This handles the case where there are multiple TXT values (wildcard certs)
|
||||
_remaining_records=$(echo "$_existing_txt" | sed "s/{\"TXT\":\"$txtvalue\"[^}]*},*//g" | sed 's/,]/]/g' | sed 's/\[,/[/g')
|
||||
_debug_sotoon "Remaining records after removal: $_remaining_records"
|
||||
|
||||
# If no records remain, set to null to remove the subdomain entirely
|
||||
if [ "$_remaining_records" = "[]" ] || [ -z "$_remaining_records" ]; then
|
||||
_dns_record="{\"spec\":{\"records\":{\"$_sub_domain\":null}}}"
|
||||
else
|
||||
_dns_record="{\"spec\":{\"records\":{\"$_sub_domain\":$_remaining_records}}}"
|
||||
fi
|
||||
|
||||
_debug_sotoon "Remove record payload: $_dns_record"
|
||||
|
||||
# Use PATCH to remove the record from the domain zone
|
||||
if _sotoon_rest PATCH "$_domain_id" "$_dns_record"; then
|
||||
_info_sotoon "Record removed, OK"
|
||||
return 0
|
||||
else
|
||||
_debug_sotoon "Response: $response"
|
||||
_err_sotoon "Error removing record"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=1
|
||||
p=1
|
||||
|
||||
_debug_sotoon "Getting root domain for: $domain"
|
||||
_debug_sotoon "Sotoon WorkspaceUUID: $Sotoon_WorkspaceUUID"
|
||||
_debug_sotoon "Sotoon WorkspaceName: $Sotoon_WorkspaceName"
|
||||
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||
_debug_sotoon "Checking domain part: $h"
|
||||
|
||||
if [ -z "$h" ]; then
|
||||
#not valid
|
||||
_err_sotoon "Could not find valid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug_sotoon "Fetching domain zones from Sotoon API"
|
||||
if ! _sotoon_rest GET ""; then
|
||||
_err_sotoon "Failed to get domain zones from Sotoon API"
|
||||
_err_sotoon "Please check your Sotoon_Token, Sotoon_WorkspaceUUID, and Sotoon_WorkspaceName"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug2_sotoon "API Response: $response"
|
||||
|
||||
# Check if the response contains our domain
|
||||
# Sotoon API uses Kubernetes CRD format with spec.origin for domain matching
|
||||
if _contains "$response" "\"origin\":\"$h\""; then
|
||||
_debug_sotoon "Found domain by origin: $h"
|
||||
|
||||
# In Kubernetes CRD format, the metadata.name is the resource identifier
|
||||
# The name can be either:
|
||||
# 1. Same as origin
|
||||
# 2. Origin with dots replaced by hyphens
|
||||
# We check both patterns in the response to determine which one exists
|
||||
|
||||
# Convert origin to hyphenated version for checking
|
||||
_h_hyphenated=$(echo "$h" | tr '.' '-')
|
||||
|
||||
# Check if the hyphenated name exists in the response
|
||||
if _contains "$response" "\"name\":\"$_h_hyphenated\""; then
|
||||
_domain_id="$_h_hyphenated"
|
||||
_debug_sotoon "Found domain ID (hyphenated): $_domain_id"
|
||||
# Check if the origin itself is used as name
|
||||
elif _contains "$response" "\"name\":\"$h\""; then
|
||||
_domain_id="$h"
|
||||
_debug_sotoon "Found domain ID (same as origin): $_domain_id"
|
||||
else
|
||||
# Fallback: use the hyphenated version (more common)
|
||||
_domain_id="$_h_hyphenated"
|
||||
_debug_sotoon "Using hyphenated domain ID as fallback: $_domain_id"
|
||||
fi
|
||||
|
||||
if [ -n "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||
_domain=$h
|
||||
_debug_sotoon "Domain ID (metadata.name): $_domain_id"
|
||||
_debug_sotoon "Sub domain: $_sub_domain"
|
||||
_debug_sotoon "Domain (origin): $_domain"
|
||||
return 0
|
||||
fi
|
||||
_err_sotoon "Found domain $h but could not extract domain ID"
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_sotoon_rest() {
|
||||
mtd="$1"
|
||||
resource_id="$2"
|
||||
data="$3"
|
||||
|
||||
token_trimmed=$(echo "$Sotoon_Token" | tr -d '"')
|
||||
|
||||
# Construct the API endpoint
|
||||
_api_path="$SOTOON_API_URL/workspaces/$Sotoon_WorkspaceUUID/namespaces/$Sotoon_WorkspaceName/domainzones"
|
||||
|
||||
if [ -n "$resource_id" ]; then
|
||||
_api_path="$_api_path/$resource_id"
|
||||
fi
|
||||
|
||||
_debug_sotoon "API Path: $_api_path"
|
||||
_debug_sotoon "Method: $mtd"
|
||||
|
||||
# Set authorization header - Sotoon API uses Bearer token
|
||||
export _H1="Authorization: Bearer $token_trimmed"
|
||||
|
||||
if [ "$mtd" = "GET" ]; then
|
||||
# GET request
|
||||
_debug_sotoon "GET" "$_api_path"
|
||||
response="$(_get "$_api_path")"
|
||||
elif [ "$mtd" = "PATCH" ]; then
|
||||
# PATCH Request
|
||||
export _H2="Content-Type: application/merge-patch+json"
|
||||
_debug_sotoon data "$data"
|
||||
response="$(_post "$data" "$_api_path" "" "$mtd")"
|
||||
else
|
||||
_err_sotoon "Unknown method: $mtd"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug2_sotoon response "$response"
|
||||
return 0
|
||||
}
|
||||
|
||||
#Wrappers for logging
|
||||
_info_sotoon() {
|
||||
_info "[Sotoon]" "$@"
|
||||
}
|
||||
|
||||
_err_sotoon() {
|
||||
_err "[Sotoon]" "$@"
|
||||
}
|
||||
|
||||
_debug_sotoon() {
|
||||
_debug "[Sotoon]" "$@"
|
||||
}
|
||||
|
||||
_debug2_sotoon() {
|
||||
_debug2 "[Sotoon]" "$@"
|
||||
}
|
||||
Reference in New Issue
Block a user