3 ## Will be called by acme.sh to add the TXT record via the Bunny DNS API.
4 ## returns 0 means success, otherwise error.
6 ## Author: nosilver4u <nosilver4u at ewww.io>
7 ## GitHub: https://github.com/nosilver4u/acme.sh
10 ## Environment Variables Required:
12 ## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc"
15 ##################### Public functions #####################
17 ## Create the text record for validation.
18 ## Usage: fulldomain txtvalue
19 ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
21 fulldomain
="$(echo "$1" | _lower_case)"
24 BUNNY_API_KEY
="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
25 # Check if API Key is set
26 if [ -z "$BUNNY_API_KEY" ]; then
28 _err
"You did not specify Bunny.net API key."
29 _err
"Please export BUNNY_API_KEY and try again."
33 _info
"Using Bunny.net dns validation - add record"
34 _debug fulldomain
"$fulldomain"
35 _debug txtvalue
"$txtvalue"
37 ## save the env vars (key and domain split location) for later automated use
38 _saveaccountconf_mutable BUNNY_API_KEY
"$BUNNY_API_KEY"
40 ## split the domain for Bunny API
41 if ! _get_base_domain
"$fulldomain"; then
42 _err
"domain not found in your account for addition"
45 _debug _sub_domain
"$_sub_domain"
46 _debug _domain
"$_domain"
47 _debug _domain_id
"$_domain_id"
49 ## Set the header with our post type and auth key
50 export _H1
="Accept: application/json"
51 export _H2
="AccessKey: $BUNNY_API_KEY"
52 export _H3
="Content-Type: application/json"
53 PURL
="https://api.bunny.net/dnszone/$_domain_id/records"
54 PBODY
='{"Id":'$_domain_id',"Type":3,"Name":"'$_sub_domain'","Value":"'$txtvalue'","ttl":120}'
59 ## the create request - POST
60 ## args: BODY, URL, [need64, httpmethod]
61 response
="$(_post "$PBODY" "$PURL" "" "PUT
")"
64 if [ "$?" != "0" ]; then
65 _err
"error in response: $response"
68 _debug2 response
"$response"
74 ## Remove the txt record after validation.
75 ## Usage: fulldomain txtvalue
76 ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
78 fulldomain
="$(echo "$1" | _lower_case)"
81 BUNNY_API_KEY
="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
82 # Check if API Key Exists
83 if [ -z "$BUNNY_API_KEY" ]; then
85 _err
"You did not specify Bunny.net API key."
86 _err
"Please export BUNNY_API_KEY and try again."
90 _info
"Using Bunny.net dns validation - remove record"
91 _debug fulldomain
"$fulldomain"
92 _debug txtvalue
"$txtvalue"
94 ## split the domain for Bunny API
95 if ! _get_base_domain
"$fulldomain"; then
96 _err
"Domain not found in your account for TXT record removal"
99 _debug _sub_domain
"$_sub_domain"
100 _debug _domain
"$_domain"
101 _debug _domain_id
"$_domain_id"
103 ## Set the header with our post type and key auth key
104 export _H1
="Accept: application/json"
105 export _H2
="AccessKey: $BUNNY_API_KEY"
106 ## get URL for the list of DNS records
107 GURL
="https://api.bunny.net/dnszone/$_domain_id"
109 ## 1) Get the domain/zone records
110 ## the fetch request - GET
111 ## args: URL, [onlyheader, timeout]
112 domain_list
="$(_get "$GURL")"
115 if [ "$?" != "0" ]; then
116 _err
"error in domain_list response: $domain_list"
119 _debug2 domain_list
"$domain_list"
121 ## 2) search through records
122 ## check for what we are looking for: "Type":3,"Value":"$txtvalue","Name":"$_sub_domain"
123 record
="$(echo "$domain_list" | _egrep_o "\"Id
\"\s
*\
:\s
*\"*[0-9]+\"*,\s
*\"Type
\"[^
}]*\"Value
\"\s
*\
:\s
*\"$txtvalue\"[^
}]*\"Name
\"\s
*\
:\s
*\"$_sub_domain\"")"
125 if [ -n "$record" ]; then
128 rec_ids
="$(echo "$record" | _egrep_o "Id
\"\s
*\
:\s
*\"*[0-9]+" | _egrep_o "[0-9]+")"
129 _debug rec_ids
"$rec_ids"
130 if [ -n "$rec_ids" ]; then
131 echo "$rec_ids" |
while IFS
= read -r rec_id
; do
133 ## delete URL for removing the one we dont want
134 DURL
="https://api.bunny.net/dnszone/$_domain_id/records/$rec_id"
136 ## the removal request - DELETE
137 ## args: BODY, URL, [need64, httpmethod]
138 response
="$(_post "" "$DURL" "" "DELETE
")"
140 ## check response (sort of)
141 if [ "$?" != "0" ]; then
142 _err
"error in remove response: $response"
145 _debug2 response
"$response"
151 ## finished correctly
155 ##################### Private functions below #####################
157 ## Split the domain provided into the "base domain" and the "start prefix".
158 ## This function searches for the longest subdomain in your account
159 ## for the full domain given and splits it into the base domain (zone)
160 ## and the prefix/record to be added/removed
162 ## EG: "_acme-challenge.two.three.four.domain.com"
164 ## _sub_domain="_acme-challenge.two"
165 ## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists
167 ## if only "domain.com" exists it will return
168 ## _sub_domain="_acme-challenge.two.three.four"
169 ## _domain="domain.com"
173 fulldomain
="$(echo "$1" | _lower_case)"
174 _debug fulldomain
"$fulldomain"
176 # domain max legal length = 253
180 ## get a list of domains for the account to check thru
182 export _H1
="Accept: application/json"
183 export _H2
="AccessKey: $BUNNY_API_KEY"
184 _debug BUNNY_API_KEY
"$BUNNY_API_KEY"
185 ## get URL for the list of domains
186 ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
187 DOMURL
="https://api.bunny.net/dnszone"
189 ## while we dont have a matching domain we keep going
190 while [ -z "$found" ]; do
191 ## get the domain list (current page)
192 domain_list
="$(_get "$DOMURL")"
195 if [ "$?" != "0" ]; then
196 _err
"error in domain_list response: $domain_list"
199 _debug2 domain_list
"$domain_list"
202 while [ $i -gt 0 ]; do
203 ## get next longest domain
204 _domain
=$
(printf "%s" "$fulldomain" | cut
-d .
-f "$i"-"$MAX_DOM")
205 ## check we got something back from our cut (or are we at the end)
206 if [ -z "$_domain" ]; then
209 ## we got part of a domain back - grep it out
210 found
="$(echo "$domain_list" | _egrep_o "\"Id
\"\s
*:\s
*\"*[0-9]+\"*,\s
*\"Domain
\"\s
*\
:\s
*\"$_domain\"")"
211 ## check if it exists
212 if [ -n "$found" ]; then
213 ## exists - exit loop returning the parts
214 sub_point
=$
(_math
$i - 1)
215 _sub_domain
=$
(printf "%s" "$fulldomain" | cut
-d .
-f 1-"$sub_point")
216 _domain_id
="$(echo "$found" | _egrep_o "Id
\"\s
*\
:\s
*\"*[0-9]+" | _egrep_o "[0-9]+")"
217 _debug _domain_id
"$_domain_id"
218 _debug _domain
"$_domain"
219 _debug _sub_domain
"$_sub_domain"
223 ## increment cut point $i
227 if [ -z "$found" ]; then
228 page
=$
(_math
$page + 1)
229 nextpage
="https://api.bunny.net/dnszone?page=$page"
230 ## Find the next page if we don't have a match.
231 hasnextpage
="$(echo "$domain_list" | _egrep_o "\"HasMoreItems
\"\s
*:\s
*true
")"
232 if [ -z "$hasnextpage" ]; then
233 _err
"No record and no nextpage in Bunny.net domain search."
237 _debug2 nextpage
"$nextpage"
243 ## We went through the entire domain zone list and didn't find one that matched.
244 ## If we ever get here, something is broken in the code...
245 _err
"Domain not found in Bunny.net account, but we should never get here!"