3 # Supports IONOS DNS API Beta v1.0.0
6 # Export IONOS_PREFIX and IONOS_SECRET before calling acme.sh:
8 # $ export IONOS_PREFIX="..."
9 # $ export IONOS_SECRET="..."
11 # $ acme.sh --issue --dns dns_ionos ...
13 IONOS_API
="https://api.hosting.ionos.com/dns"
14 IONOS_ROUTE_ZONES
="/v1/zones"
16 IONOS_TXT_TTL
=60 # minimum accepted by API
23 if ! _ionos_init
; then
27 _new_record
="{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}"
29 # As no POST route is supported by the API, check for existing records and include them in the PATCH request in order not delete them.
30 # This is required to support ACME v2 wildcard certificate creation, where two TXT records for the same domain name are created.
32 _ionos_get_existing_records
"$fulldomain" "$_zone_id"
34 if [ "$_existing_records" ]; then
35 _body
="[$_new_record,$_existing_records]"
37 _body
="[$_new_record]"
40 if _ionos_rest PATCH
"$IONOS_ROUTE_ZONES/$_zone_id" "$_body" && [ -z "$response" ]; then
41 _info
"TXT record has been created successfully."
52 if ! _ionos_init
; then
56 if ! _ionos_get_record
"$fulldomain" "$_zone_id" "$txtvalue"; then
57 _err
"Could not find _acme-challenge TXT record."
61 if _ionos_rest DELETE
"$IONOS_ROUTE_ZONES/$_zone_id/records/$_record_id" && [ -z "$response" ]; then
62 _info
"TXT record has been deleted successfully."
70 IONOS_PREFIX
="${IONOS_PREFIX:-$(_readaccountconf_mutable IONOS_PREFIX)}"
71 IONOS_SECRET
="${IONOS_SECRET:-$(_readaccountconf_mutable IONOS_SECRET)}"
73 if [ -z "$IONOS_PREFIX" ] ||
[ -z "$IONOS_SECRET" ]; then
74 _err
"You didn't specify an IONOS api prefix and secret yet."
75 _err
"Read https://beta.developer.hosting.ionos.de/docs/getstarted to learn how to get a prefix and secret."
77 _err
"Then set them before calling acme.sh:"
78 _err
"\$ export IONOS_PREFIX=\"...\""
79 _err
"\$ export IONOS_SECRET=\"...\""
80 _err
"\$ acme.sh --issue -d ... --dns dns_ionos"
84 _saveaccountconf_mutable IONOS_PREFIX
"$IONOS_PREFIX"
85 _saveaccountconf_mutable IONOS_SECRET
"$IONOS_SECRET"
87 if ! _get_root
"$fulldomain"; then
88 _err
"Cannot find this domain in your IONOS account."
98 if _ionos_rest GET
"$IONOS_ROUTE_ZONES"; then
99 response
="$(echo "$response" | tr -d "\n")"
102 h
=$
(printf "%s" "$domain" | cut
-d .
-f $i-100)
107 _zone
="$(echo "$response" | _egrep_o "\"name
\":\"$h\".
*\
}")"
108 if [ "$_zone" ]; then
109 _zone_id
=$
(printf "%s\n" "$_zone" | _egrep_o
"\"id\":\"[a-fA-F0-9\-]*\"" | _head_n
1 | cut
-d : -f 2 |
tr -d '\"')
110 if [ "$_zone_id" ]; then
111 _sub_domain
=$
(printf "%s" "$domain" | cut
-d .
-f 1-$p)
128 _ionos_get_existing_records
() {
132 if _ionos_rest GET
"$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then
133 response
="$(echo "$response" | tr -d "\n")"
135 _existing_records
="$(printf "%s
\n" "$response" | _egrep_o "\"records
\":\
[.
*\
]" | _head_n 1 | cut -d '[' -f 2 | sed 's/]//')"
139 _ionos_get_record
() {
144 if _ionos_rest GET
"$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then
145 response
="$(echo "$response" | tr -d "\n")"
147 _record
="$(echo "$response" | _egrep_o "\"name
\":\"$fulldomain\"[^\
}]*\"type\":\"TXT
\"[^\
}]*\"content
\":\"\\\\\"$txtrecord\\\\\"\".
*\
}")"
148 if [ "$_record" ]; then
149 _record_id
=$
(printf "%s\n" "$_record" | _egrep_o
"\"id\":\"[a-fA-F0-9\-]*\"" | _head_n
1 | cut
-d : -f 2 |
tr -d '\"')
163 IONOS_API_KEY
="$(printf "%s.
%s
" "$IONOS_PREFIX" "$IONOS_SECRET")"
165 export _H1
="X-API-Key: $IONOS_API_KEY"
167 if [ "$method" != "GET" ]; then
168 export _H2
="Accept: application/json"
169 export _H3
="Content-Type: application/json"
171 response
="$(_post "$data" "$IONOS_API$route" "" "$method")"
173 export _H2
="Accept: */*"
175 response
="$(_get "$IONOS_API$route")"
178 if [ "$?" != "0" ]; then