]>
Commit | Line | Data |
---|---|---|
688a2341 BB |
1 | #!/usr/bin/env sh |
2 | ||
3 | #This file name is "dns_dnsservices.sh" | |
04ca808e | 4 | #Script for Danish DNS registra and DNS hosting provider https://dns.services |
c8d17bc3 | 5 | |
688a2341 | 6 | #Author: Bjarke Bruun <bbruun@gmail.com> |
543c4423 | 7 | #Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/4152 |
688a2341 | 8 | |
a364ab4e | 9 | # Global variable to connect to the DNS.Services API |
688a2341 BB |
10 | DNSServices_API=https://dns.services/api |
11 | ||
12 | ######## Public functions ##################### | |
13 | ||
dc882e62 | 14 | #Usage: dns_dnsservices_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
688a2341 | 15 | dns_dnsservices_add() { |
5c00afc6 BB |
16 | fulldomain="$1" |
17 | txtvalue="$2" | |
56a686d3 BB |
18 | |
19 | _info "Using dns.services to create ACME DNS challenge" | |
20 | _debug2 add_fulldomain "$fulldomain" | |
21 | _debug2 add_txtvalue "$txtvalue" | |
22 | ||
23 | # Read username/password from environment or .acme.sh/accounts.conf | |
24 | DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}" | |
25 | DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}" | |
26 | if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then | |
27 | DnsServices_Username="" | |
28 | DnsServices_Password="" | |
29 | _err "You didn't specify dns.services api username and password yet." | |
30 | _err "Set environment variables DnsServices_Username and DnsServices_Password" | |
31 | return 1 | |
32 | fi | |
33 | ||
34 | # Setup GET/POST/DELETE headers | |
35 | _setup_headers | |
36 | ||
37 | #save the credentials to the account conf file. | |
38 | _saveaccountconf_mutable DnsServices_Username "$DnsServices_Username" | |
39 | _saveaccountconf_mutable DnsServices_Password "$DnsServices_Password" | |
40 | ||
41 | if ! _contains "$DnsServices_Username" "@"; then | |
42 | _err "It seems that the username variable DnsServices_Username has not been set/left blank" | |
43 | _err "or is not a valid email. Please correct and try again." | |
44 | return 1 | |
45 | fi | |
46 | ||
47 | if ! _get_root "${fulldomain}"; then | |
48 | _err "Invalid domain ${fulldomain}" | |
49 | return 1 | |
50 | fi | |
51 | ||
52 | if ! createRecord "$fulldomain" "${txtvalue}"; then | |
53 | _err "Error creating TXT record in domain $fulldomain in $rootZoneName" | |
54 | return 1 | |
55 | fi | |
56 | ||
57 | _debug2 challenge-created "Created $fulldomain" | |
58 | return 0 | |
688a2341 BB |
59 | } |
60 | ||
61 | #Usage: fulldomain txtvalue | |
62 | #Description: Remove the txt record after validation. | |
63 | dns_dnsservices_rm() { | |
5c00afc6 BB |
64 | fulldomain="$1" |
65 | txtvalue="$2" | |
56a686d3 | 66 | |
c1ba4f1b | 67 | _info "Using dns.services to remove DNS record $fulldomain TXT $txtvalue" |
56a686d3 BB |
68 | _debug rm_fulldomain "$fulldomain" |
69 | _debug rm_txtvalue "$txtvalue" | |
70 | ||
71 | # Read username/password from environment or .acme.sh/accounts.conf | |
72 | DnsServices_Username="${DnsServices_Username:-$(_readaccountconf_mutable DnsServices_Username)}" | |
73 | DnsServices_Password="${DnsServices_Password:-$(_readaccountconf_mutable DnsServices_Password)}" | |
74 | if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then | |
75 | DnsServices_Username="" | |
76 | DnsServices_Password="" | |
77 | _err "You didn't specify dns.services api username and password yet." | |
78 | _err "Set environment variables DnsServices_Username and DnsServices_Password" | |
79 | return 1 | |
80 | fi | |
81 | ||
82 | # Setup GET/POST/DELETE headers | |
83 | _setup_headers | |
84 | ||
85 | if ! _get_root "${fulldomain}"; then | |
86 | _err "Invalid domain ${fulldomain}" | |
87 | return 1 | |
88 | fi | |
89 | ||
90 | _debug2 rm_rootDomainInfo "found root domain $rootZoneName for $fulldomain" | |
91 | ||
92 | if ! deleteRecord "${fulldomain}" "${txtvalue}"; then | |
93 | _err "Error removing record: $fulldomain TXT ${txtvalue}" | |
94 | return 1 | |
95 | fi | |
96 | ||
97 | return 0 | |
688a2341 BB |
98 | } |
99 | ||
100 | #################### Private functions below ################################## | |
101 | ||
102 | _setup_headers() { | |
56a686d3 BB |
103 | # Set up API Headers for _get() and _post() |
104 | # The <function>_add or <function>_rm must have been called before to work | |
688a2341 | 105 | |
56a686d3 BB |
106 | if [ -z "$DnsServices_Username" ] || [ -z "$DnsServices_Password" ]; then |
107 | _err "Could not setup BASIC authentication headers, they are missing" | |
108 | return 1 | |
109 | fi | |
688a2341 | 110 | |
56a686d3 BB |
111 | DnsServiceCredentials="$(printf "%s" "$DnsServices_Username:$DnsServices_Password" | _base64)" |
112 | export _H1="Authorization: Basic $DnsServiceCredentials" | |
113 | export _H2="Content-Type: application/json" | |
688a2341 | 114 | |
56a686d3 BB |
115 | # Just return if headers are set |
116 | return 0 | |
688a2341 BB |
117 | } |
118 | ||
119 | _get_root() { | |
5c00afc6 | 120 | domain="$1" |
56a686d3 BB |
121 | _debug2 _get_root "Get the root domain of ${domain} for DNS API" |
122 | ||
123 | # Setup _get() and _post() headers | |
124 | #_setup_headers | |
125 | ||
126 | result=$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/dns") | |
5c00afc6 BB |
127 | result2="$(printf "%s\n" "$result" | tr '[' '\n' | grep '"name"')" |
128 | result3="$(printf "%s\n" "$result2" | tr '}' '\n' | grep '"name"' | sed "s,^\,,,g" | sed "s,$,},g")" | |
129 | useResult="" | |
56a686d3 BB |
130 | _debug2 _get_root "Got the following root domain(s) $result" |
131 | _debug2 _get_root "- JSON: $result" | |
132 | ||
5c00afc6 | 133 | if [ "$(printf "%s\n" "$result" | tr '}' '\n' | grep -c '"name"')" -gt "1" ]; then |
56a686d3 BB |
134 | checkMultiZones="true" |
135 | _debug2 _get_root "- multiple zones found" | |
136 | else | |
137 | checkMultiZones="false" | |
5c00afc6 | 138 | _debug2 _get_root "- single zone found" |
56a686d3 BB |
139 | fi |
140 | ||
141 | # Find/isolate the root zone to work with in createRecord() and deleteRecord() | |
142 | rootZone="" | |
143 | if [ "$checkMultiZones" = "true" ]; then | |
5c00afc6 BB |
144 | #rootZone=$(for x in $(printf "%s" "${result3}" | tr ',' '\n' | sed -n 's/.*"name":"\(.*\)",.*/\1/p'); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done) |
145 | rootZone=$(for x in $(printf "%s\n" "${result3}" | tr ',' '\n' | grep name | cut -d'"' -f4); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done) | |
146 | if [ "$rootZone" != "" ]; then | |
147 | _debug2 _rootZone "- root zone for $domain is $rootZone" | |
148 | else | |
149 | _err "Could not find root zone for $domain, is it correctly typed?" | |
150 | return 1 | |
151 | fi | |
56a686d3 | 152 | else |
5c00afc6 | 153 | rootZone=$(echo "$result" | tr '}' '\n' | _egrep_o '"name":"[^"]*' | cut -d'"' -f4) |
56a686d3 BB |
154 | _debug2 _get_root "- only found 1 domain in API: $rootZone" |
155 | fi | |
156 | ||
157 | if [ -z "$rootZone" ]; then | |
158 | _err "Could not find root domain for $domain - is it correctly typed?" | |
159 | return 1 | |
160 | fi | |
161 | ||
5c00afc6 BB |
162 | # Make sure we use the correct API zone data |
163 | useResult="$(printf "%s\n" "${result3}" tr ',' '\n' | grep "$rootZone")" | |
164 | _debug2 _useResult "useResult=$useResult" | |
165 | ||
a364ab4e | 166 | # Setup variables used by other functions to communicate with DNS.Services API |
5c00afc6 BB |
167 | #zoneInfo=$(printf "%s\n" "$useResult" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g') |
168 | zoneInfo=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep '"name"' | cut -d'"' -f4) | |
56a686d3 | 169 | rootZoneName="$rootZone" |
5c00afc6 BB |
170 | subDomainName="$(printf "%s\n" "$domain" | sed "s,\.$rootZone,,g")" |
171 | subDomainNameClean="$(printf "%s\n" "$domain" | sed "s,_acme-challenge.,,g")" | |
172 | rootZoneDomainID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep domain_id | cut -d'"' -f4) | |
173 | rootZoneServiceID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep service_id | cut -d'"' -f4) | |
56a686d3 | 174 | |
ae71a5ab | 175 | _debug2 _zoneInfo "Zone info from API : $zoneInfo" |
56a686d3 BB |
176 | _debug2 _get_root "Root zone name : $rootZoneName" |
177 | _debug2 _get_root "Root zone domain ID : $rootZoneDomainID" | |
178 | _debug2 _get_root "Root zone service ID: $rootZoneServiceID" | |
179 | _debug2 _get_root "Sub domain : $subDomainName" | |
180 | ||
181 | _debug _get_root "Found valid root domain $rootZone for $subDomainNameClean" | |
182 | return 0 | |
688a2341 BB |
183 | } |
184 | ||
185 | createRecord() { | |
5c00afc6 | 186 | fulldomain="$1" |
56a686d3 | 187 | txtvalue="$2" |
04ca808e | 188 | |
a364ab4e | 189 | # Get root domain information - needed for DNS.Services API communication |
56a686d3 BB |
190 | if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then |
191 | _get_root "$fulldomain" | |
192 | fi | |
5c00afc6 BB |
193 | if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then |
194 | _err "Something happend - could not get the API zone information" | |
195 | return 1 | |
196 | fi | |
04ca808e | 197 | |
56a686d3 | 198 | _debug2 createRecord "CNAME TXT value is: $txtvalue" |
04ca808e | 199 | |
56a686d3 BB |
200 | # Prepare data to send to API |
201 | data="{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"${txtvalue}\", \"ttl\":\"10\"}" | |
04ca808e | 202 | |
56a686d3 BB |
203 | _debug2 createRecord "data to API: $data" |
204 | result=$(_post "$data" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records" "" "POST") | |
205 | _debug2 createRecord "result from API: $result" | |
04ca808e | 206 | |
df199c57 | 207 | if [ "$(echo "$result" | _egrep_o "\"success\":true")" = "" ]; then |
56a686d3 BB |
208 | _err "Failed to create TXT record $fulldomain with content $txtvalue in zone $rootZoneName" |
209 | _err "$result" | |
210 | return 1 | |
211 | fi | |
04ca808e | 212 | |
56a686d3 BB |
213 | _info "Record \"$fulldomain TXT $txtvalue\" has been created" |
214 | return 0 | |
688a2341 BB |
215 | } |
216 | ||
217 | deleteRecord() { | |
5c00afc6 BB |
218 | fulldomain="$1" |
219 | txtvalue="$2" | |
56a686d3 | 220 | |
c1ba4f1b | 221 | _log deleteRecord "Deleting $fulldomain TXT $txtvalue record" |
56a686d3 BB |
222 | |
223 | if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then | |
224 | _get_root "$fulldomain" | |
225 | fi | |
226 | ||
227 | result="$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID")" | |
5c00afc6 BB |
228 | #recordInfo="$(echo "$result" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}")" |
229 | #recordID="$(echo "$recordInfo" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"id":")([^"]*)"(.*)$,\2,g')" | |
230 | recordID="$(printf "%s\n" "$result" | tr '}' '\n' | grep -- "$txtvalue" | tr ',' '\n' | grep '"id"' | cut -d'"' -f4)" | |
231 | _debug2 _recordID "recordID used for deletion of record: $recordID" | |
56a686d3 BB |
232 | |
233 | if [ -z "$recordID" ]; then | |
234 | _info "Record $fulldomain TXT $txtvalue not found or already deleted" | |
235 | return 0 | |
236 | else | |
237 | _debug2 deleteRecord "Found recordID=$recordID" | |
238 | fi | |
239 | ||
240 | _debug2 deleteRecord "DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID" | |
e4387e4a | 241 | _log "curl DELETE request $DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID" |
56a686d3 BB |
242 | result="$(_H1="$_H1" _H2="$_H2" _post "" "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID/records/$recordID" "" "DELETE")" |
243 | _debug2 deleteRecord "API Delete result \"$result\"" | |
e4387e4a | 244 | _log "curl API Delete result \"$result\"" |
56a686d3 BB |
245 | |
246 | # Return OK regardless | |
247 | return 0 | |
688a2341 | 248 | } |