Browse Source
feat(dnsapi): add ApertoDNS DNS plugin
feat(dnsapi): add ApertoDNS DNS plugin
Add dns_apertodns.sh for ApertoDNS dynamic DNS service. Features: - DNS-01 challenge support for Let's Encrypt - Works with standard (*.apertodns.com) and custom domains - Uses ApertoDNS Protocol API v1.2 Usage: export APERTODNS_API_KEY="your-api-key" acme.sh --issue --dns dns_apertodns -d example.apertodns.com Documentation: https://github.com/apertodns/acme-dns-apertodnspull/6758/head
1 changed files with 201 additions and 0 deletions
@ -0,0 +1,201 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# shellcheck disable=SC2034 |
||||
|
dns_apertodns_info='ApertoDNS |
||||
|
Site: www.apertodns.com |
||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_apertodns |
||||
|
Options: |
||||
|
APERTODNS_API_KEY API Key |
||||
|
APERTODNS_API_URL API URL (optional, default: https://api.apertodns.com) |
||||
|
Author: Andrea Ferro <support@apertodns.com> |
||||
|
' |
||||
|
|
||||
|
APERTODNS_API_DEFAULT="https://api.apertodns.com" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
# Usage: dns_apertodns_add _acme-challenge.myhost.apertodns.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_apertodns_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "Using ApertoDNS API" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
APERTODNS_API_KEY="${APERTODNS_API_KEY:-$(_readaccountconf_mutable APERTODNS_API_KEY)}" |
||||
|
APERTODNS_API_URL="${APERTODNS_API_URL:-$(_readaccountconf_mutable APERTODNS_API_URL)}" |
||||
|
|
||||
|
if [ -z "$APERTODNS_API_KEY" ]; then |
||||
|
APERTODNS_API_KEY="" |
||||
|
_err "You did not specify APERTODNS_API_KEY yet." |
||||
|
_err "Please create your API key at https://www.apertodns.com/dashboard and try again." |
||||
|
_err "e.g." |
||||
|
_err "export APERTODNS_API_KEY=apertodns_live_xxxxxxxx" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$APERTODNS_API_URL" ]; then |
||||
|
APERTODNS_API_URL="$APERTODNS_API_DEFAULT" |
||||
|
fi |
||||
|
|
||||
|
# Save the credentials |
||||
|
_saveaccountconf_mutable APERTODNS_API_KEY "$APERTODNS_API_KEY" |
||||
|
if [ "$APERTODNS_API_URL" != "$APERTODNS_API_DEFAULT" ]; then |
||||
|
_saveaccountconf_mutable APERTODNS_API_URL "$APERTODNS_API_URL" |
||||
|
fi |
||||
|
|
||||
|
# Extract hostname and TXT name from fulldomain |
||||
|
# fulldomain: _acme-challenge.myhost.apertodns.com |
||||
|
# hostname: myhost.apertodns.com |
||||
|
# txtname: _acme-challenge |
||||
|
if ! _apertodns_parse_domain "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _hostname "$_hostname" |
||||
|
_debug _txtname "$_txtname" |
||||
|
|
||||
|
# Build JSON payload |
||||
|
_info "Adding TXT record for $_hostname" |
||||
|
_body="{\"hostname\":\"$_hostname\",\"txt\":{\"name\":\"$_txtname\",\"value\":\"$txtvalue\",\"action\":\"set\"}}" |
||||
|
|
||||
|
if _apertodns_rest POST "/.well-known/apertodns/v1/update" "$_body"; then |
||||
|
if _contains "$response" "\"success\":true" || _contains "$response" "\"status\":\"good\"" || _contains "$response" "\"status\":\"nochg\""; then |
||||
|
_info "TXT record added successfully" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Failed to add TXT record: $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
_err "Error adding TXT record" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
# Usage: dns_apertodns_rm _acme-challenge.myhost.apertodns.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
||||
|
dns_apertodns_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
_info "Using ApertoDNS API" |
||||
|
_debug fulldomain "$fulldomain" |
||||
|
_debug txtvalue "$txtvalue" |
||||
|
|
||||
|
APERTODNS_API_KEY="${APERTODNS_API_KEY:-$(_readaccountconf_mutable APERTODNS_API_KEY)}" |
||||
|
APERTODNS_API_URL="${APERTODNS_API_URL:-$(_readaccountconf_mutable APERTODNS_API_URL)}" |
||||
|
|
||||
|
if [ -z "$APERTODNS_API_KEY" ]; then |
||||
|
APERTODNS_API_KEY="" |
||||
|
_err "You did not specify APERTODNS_API_KEY yet." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$APERTODNS_API_URL" ]; then |
||||
|
APERTODNS_API_URL="$APERTODNS_API_DEFAULT" |
||||
|
fi |
||||
|
|
||||
|
# Extract hostname and TXT name from fulldomain |
||||
|
if ! _apertodns_parse_domain "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _hostname "$_hostname" |
||||
|
_debug _txtname "$_txtname" |
||||
|
|
||||
|
# Build JSON payload |
||||
|
_info "Removing TXT record for $_hostname" |
||||
|
_body="{\"hostname\":\"$_hostname\",\"txt\":{\"name\":\"$_txtname\",\"action\":\"delete\"}}" |
||||
|
|
||||
|
if _apertodns_rest POST "/.well-known/apertodns/v1/update" "$_body"; then |
||||
|
if _contains "$response" "\"success\":true" || _contains "$response" "\"status\":\"good\"" || _contains "$response" "\"status\":\"nochg\""; then |
||||
|
_info "TXT record removed successfully" |
||||
|
return 0 |
||||
|
else |
||||
|
_err "Failed to remove TXT record: $response" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
_err "Error removing TXT record" |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
# Parse fulldomain to extract hostname and txtname |
||||
|
# Input: _acme-challenge.myhost.apertodns.com |
||||
|
# Output: _hostname=myhost.apertodns.com, _txtname=_acme-challenge |
||||
|
_apertodns_parse_domain() { |
||||
|
domain="$1" |
||||
|
|
||||
|
# Check if domain ends with .apertodns.com |
||||
|
if ! _contains "$domain" ".apertodns.com"; then |
||||
|
_err "Domain must be under apertodns.com" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
# Extract the TXT name (first part before the hostname) |
||||
|
# For _acme-challenge.myhost.apertodns.com: |
||||
|
# - _txtname = _acme-challenge |
||||
|
# - _hostname = myhost.apertodns.com |
||||
|
|
||||
|
# Count dots to determine structure |
||||
|
# _acme-challenge.myhost.apertodns.com has 4 parts |
||||
|
# myhost.apertodns.com has 3 parts |
||||
|
|
||||
|
# Get everything after the first dot |
||||
|
_rest="$(printf "%s" "$domain" | cut -d . -f 2-)" |
||||
|
|
||||
|
# Check if _rest is a valid apertodns hostname (X.apertodns.com) |
||||
|
if _contains "$_rest" ".apertodns.com"; then |
||||
|
# The first part is the TXT name |
||||
|
_txtname="$(printf "%s" "$domain" | cut -d . -f 1)" |
||||
|
_hostname="$_rest" |
||||
|
else |
||||
|
# No subdomain prefix, use the full domain |
||||
|
_txtname="_acme-challenge" |
||||
|
_hostname="$domain" |
||||
|
fi |
||||
|
|
||||
|
# Validate hostname format |
||||
|
if [ -z "$_hostname" ] || [ -z "$_txtname" ]; then |
||||
|
_err "Could not parse domain: $domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# REST API call |
||||
|
# Usage: _apertodns_rest METHOD ENDPOINT [DATA] |
||||
|
_apertodns_rest() { |
||||
|
method="$1" |
||||
|
endpoint="$2" |
||||
|
data="$3" |
||||
|
|
||||
|
url="$APERTODNS_API_URL$endpoint" |
||||
|
|
||||
|
export _H1="Authorization: Bearer $APERTODNS_API_KEY" |
||||
|
export _H2="Content-Type: application/json" |
||||
|
export _H3="Accept: application/json" |
||||
|
|
||||
|
_debug url "$url" |
||||
|
|
||||
|
if [ "$method" = "POST" ]; then |
||||
|
_secure_debug2 data "$data" |
||||
|
response="$(_post "$data" "$url" "" "POST")" |
||||
|
else |
||||
|
response="$(_get "$url")" |
||||
|
fi |
||||
|
|
||||
|
_ret="$?" |
||||
|
_debug2 response "$response" |
||||
|
|
||||
|
if [ "$_ret" != "0" ]; then |
||||
|
_err "API request failed" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue