]> git.proxmox.com Git - mirror_acme.sh.git/blame - dnsapi/dns_oci.sh
Merge pull request #4658 from Justman10000/master
[mirror_acme.sh.git] / dnsapi / dns_oci.sh
CommitLineData
6f88c816
AM
1#!/usr/bin/env sh
2#
3# Acme.sh DNS API plugin for Oracle Cloud Infrastructure
4# Copyright (c) 2021, Oracle and/or its affiliates
5#
946c8b49
AM
6# The plugin will automatically use the default profile from an OCI SDK and CLI
7# configuration file, if it exists.
8#
9# Alternatively, set the following environment variables:
017a1018
AM
10# - OCI_CLI_TENANCY : OCID of tenancy that contains the target DNS zone
11# - OCI_CLI_USER : OCID of user with permission to add/remove records from zones
12# - OCI_CLI_REGION : Should point to the tenancy home region
6f88c816 13#
017a1018
AM
14# One of the following two variables is required:
15# - OCI_CLI_KEY_FILE: Path to private API signing key file in PEM format; or
16# - OCI_CLI_KEY : The private API signing key in PEM format
17#
18# NOTE: using an encrypted private key that needs a passphrase is not supported.
6f88c816
AM
19#
20
21dns_oci_add() {
22 _fqdn="$1"
23 _rdata="$2"
24
946c8b49
AM
25 if _get_oci_zone; then
26
27 _add_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"ttl\": 30,\"operation\":\"ADD\"}]}"
28 response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_add_record_body")
29 if [ "$response" ]; then
30 _info "Success: added TXT record for ${_sub_domain}.${_domain}."
ed971df9 31 else
946c8b49
AM
32 _err "Error: failed to add TXT record for ${_sub_domain}.${_domain}."
33 _err "Check that the user has permission to add records to this zone."
ed971df9 34 return 1
6f88c816
AM
35 fi
36
37 else
38 return 1
39 fi
40
41}
42
43dns_oci_rm() {
44 _fqdn="$1"
45 _rdata="$2"
46
946c8b49
AM
47 if _get_oci_zone; then
48
49 _remove_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"operation\":\"REMOVE\"}]}"
50 response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_remove_record_body")
51 if [ "$response" ]; then
52 _info "Success: removed TXT record for ${_sub_domain}.${_domain}."
ed971df9 53 else
946c8b49
AM
54 _err "Error: failed to remove TXT record for ${_sub_domain}.${_domain}."
55 _err "Check that the user has permission to remove records from this zone."
ed971df9 56 return 1
6f88c816
AM
57 fi
58
59 else
60 return 1
61 fi
62
63}
64
65#################### Private functions below ##################################
946c8b49
AM
66_get_oci_zone() {
67
68 if ! _oci_config; then
69 return 1
70 fi
71
72 if ! _get_zone "$_fqdn"; then
73 _err "Error: DNS Zone not found for $_fqdn in $OCI_CLI_TENANCY"
74 return 1
75 fi
76
77 return 0
78
79}
80
6f88c816
AM
81_oci_config() {
82
1d089d45
AM
83 _DEFAULT_OCI_CLI_CONFIG_FILE="$HOME/.oci/config"
84 OCI_CLI_CONFIG_FILE="${OCI_CLI_CONFIG_FILE:-$(_readaccountconf_mutable OCI_CLI_CONFIG_FILE)}"
85
86 if [ -z "$OCI_CLI_CONFIG_FILE" ]; then
87 OCI_CLI_CONFIG_FILE="$_DEFAULT_OCI_CLI_CONFIG_FILE"
946c8b49 88 fi
6f88c816 89
1d089d45
AM
90 if [ "$_DEFAULT_OCI_CLI_CONFIG_FILE" != "$OCI_CLI_CONFIG_FILE" ]; then
91 _saveaccountconf_mutable OCI_CLI_CONFIG_FILE "$OCI_CLI_CONFIG_FILE"
92 else
93 _clearaccountconf_mutable OCI_CLI_CONFIG_FILE
94 fi
6f88c816 95
1d089d45
AM
96 _DEFAULT_OCI_CLI_PROFILE="DEFAULT"
97 OCI_CLI_PROFILE="${OCI_CLI_PROFILE:-$(_readaccountconf_mutable OCI_CLI_PROFILE)}"
98 if [ "$_DEFAULT_OCI_CLI_PROFILE" != "$OCI_CLI_PROFILE" ]; then
99 _saveaccountconf_mutable OCI_CLI_PROFILE "$OCI_CLI_PROFILE"
100 else
101 OCI_CLI_PROFILE="$_DEFAULT_OCI_CLI_PROFILE"
102 _clearaccountconf_mutable OCI_CLI_PROFILE
6f88c816
AM
103 fi
104
1d089d45
AM
105 OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readaccountconf_mutable OCI_CLI_TENANCY)}"
106 if [ "$OCI_CLI_TENANCY" ]; then
107 _saveaccountconf_mutable OCI_CLI_TENANCY "$OCI_CLI_TENANCY"
108 elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
109 _debug "Reading OCI_CLI_TENANCY value from: $OCI_CLI_CONFIG_FILE"
110 OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readini "$OCI_CLI_CONFIG_FILE" tenancy "$OCI_CLI_PROFILE")}"
6f88c816
AM
111 fi
112
017a1018 113 if [ -z "$OCI_CLI_TENANCY" ]; then
1d089d45
AM
114 _err "Error: unable to read OCI_CLI_TENANCY from config file or environment variable."
115 return 1
6f88c816
AM
116 fi
117
1d089d45
AM
118 OCI_CLI_USER="${OCI_CLI_USER:-$(_readaccountconf_mutable OCI_CLI_USER)}"
119 if [ "$OCI_CLI_USER" ]; then
120 _saveaccountconf_mutable OCI_CLI_USER "$OCI_CLI_USER"
121 elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
122 _debug "Reading OCI_CLI_USER value from: $OCI_CLI_CONFIG_FILE"
123 OCI_CLI_USER="${OCI_CLI_USER:-$(_readini "$OCI_CLI_CONFIG_FILE" user "$OCI_CLI_PROFILE")}"
124 fi
017a1018 125 if [ -z "$OCI_CLI_USER" ]; then
1d089d45
AM
126 _err "Error: unable to read OCI_CLI_USER from config file or environment variable."
127 return 1
6f88c816
AM
128 fi
129
1d089d45
AM
130 OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readaccountconf_mutable OCI_CLI_REGION)}"
131 if [ "$OCI_CLI_REGION" ]; then
132 _saveaccountconf_mutable OCI_CLI_REGION "$OCI_CLI_REGION"
133 elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
134 _debug "Reading OCI_CLI_REGION value from: $OCI_CLI_CONFIG_FILE"
135 OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readini "$OCI_CLI_CONFIG_FILE" region "$OCI_CLI_PROFILE")}"
136 fi
017a1018 137 if [ -z "$OCI_CLI_REGION" ]; then
1d089d45
AM
138 _err "Error: unable to read OCI_CLI_REGION from config file or environment variable."
139 return 1
6f88c816
AM
140 fi
141
1d089d45
AM
142 OCI_CLI_KEY="${OCI_CLI_KEY:-$(_readaccountconf_mutable OCI_CLI_KEY)}"
143 if [ -z "$OCI_CLI_KEY" ]; then
144 _clearaccountconf_mutable OCI_CLI_KEY
145 OCI_CLI_KEY_FILE="${OCI_CLI_KEY_FILE:-$(_readini "$OCI_CLI_CONFIG_FILE" key_file "$OCI_CLI_PROFILE")}"
146 if [ "$OCI_CLI_KEY_FILE" ] && [ -f "$OCI_CLI_KEY_FILE" ]; then
147 _debug "Reading OCI_CLI_KEY value from: $OCI_CLI_KEY_FILE"
148 OCI_CLI_KEY=$(_base64 <"$OCI_CLI_KEY_FILE")
946c8b49 149 _saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY"
946c8b49 150 fi
1d089d45
AM
151 else
152 _saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY"
153 fi
154
155 if [ -z "$OCI_CLI_KEY_FILE" ] && [ -z "$OCI_CLI_KEY" ]; then
156 _err "Error: unable to find key file path in OCI config file or OCI_CLI_KEY_FILE."
157 _err "Error: unable to load private API signing key from OCI_CLI_KEY."
158 return 1
6f88c816
AM
159 fi
160
1d089d45 161 if [ "$(printf "%s\n" "$OCI_CLI_KEY" | wc -l)" -eq 1 ]; then
32adc38e 162 OCI_CLI_KEY=$(printf "%s" "$OCI_CLI_KEY" | _dbase64)
6f88c816
AM
163 fi
164
1d089d45 165 return 0
6f88c816
AM
166
167}
168
169# _get_zone(): retrieves the Zone name and OCID
170#
171# _sub_domain=_acme-challenge.www
172# _domain=domain.com
173# _domain_ociid=ocid1.dns-zone.oc1..
174_get_zone() {
175 domain=$1
176 i=1
177 p=1
178
179 while true; do
180 h=$(printf "%s" "$domain" | cut -d . -f $i-100)
181 _debug h "$h"
182 if [ -z "$h" ]; then
183 # not valid
184 return 1
185 fi
186
187 _domain_id=$(_signed_request "GET" "/20180115/zones/$h" "" "id")
188 if [ "$_domain_id" ]; then
189 _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
190 _domain=$h
191
192 _debug _domain_id "$_domain_id"
193 _debug _sub_domain "$_sub_domain"
194 _debug _domain "$_domain"
195 return 0
196 fi
197
198 p=$i
199 i=$(_math "$i" + 1)
200 done
201 return 1
202
203}
204
017a1018
AM
205#Usage: privatekey
206#Output MD5 fingerprint
207_fingerprint() {
1d089d45 208
017a1018
AM
209 pkey="$1"
210 if [ -z "$pkey" ]; then
211 _usage "Usage: _fingerprint privkey"
212 return 1
213 fi
214
215 printf "%s" "$pkey" | ${ACME_OPENSSL_BIN:-openssl} rsa -pubout -outform DER 2>/dev/null | ${ACME_OPENSSL_BIN:-openssl} md5 -c | cut -d = -f 2 | tr -d ' '
216
217}
218
6f88c816
AM
219_signed_request() {
220
221 _sig_method="$1"
222 _sig_target="$2"
223 _sig_body="$3"
224 _return_field="$4"
225
017a1018
AM
226 _key_fingerprint=$(_fingerprint "$OCI_CLI_KEY")
227 _sig_host="dns.$OCI_CLI_REGION.oraclecloud.com"
228 _sig_keyId="$OCI_CLI_TENANCY/$OCI_CLI_USER/$_key_fingerprint"
6f88c816
AM
229 _sig_alg="rsa-sha256"
230 _sig_version="1"
231 _sig_now="$(LC_ALL=C \date -u "+%a, %d %h %Y %H:%M:%S GMT")"
232
6f88c816
AM
233 _request_method=$(printf %s "$_sig_method" | _lower_case)
234 _curl_method=$(printf %s "$_sig_method" | _upper_case)
235
236 _request_target="(request-target): $_request_method $_sig_target"
237 _date_header="date: $_sig_now"
238 _host_header="host: $_sig_host"
239
240 _string_to_sign="$_request_target\n$_date_header\n$_host_header"
241 _sig_headers="(request-target) date host"
242
243 if [ "$_sig_body" ]; then
244 _secure_debug3 _sig_body "$_sig_body"
017a1018 245 _sig_body_sha256="x-content-sha256: $(printf %s "$_sig_body" | _digest sha256)"
6f88c816
AM
246 _sig_body_type="content-type: application/json"
247 _sig_body_length="content-length: ${#_sig_body}"
248 _string_to_sign="$_string_to_sign\n$_sig_body_sha256\n$_sig_body_type\n$_sig_body_length"
249 _sig_headers="$_sig_headers x-content-sha256 content-type content-length"
250 fi
251
252 _tmp_file=$(_mktemp)
253 if [ -f "$_tmp_file" ]; then
017a1018
AM
254 printf '%s' "$OCI_CLI_KEY" >"$_tmp_file"
255 _signature=$(printf '%b' "$_string_to_sign" | _sign "$_tmp_file" sha256 | tr -d '\r\n')
6f88c816
AM
256 rm -f "$_tmp_file"
257 fi
258
259 _signed_header="Authorization: Signature version=\"$_sig_version\",keyId=\"$_sig_keyId\",algorithm=\"$_sig_alg\",headers=\"$_sig_headers\",signature=\"$_signature\""
260 _secure_debug3 _signed_header "$_signed_header"
261
262 if [ "$_curl_method" = "GET" ]; then
263 export _H1="$_date_header"
264 export _H2="$_signed_header"
265 _response="$(_get "https://${_sig_host}${_sig_target}")"
266 elif [ "$_curl_method" = "PATCH" ]; then
267 export _H1="$_date_header"
3a1c6d84 268 # shellcheck disable=SC2090
6f88c816
AM
269 export _H2="$_sig_body_sha256"
270 export _H3="$_sig_body_type"
271 export _H4="$_sig_body_length"
272 export _H5="$_signed_header"
273 _response="$(_post "$_sig_body" "https://${_sig_host}${_sig_target}" "" "PATCH")"
274 else
275 _err "Unable to process method: $_curl_method."
276 fi
277
278 _ret="$?"
279 if [ "$_return_field" ]; then
280 _response="$(echo "$_response" | sed 's/\\\"//g'))"
281 _return=$(echo "${_response}" | _egrep_o "\"$_return_field\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")
282 else
283 _return="$_response"
284 fi
285
286 printf "%s" "$_return"
287 return $_ret
288
289}
946c8b49
AM
290
291# file key [section]
76660228 292_readini() {
1d089d45
AM
293 _file="$1"
294 _key="$2"
76660228 295 _section="${3:-DEFAULT}"
946c8b49 296
76660228 297 _start_n=$(grep -n '\['"$_section"']' "$_file" | cut -d : -f 1)
1d089d45 298 _debug3 _start_n "$_start_n"
946c8b49 299 if [ -z "$_start_n" ]; then
76660228 300 _err "Can not find section: $_section"
946c8b49
AM
301 return 1
302 fi
303
304 _start_nn=$(_math "$_start_n" + 1)
1d089d45 305 _debug3 "_start_nn" "$_start_nn"
946c8b49 306
76660228 307 _left="$(sed -n "${_start_nn},99999p" "$_file")"
1d089d45 308 _debug3 _left "$_left"
946c8b49 309 _end="$(echo "$_left" | grep -n "^\[" | _head_n 1)"
1d089d45 310 _debug3 "_end" "$_end"
946c8b49
AM
311 if [ "$_end" ]; then
312 _end_n=$(echo "$_end" | cut -d : -f 1)
1d089d45 313 _debug3 "_end_n" "$_end_n"
946c8b49
AM
314 _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
315 else
316 _seg_n="$_left"
317 fi
318
1d089d45 319 _debug3 "_seg_n" "$_seg_n"
946c8b49 320 _lineini="$(echo "$_seg_n" | grep "^ *$_key *= *")"
1d089d45
AM
321 _inivalue="$(printf "%b" "$(eval "echo $_lineini | sed \"s/^ *${_key} *= *//g\"")")"
322 _debug2 _inivalue "$_inivalue"
323 echo "$_inivalue"
946c8b49 324
946c8b49 325}