]> git.proxmox.com Git - mirror_acme.sh.git/blob - dnsapi/dns_inwx.sh
Fix multiple domains with 2FA, reuse session cookie
[mirror_acme.sh.git] / dnsapi / dns_inwx.sh
1 #!/usr/bin/env sh
2
3 #
4 #INWX_User="username"
5 #
6 #INWX_Password="password"
7 #
8 # Dependencies:
9 # -------------
10 # - oathtool (When using 2 Factor Authentication)
11
12 INWX_Api="https://api.domrobot.com/xmlrpc/"
13
14 ######## Public functions #####################
15
16 #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
17 dns_inwx_add() {
18 fulldomain=$1
19 txtvalue=$2
20
21 INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
22 INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
23 INWX_Shared_Secret="${INWX_Shared_Secret:-$(_readaccountconf_mutable INWX_Shared_Secret)}"
24 if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
25 INWX_User=""
26 INWX_Password=""
27 _err "You don't specify inwx user and password yet."
28 _err "Please create you key and try again."
29 return 1
30 fi
31
32 #save the api key and email to the account conf file.
33 _saveaccountconf_mutable INWX_User "$INWX_User"
34 _saveaccountconf_mutable INWX_Password "$INWX_Password"
35 _saveaccountconf_mutable INWX_Shared_Secret "$INWX_Shared_Secret"
36
37 if ! _inwx_login; then
38 return 1
39 fi
40
41 _debug "First detect the root zone"
42 if ! _get_root "$fulldomain"; then
43 _err "invalid domain"
44 return 1
45 fi
46 _debug _sub_domain "$_sub_domain"
47 _debug _domain "$_domain"
48
49 _info "Adding record"
50 _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue"
51
52 }
53
54 #fulldomain txtvalue
55 dns_inwx_rm() {
56
57 fulldomain=$1
58 txtvalue=$2
59
60 INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
61 INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
62 INWX_Shared_Secret="${INWX_Shared_Secret:-$(_readaccountconf_mutable INWX_Shared_Secret)}"
63 if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
64 INWX_User=""
65 INWX_Password=""
66 _err "You don't specify inwx user and password yet."
67 _err "Please create you key and try again."
68 return 1
69 fi
70
71 if ! _inwx_login; then
72 return 1
73 fi
74
75 _debug "First detect the root zone"
76 if ! _get_root "$fulldomain"; then
77 _err "invalid domain"
78 return 1
79 fi
80 _debug _sub_domain "$_sub_domain"
81 _debug _domain "$_domain"
82
83 _debug "Getting txt records"
84
85 xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
86 <methodCall>
87 <methodName>nameserver.info</methodName>
88 <params>
89 <param>
90 <value>
91 <struct>
92 <member>
93 <name>domain</name>
94 <value>
95 <string>%s</string>
96 </value>
97 </member>
98 <member>
99 <name>type</name>
100 <value>
101 <string>TXT</string>
102 </value>
103 </member>
104 <member>
105 <name>name</name>
106 <value>
107 <string>%s</string>
108 </value>
109 </member>
110 </struct>
111 </value>
112 </param>
113 </params>
114 </methodCall>' "$_domain" "$_sub_domain")
115 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
116
117 if ! _contains "$response" "Command completed successfully"; then
118 _err "Error could not get txt records"
119 return 1
120 fi
121
122 if ! printf "%s" "$response" | grep "count" >/dev/null; then
123 _info "Do not need to delete record"
124 else
125 _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]+')
126 _info "Deleting record"
127 _inwx_delete_record "$_record_id"
128 fi
129
130 }
131
132 #################### Private functions below ##################################
133
134 _inwx_check_cookie() {
135 INWX_Cookie="${INWX_Cookie:-$(_readaccountconf_mutable INWX_Cookie)}"
136 if [ -z "$INWX_Cookie" ]; then
137 _debug "No cached cookie found"
138 return 1
139 fi
140 _H1="$INWX_Cookie"
141 export _H1
142
143 xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
144 <methodCall>
145 <methodName>account.info</methodName>
146 </methodCall>')
147
148 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
149
150 if _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
151 _debug "Cached cookie still valid"
152 return 0
153 fi
154
155 _debug "Cached cookie no longer valid"
156 _H1=""
157 export _H1
158 INWX_Cookie=""
159 _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
160 return 1
161 }
162
163 _inwx_login() {
164
165 if _inwx_check_cookie; then
166 _debug "Already logged in"
167 return 0
168 fi
169
170 xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
171 <methodCall>
172 <methodName>account.login</methodName>
173 <params>
174 <param>
175 <value>
176 <struct>
177 <member>
178 <name>user</name>
179 <value>
180 <string>%s</string>
181 </value>
182 </member>
183 <member>
184 <name>pass</name>
185 <value>
186 <string>%s</string>
187 </value>
188 </member>
189 </struct>
190 </value>
191 </param>
192 </params>
193 </methodCall>' "$INWX_User" "$INWX_Password")
194
195 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
196
197 INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
198 _H1=$INWX_Cookie
199 export _H1
200 export INWX_Cookie
201 _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
202
203 if ! _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
204 _err "INWX API: Authentication error (username/password correct?)"
205 return 1
206 fi
207
208 #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71
209 if _contains "$response" "<member><name>tfa</name><value><string>GOOGLE-AUTH</string></value></member>"; then
210 if [ -z "$INWX_Shared_Secret" ]; then
211 _err "INWX API: Mobile TAN detected."
212 _err "Please define a shared secret."
213 return 1
214 fi
215
216 if ! _exists oathtool; then
217 _err "Please install oathtool to use 2 Factor Authentication."
218 _err ""
219 return 1
220 fi
221
222 tan="$(oathtool --base32 --totp "${INWX_Shared_Secret}" 2>/dev/null)"
223
224 xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
225 <methodCall>
226 <methodName>account.unlock</methodName>
227 <params>
228 <param>
229 <value>
230 <struct>
231 <member>
232 <name>tan</name>
233 <value>
234 <string>%s</string>
235 </value>
236 </member>
237 </struct>
238 </value>
239 </param>
240 </params>
241 </methodCall>' "$tan")
242
243 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
244
245 if ! _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>"; then
246 _err "INWX API: Mobile TAN not correct."
247 return 1
248 fi
249 fi
250
251 }
252
253 _get_root() {
254 domain=$1
255 _debug "get root"
256
257 domain=$1
258 i=2
259 p=1
260
261 xml_content='<?xml version="1.0" encoding="UTF-8"?>
262 <methodCall>
263 <methodName>nameserver.list</methodName>
264 </methodCall>'
265
266 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
267 while true; do
268 h=$(printf "%s" "$domain" | cut -d . -f $i-100)
269 _debug h "$h"
270 if [ -z "$h" ]; then
271 #not valid
272 return 1
273 fi
274
275 if _contains "$response" "$h"; then
276 _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
277 _domain="$h"
278 return 0
279 fi
280 p=$i
281 i=$(_math "$i" + 1)
282 done
283 return 1
284
285 }
286
287 _inwx_delete_record() {
288 record_id=$1
289 xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
290 <methodCall>
291 <methodName>nameserver.deleteRecord</methodName>
292 <params>
293 <param>
294 <value>
295 <struct>
296 <member>
297 <name>id</name>
298 <value>
299 <int>%s</int>
300 </value>
301 </member>
302 </struct>
303 </value>
304 </param>
305 </params>
306 </methodCall>' "$record_id")
307
308 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
309
310 if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
311 _err "Error"
312 return 1
313 fi
314 return 0
315
316 }
317
318 _inwx_update_record() {
319 record_id=$1
320 txtval=$2
321 xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
322 <methodCall>
323 <methodName>nameserver.updateRecord</methodName>
324 <params>
325 <param>
326 <value>
327 <struct>
328 <member>
329 <name>content</name>
330 <value>
331 <string>%s</string>
332 </value>
333 </member>
334 <member>
335 <name>id</name>
336 <value>
337 <int>%s</int>
338 </value>
339 </member>
340 </struct>
341 </value>
342 </param>
343 </params>
344 </methodCall>' "$txtval" "$record_id")
345
346 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
347
348 if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
349 _err "Error"
350 return 1
351 fi
352 return 0
353
354 }
355
356 _inwx_add_record() {
357
358 domain=$1
359 sub_domain=$2
360 txtval=$3
361
362 xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
363 <methodCall>
364 <methodName>nameserver.createRecord</methodName>
365 <params>
366 <param>
367 <value>
368 <struct>
369 <member>
370 <name>domain</name>
371 <value>
372 <string>%s</string>
373 </value>
374 </member>
375 <member>
376 <name>type</name>
377 <value>
378 <string>TXT</string>
379 </value>
380 </member>
381 <member>
382 <name>content</name>
383 <value>
384 <string>%s</string>
385 </value>
386 </member>
387 <member>
388 <name>name</name>
389 <value>
390 <string>%s</string>
391 </value>
392 </member>
393 </struct>
394 </value>
395 </param>
396 </params>
397 </methodCall>' "$domain" "$txtval" "$sub_domain")
398
399 response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
400
401 if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
402 _err "Error"
403 return 1
404 fi
405 return 0
406 }