@ -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"
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
# 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 "
_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 "
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 "
_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 ' "
baseurl = " https:// $Infoblox_Portal /api/ddi/v1/dns/record "
_info "Adding record"
body = " {\"type\":\"TXT\",\"name_in_zone\":\" $_sub_domain \",\"zone\":\" $_domain_id \",\"ttl\":120,\"inheritance_sources\":{\"ttl\":{\"action\":\"override\"}},\"rdata\":{\"text\":\" $txtvalue \"}} "
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 "
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 "Error encountered during record addition "
_err " Response: $result "
_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"
@ -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 "
zone_fqdn = ""
temp_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 "
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
if ! _contains " $response " '"results"' ; then
_info "Don't need to remove, record not found."
return 0
fi
temp_domain = $( echo " $temp_domain " | sed 's/^[^.]*\.//' )
if ! echo " $temp_domain " | grep -q '\.' ; then
break
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
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/"//' ) "
# 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
# 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/' )
_info "Removed record successfully"
return 0
}
_debug zone_id " $zone_id "
#################### 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
if [ -z " $zone_id " ] ; then
_err " Could not find zone ID for $zone_fqdn "
_debug " Zone result: $zone_result "
# 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
name_in_zone = $( echo " $fulldomain " | sed " s/\. $zone_fqdn \$// " | sed 's/\.$//' )
_debug name_in_zone " $name_in_zone "
# 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 "
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 "
# Calculate subdomain
if [ " $_domain " = " $domain " ] ; then
_sub_domain = ""
else
_cutlength = $(( ${# domain } - ${# _domain } - 1 ))
_sub_domain = $( printf "%s" " $domain " | cut -c " 1- $_cutlength " )
fi
result = " $( _get " $geturl " ) "
_debug2 result " $result "
return 0
fi
fi
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 "
p = $i
i = $(( i + 1 ) )
done
if [ -n " $record_id " ] ; then
record_uuid = $( echo " $record_id " | sed 's/.*\/\([a-f0-9-]*\)$/\1/' )
_debug record_uuid " $record_uuid "
return 1
}
delurl = " https:// $Infoblox_Portal /api/ddi/v1/dns/record/ $record_uuid "
rmResult = " $( _post "" " $delurl " "" "DELETE" ) "
# _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 "
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
_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 matching txtvalue found)"
return 1
response = " $( _get " $Infoblox_UDDI_Api / $ep " ) "
fi
else
_err "Record to delete didn't match an existing record (no results found)"
_debug " Response: $result "
_ret = " $? "
_debug2 response " $response "
if [ " $_ret " != "0" ] ; then
_err " Error: $ep "
return 1
fi
return 0
}