3 #YC_Zone_ID="" # DNS Zone ID
4 #YC_Folder_ID="" # YC Folder ID
5 #YC_SA_ID="" # Service Account ID
6 #YC_SA_Key_ID="" # Service Account IAM Key ID
7 #YC_SA_Key_File_Path="/path/to/private.key" # Path to private.key use instead of YC_SA_Key_File_PEM_b64
8 #YC_SA_Key_File_PEM_b64="" # Base64 content of private.key use instead of YC_SA_Key_File_Path
9 YC_Api
="https://dns.api.cloud.yandex.net/dns/v1"
11 ######## Public functions #####################
13 #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
15 fulldomain
="$(echo "$1". | _lower_case)" # Add dot at end of domain name
18 YC_SA_Key_File_PEM_b64
="${YC_SA_Key_File_PEM_b64:-$(_readaccountconf_mutable YC_SA_Key_File_PEM_b64)}"
19 YC_SA_Key_File_Path
="${YC_SA_Key_File_Path:-$(_readaccountconf_mutable YC_SA_Key_File_Path)}"
21 if [ "$YC_SA_Key_File_PEM_b64" ]; then
22 echo "$YC_SA_Key_File_PEM_b64" | _dbase64
>private.key
23 YC_SA_Key_File
="private.key"
24 _savedomainconf YC_SA_Key_File_PEM_b64
"$YC_SA_Key_File_PEM_b64"
26 YC_SA_Key_File
="$YC_SA_Key_File_Path"
27 _savedomainconf YC_SA_Key_File_Path
"$YC_SA_Key_File_Path"
30 YC_Zone_ID
="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
31 YC_Folder_ID
="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
32 YC_SA_ID
="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
33 YC_SA_Key_ID
="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
35 if [ "$YC_SA_ID" ] && [ "$YC_SA_Key_ID" ] && [ "$YC_SA_Key_File" ]; then
36 if [ -f "$YC_SA_Key_File" ]; then
37 if _isRSA
"$YC_SA_Key_File" >/dev
/null
2>&1; then
38 if [ "$YC_Zone_ID" ]; then
39 _savedomainconf YC_Zone_ID
"$YC_Zone_ID"
40 _savedomainconf YC_SA_ID
"$YC_SA_ID"
41 _savedomainconf YC_SA_Key_ID
"$YC_SA_Key_ID"
42 elif [ "$YC_Folder_ID" ]; then
43 _savedomainconf YC_Folder_ID
"$YC_Folder_ID"
44 _saveaccountconf_mutable YC_SA_ID
"$YC_SA_ID"
45 _saveaccountconf_mutable YC_SA_Key_ID
"$YC_SA_Key_ID"
46 _clearaccountconf_mutable YC_Zone_ID
47 _clearaccountconf YC_Zone_ID
49 _err
"You didn't specify a Yandex Cloud Zone ID or Folder ID yet."
53 _err
"YC_SA_Key_File not a RSA file(_isRSA function return false)."
57 _err
"YC_SA_Key_File not found in path $YC_SA_Key_File."
61 _clearaccountconf YC_Zone_ID
62 _clearaccountconf YC_Folder_ID
63 _clearaccountconf YC_SA_ID
64 _clearaccountconf YC_SA_Key_ID
65 _clearaccountconf YC_SA_Key_File_PEM_b64
66 _clearaccountconf YC_SA_Key_File_Path
67 _err
"You didn't specify a YC_SA_ID or YC_SA_Key_ID or YC_SA_Key_File."
71 _debug
"First detect the root zone"
72 if ! _get_root
"$fulldomain"; then
76 _debug _domain_id
"$_domain_id"
77 _debug _sub_domain
"$_sub_domain"
78 _debug _domain
"$_domain"
80 _debug
"Getting txt records"
81 if ! _yc_rest GET
"zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
82 _err
"Error: $response"
87 if _yc_rest POST
"zones/$_domain_id:upsertRecordSets" "{\"merges\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
88 if _contains
"$response" "\"done\": true"; then
92 _err
"Add txt record error."
96 _err
"Add txt record error."
103 fulldomain
="$(echo "$1". | _lower_case)" # Add dot at end of domain name
106 YC_Zone_ID
="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
107 YC_Folder_ID
="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
108 YC_SA_ID
="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
109 YC_SA_Key_ID
="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
111 _debug
"First detect the root zone"
112 if ! _get_root
"$fulldomain"; then
113 _err
"invalid domain"
116 _debug _domain_id
"$_domain_id"
117 _debug _sub_domain
"$_sub_domain"
118 _debug _domain
"$_domain"
120 _debug
"Getting txt records"
121 if _yc_rest GET
"zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
122 exists_txtvalue
=$
(echo "$response" | _normalizeJson | _egrep_o
"\"data\".*\][^,]*" | _egrep_o
"[^:]*$")
123 _debug exists_txtvalue
"$exists_txtvalue"
125 _err
"Error: $response"
129 if _yc_rest POST
"zones/$_domain_id:updateRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":$exists_txtvalue}]}"; then
130 if _contains
"$response" "\"done\": true"; then
134 _err
"Delete record error."
138 _err
"Delete record error."
142 #################### Private functions below ##################################
143 #_acme-challenge.www.domain.com
145 # _sub_domain=_acme-challenge.www
147 # _domain_id=sdjkglgdfewsdfg
153 # Use Zone ID directly if provided
154 if [ "$YC_Zone_ID" ]; then
155 if ! _yc_rest GET
"zones/$YC_Zone_ID"; then
158 if echo "$response" |
tr -d " " | _egrep_o
"\"id\":\"$YC_Zone_ID\"" >/dev
/null
; then
159 _domain
=$
(echo "$response" | _egrep_o
"\"zone\": *\"[^\"]*\"" | cut
-d : -f 2 |
tr -d \" | _head_n
1 |
tr -d " ")
160 if [ "$_domain" ]; then
161 _cutlength
=$
((${#domain} - ${#_domain}))
162 _sub_domain
=$
(printf "%s" "$domain" | cut
-c "1-$_cutlength")
163 _domain_id
=$YC_Zone_ID
175 h
=$
(printf "%s" "$domain" | cut
-d .
-f $i-100)
181 if [ "$YC_Folder_ID" ]; then
182 if ! _yc_rest GET
"zones?folderId=$YC_Folder_ID"; then
186 echo "You didn't specify a Yandex Cloud Folder ID."
189 if _contains
"$response" "\"zone\": \"$h\""; then
190 _domain_id
=$
(echo "$response" | _normalizeJson | _egrep_o
"[^{]*\"zone\":\"$h\"[^}]*" | _egrep_o
"\"id\"[^,]*" | _egrep_o
"[^:]*$" |
tr -d '"')
191 _debug _domain_id
"$_domain_id"
192 if [ "$_domain_id" ]; then
193 _sub_domain
=$
(printf "%s" "$domain" | cut
-d .
-f 1-$p)
211 if [ ! "$YC_Token" ]; then
215 _debug
"Token already exists. Skip Login."
218 token_trimmed
=$
(echo "$YC_Token" |
tr -d '"')
220 export _H1
="Content-Type: application/json"
221 export _H2
="Authorization: Bearer $token_trimmed"
223 if [ "$m" != "GET" ]; then
225 response
="$(_post "$data" "$YC_Api/$ep" "" "$m")"
227 response
="$(_get "$YC_Api/$ep")"
230 if [ "$?" != "0" ]; then
234 _debug2 response
"$response"
239 header
=$
(echo "{\"typ\":\"JWT\",\"alg\":\"PS256\",\"kid\":\"$YC_SA_Key_ID\"}" | _normalizeJson | _base64 | _url_replace
)
240 _debug header
"$header"
242 _current_timestamp
=$
(_time
)
243 _expire_timestamp
=$
(_math
"$_current_timestamp" + 1200) # 20 minutes
244 payload
=$
(echo "{\"iss\":\"$YC_SA_ID\",\"aud\":\"https://iam.api.cloud.yandex.net/iam/v1/tokens\",\"iat\":$_current_timestamp,\"exp\":$_expire_timestamp}" | _normalizeJson | _base64 | _url_replace
)
245 _debug payload
"$payload"
247 #signature=$(printf "%s.%s" "$header" "$payload" | ${ACME_OPENSSL_BIN:-openssl} dgst -sign "$YC_SA_Key_File -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _base64 | _url_replace )
248 _signature
=$
(printf "%s.%s" "$header" "$payload" | _sign
"$YC_SA_Key_File" "sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _url_replace
)
249 _debug2 _signature
"$_signature"
251 rm -rf "$YC_SA_Key_File"
253 _jwt
=$
(printf "{\"jwt\": \"%s.%s.%s\"}" "$header" "$payload" "$_signature")
256 export _H1
="Content-Type: application/json"
257 _iam_response
="$(_post "$_jwt" "https
://iam.api.cloud.yandex.net
/iam
/v
1/tokens
" "" "POST
")"
258 _debug3 _iam_response
"$(echo "$_iam_response" | _normalizeJson)"
260 YC_Token
="$(echo "$_iam_response" | _normalizeJson | _egrep_o "\"iamToken
\"[^
,]*" | _egrep_o "[^
:]*$
" | tr -d '"')"