]>
Commit | Line | Data |
---|---|---|
025e0e80 | 1 | #!/usr/bin/env sh |
35fb1f85 | 2 | |
025e0e80 | 3 | ## Will be called by acme.sh to add the TXT record via the Bunny DNS API. |
35fb1f85 SB |
4 | ## returns 0 means success, otherwise error. |
5 | ||
6 | ## Author: nosilver4u <nosilver4u at ewww.io> | |
7 | ## GitHub: https://github.com/nosilver4u/acme.sh | |
8 | ||
9 | ## | |
10 | ## Environment Variables Required: | |
11 | ## | |
12 | ## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc" | |
13 | ## | |
14 | ||
15 | ##################### Public functions ##################### | |
16 | ||
17 | ## Create the text record for validation. | |
18 | ## Usage: fulldomain txtvalue | |
19 | ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs" | |
20 | dns_bunny_add() { | |
21 | fulldomain="$(echo "$1" | _lower_case)" | |
22 | txtvalue=$2 | |
23 | ||
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 | |
27 | BUNNY_API_KEY="" | |
28 | _err "You did not specify Bunny.net API key." | |
29 | _err "Please export BUNNY_API_KEY and try again." | |
30 | return 1 | |
31 | fi | |
32 | ||
33 | _info "Using Bunny.net dns validation - add record" | |
34 | _debug fulldomain "$fulldomain" | |
35 | _debug txtvalue "$txtvalue" | |
36 | ||
37 | ## save the env vars (key and domain split location) for later automated use | |
38 | _saveaccountconf_mutable BUNNY_API_KEY "$BUNNY_API_KEY" | |
39 | ||
40 | ## split the domain for Bunny API | |
41 | if ! _get_base_domain "$fulldomain"; then | |
42 | _err "domain not found in your account for addition" | |
43 | return 1 | |
44 | fi | |
45 | _debug _sub_domain "$_sub_domain" | |
46 | _debug _domain "$_domain" | |
47 | _debug _domain_id "$_domain_id" | |
48 | ||
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}' | |
55 | ||
56 | _debug PURL "$PURL" | |
57 | _debug PBODY "$PBODY" | |
58 | ||
59 | ## the create request - POST | |
60 | ## args: BODY, URL, [need64, httpmethod] | |
61 | response="$(_post "$PBODY" "$PURL" "" "PUT")" | |
62 | ||
63 | ## check response | |
64 | if [ "$?" != "0" ]; then | |
65 | _err "error in response: $response" | |
66 | return 1 | |
67 | fi | |
68 | _debug2 response "$response" | |
69 | ||
70 | ## finished correctly | |
71 | return 0 | |
72 | } | |
73 | ||
74 | ## Remove the txt record after validation. | |
75 | ## Usage: fulldomain txtvalue | |
76 | ## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs" | |
77 | dns_bunny_rm() { | |
78 | fulldomain="$(echo "$1" | _lower_case)" | |
79 | txtvalue=$2 | |
80 | ||
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 | |
84 | BUNNY_API_KEY="" | |
85 | _err "You did not specify Bunny.net API key." | |
86 | _err "Please export BUNNY_API_KEY and try again." | |
87 | return 1 | |
88 | fi | |
89 | ||
90 | _info "Using Bunny.net dns validation - remove record" | |
91 | _debug fulldomain "$fulldomain" | |
92 | _debug txtvalue "$txtvalue" | |
93 | ||
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" | |
97 | return 1 | |
98 | fi | |
99 | _debug _sub_domain "$_sub_domain" | |
100 | _debug _domain "$_domain" | |
101 | _debug _domain_id "$_domain_id" | |
102 | ||
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" | |
108 | ||
109 | ## 1) Get the domain/zone records | |
110 | ## the fetch request - GET | |
111 | ## args: URL, [onlyheader, timeout] | |
112 | domain_list="$(_get "$GURL")" | |
113 | ||
114 | ## check response | |
115 | if [ "$?" != "0" ]; then | |
116 | _err "error in domain_list response: $domain_list" | |
117 | return 1 | |
118 | fi | |
119 | _debug2 domain_list "$domain_list" | |
120 | ||
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\"")" | |
124 | ||
125 | if [ -n "$record" ]; then | |
126 | ||
127 | ## We found records | |
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 | |
132 | ## delete the record | |
133 | ## delete URL for removing the one we dont want | |
134 | DURL="https://api.bunny.net/dnszone/$_domain_id/records/$rec_id" | |
135 | ||
136 | ## the removal request - DELETE | |
137 | ## args: BODY, URL, [need64, httpmethod] | |
138 | response="$(_post "" "$DURL" "" "DELETE")" | |
139 | ||
140 | ## check response (sort of) | |
141 | if [ "$?" != "0" ]; then | |
142 | _err "error in remove response: $response" | |
143 | return 1 | |
144 | fi | |
145 | _debug2 response "$response" | |
146 | ||
147 | done | |
148 | fi | |
149 | fi | |
150 | ||
151 | ## finished correctly | |
152 | return 0 | |
153 | } | |
154 | ||
155 | ##################### Private functions below ##################### | |
156 | ||
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 | |
161 | ## USAGE: fulldomain | |
162 | ## EG: "_acme-challenge.two.three.four.domain.com" | |
163 | ## returns | |
164 | ## _sub_domain="_acme-challenge.two" | |
165 | ## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists | |
166 | ## _domain_id=234 | |
167 | ## if only "domain.com" exists it will return | |
168 | ## _sub_domain="_acme-challenge.two.three.four" | |
169 | ## _domain="domain.com" | |
170 | ## _domain_id=234 | |
171 | _get_base_domain() { | |
172 | # args | |
173 | fulldomain="$(echo "$1" | _lower_case)" | |
174 | _debug fulldomain "$fulldomain" | |
175 | ||
176 | # domain max legal length = 253 | |
177 | MAX_DOM=255 | |
178 | page=1 | |
179 | ||
180 | ## get a list of domains for the account to check thru | |
181 | ## Set the headers | |
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" | |
188 | ||
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")" | |
193 | ||
194 | ## check response | |
195 | if [ "$?" != "0" ]; then | |
196 | _err "error in domain_list response: $domain_list" | |
197 | return 1 | |
198 | fi | |
199 | _debug2 domain_list "$domain_list" | |
200 | ||
4b0a7a6e | 201 | i=1 |
35fb1f85 SB |
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 | |
207 | break | |
208 | fi | |
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" | |
220 | found="" | |
221 | return 0 | |
222 | fi | |
223 | ## increment cut point $i | |
224 | i=$(_math $i + 1) | |
225 | done | |
226 | ||
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." | |
234 | found="" | |
235 | return 1 | |
236 | fi | |
237 | _debug2 nextpage "$nextpage" | |
238 | DOMURL="$nextpage" | |
239 | fi | |
240 | ||
241 | done | |
242 | ||
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!" | |
246 | found="" | |
247 | return 1 | |
248 | } |