3 # Acme.sh DNS API plugin for Oracle Cloud Infrastructure
4 # Copyright (c) 2021, Oracle and/or its affiliates
6 # The plugin will automatically use the default profile from an OCI SDK and CLI
7 # configuration file, if it exists.
9 # Alternatively, set the following environment variables:
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
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
18 # NOTE: using an encrypted private key that needs a passphrase is not supported.
25 if _get_oci_zone
; then
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}."
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."
47 if _get_oci_zone
; then
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}."
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."
65 #################### Private functions below ##################################
68 if ! _oci_config
; then
72 if ! _get_zone
"$_fqdn"; then
73 _err
"Error: DNS Zone not found for $_fqdn in $OCI_CLI_TENANCY"
83 _DEFAULT_OCI_CLI_CONFIG_FILE
="$HOME/.oci/config"
84 OCI_CLI_CONFIG_FILE
="${OCI_CLI_CONFIG_FILE:-$(_readaccountconf_mutable OCI_CLI_CONFIG_FILE)}"
86 if [ -z "$OCI_CLI_CONFIG_FILE" ]; then
87 OCI_CLI_CONFIG_FILE
="$_DEFAULT_OCI_CLI_CONFIG_FILE"
90 if [ "$_DEFAULT_OCI_CLI_CONFIG_FILE" != "$OCI_CLI_CONFIG_FILE" ]; then
91 _saveaccountconf_mutable OCI_CLI_CONFIG_FILE
"$OCI_CLI_CONFIG_FILE"
93 _clearaccountconf_mutable OCI_CLI_CONFIG_FILE
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"
101 OCI_CLI_PROFILE
="$_DEFAULT_OCI_CLI_PROFILE"
102 _clearaccountconf_mutable OCI_CLI_PROFILE
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")}"
113 if [ -z "$OCI_CLI_TENANCY" ]; then
114 _err
"Error: unable to read OCI_CLI_TENANCY from config file or environment variable."
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")}"
125 if [ -z "$OCI_CLI_USER" ]; then
126 _err
"Error: unable to read OCI_CLI_USER from config file or environment variable."
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")}"
137 if [ -z "$OCI_CLI_REGION" ]; then
138 _err
"Error: unable to read OCI_CLI_REGION from config file or environment variable."
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")
149 _saveaccountconf_mutable OCI_CLI_KEY
"$OCI_CLI_KEY"
152 _saveaccountconf_mutable OCI_CLI_KEY
"$OCI_CLI_KEY"
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."
161 if [ "$(printf "%s
\n" "$OCI_CLI_KEY" | wc -l)" -eq 1 ]; then
162 OCI_CLI_KEY
=$
(printf "%s" "$OCI_CLI_KEY" | _dbase64
)
169 # _get_zone(): retrieves the Zone name and OCID
171 # _sub_domain=_acme-challenge.www
173 # _domain_ociid=ocid1.dns-zone.oc1..
180 h
=$
(printf "%s" "$domain" | cut
-d .
-f $i-100)
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)
192 _debug _domain_id
"$_domain_id"
193 _debug _sub_domain
"$_sub_domain"
194 _debug _domain
"$_domain"
206 #Output MD5 fingerprint
210 if [ -z "$pkey" ]; then
211 _usage
"Usage: _fingerprint privkey"
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 ' '
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"
229 _sig_alg
="rsa-sha256"
231 _sig_now
="$(LC_ALL=C \date -u "+%a
, %d
%h
%Y
%H
:%M
:%S GMT
")"
233 _request_method
=$
(printf %s
"$_sig_method" | _lower_case
)
234 _curl_method
=$
(printf %s
"$_sig_method" | _upper_case
)
236 _request_target
="(request-target): $_request_method $_sig_target"
237 _date_header
="date: $_sig_now"
238 _host_header
="host: $_sig_host"
240 _string_to_sign
="$_request_target\n$_date_header\n$_host_header"
241 _sig_headers
="(request-target) date host"
243 if [ "$_sig_body" ]; then
244 _secure_debug3 _sig_body
"$_sig_body"
245 _sig_body_sha256
="x-content-sha256: $(printf %s "$_sig_body" | _digest sha256)"
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"
253 if [ -f "$_tmp_file" ]; then
254 printf '%s' "$OCI_CLI_KEY" >"$_tmp_file"
255 _signature
=$
(printf '%b' "$_string_to_sign" | _sign
"$_tmp_file" sha256 |
tr -d '\r\n')
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"
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"
268 # shellcheck disable=SC2090
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
")"
275 _err
"Unable to process method: $_curl_method."
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 "\"")
286 printf "%s" "$_return"
295 _section
="${3:-DEFAULT}"
297 _start_n
=$
(grep -n '\['"$_section"']' "$_file" | cut
-d : -f 1)
298 _debug3 _start_n
"$_start_n"
299 if [ -z "$_start_n" ]; then
300 _err
"Can not find section: $_section"
304 _start_nn
=$
(_math
"$_start_n" + 1)
305 _debug3
"_start_nn" "$_start_nn"
307 _left
="$(sed -n "${_start_nn},99999p
" "$_file")"
308 _debug3 _left
"$_left"
309 _end
="$(echo "$_left" | grep -n "^\
[" | _head_n 1)"
310 _debug3
"_end" "$_end"
312 _end_n
=$
(echo "$_end" | cut
-d : -f 1)
313 _debug3
"_end_n" "$_end_n"
314 _seg_n
=$
(echo "$_left" |
sed -n "1,${_end_n}p")
319 _debug3
"_seg_n" "$_seg_n"
320 _lineini
="$(echo "$_seg_n" | grep "^
*$_key *= *")"
321 _inivalue
="$(printf "%b
" "$
(eval "echo $_lineini | sed \"s/^ *${_key} *= *//g\"")")"
322 _debug2 _inivalue
"$_inivalue"