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 PEM
8 #YC_SA_Key_File_PEM="" # Content of private.key use instead of 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 if ["$YC_SA_Key_File_PEM"]; then
19 YC_SA_Key_File
="<(echo '$YC_SA_Key_File_PEM')"
21 YC_SA_Key_File
=$YC_SA_Key_File_Path
24 YC_Zone_ID
="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
25 YC_Folder_ID
="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
26 YC_SA_ID
="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
27 YC_SA_Key_ID
="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
28 YC_SA_Key_File
="${YC_SA_Key_File:-$(_readaccountconf_mutable YC_SA_Key_File)}"
30 if [ "$YC_SA_ID" ] && [ "$YC_SA_Key_ID" ] && [ "$YC_SA_Key_File" ]; then
31 if [ -f "$YC_SA_Key_File" ]; then
32 if _isRSA
"$YC_SA_Key_File" >/dev
/null
2>&1; then
33 if [ "$YC_Zone_ID" ]; then
34 _savedomainconf YC_Zone_ID
"$YC_Zone_ID"
35 _savedomainconf YC_SA_ID
"$YC_SA_ID"
36 _savedomainconf YC_SA_Key_ID
"$YC_SA_Key_ID"
37 _savedomainconf YC_SA_Key_File
"$YC_SA_Key_File"
38 elif [ "$YC_Folder_ID" ]; then
39 _savedomainconf YC_Folder_ID
"$YC_Folder_ID"
40 _saveaccountconf_mutable YC_SA_ID
"$YC_SA_ID"
41 _saveaccountconf_mutable YC_SA_Key_ID
"$YC_SA_Key_ID"
42 _saveaccountconf_mutable YC_SA_Key_File
"$YC_SA_Key_File"
43 _clearaccountconf_mutable YC_Zone_ID
44 _clearaccountconf YC_Zone_ID
46 _err
"You didn't specify a Yandex Cloud Zone ID or Folder ID yet."
50 _err
"YC_SA_Key_File not a RSA file(_isRSA function return false)."
54 _err
"YC_SA_Key_File not found in path $YC_SA_Key_File."
58 _clearaccountconf YC_Zone_ID
59 _clearaccountconf YC_Folder_ID
60 _clearaccountconf YC_SA_ID
61 _clearaccountconf YC_SA_Key_ID
62 _clearaccountconf YC_SA_Key_File
63 _err
"You didn't specify a YC_SA_ID or YC_SA_Key_ID or YC_SA_Key_File."
67 _debug
"First detect the root zone"
68 if ! _get_root
"$fulldomain"; then
72 _debug _domain_id
"$_domain_id"
73 _debug _sub_domain
"$_sub_domain"
74 _debug _domain
"$_domain"
76 _debug
"Getting txt records"
77 if ! _yc_rest GET
"zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
78 _err
"Error: $response"
83 if _yc_rest POST
"zones/$_domain_id:upsertRecordSets" "{\"merges\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
84 if _contains
"$response" "\"done\": true"; then
88 _err
"Add txt record error."
92 _err
"Add txt record error."
99 fulldomain
="$(echo "$1". | _lower_case)" # Add dot at end of domain name
102 YC_Zone_ID
="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
103 YC_Folder_ID
="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
104 YC_SA_ID
="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
105 YC_SA_Key_ID
="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
106 YC_SA_Key_File
="${YC_SA_Key_File:-$(_readaccountconf_mutable YC_SA_Key_File)}"
108 _debug
"First detect the root zone"
109 if ! _get_root
"$fulldomain"; then
110 _err
"invalid domain"
113 _debug _domain_id
"$_domain_id"
114 _debug _sub_domain
"$_sub_domain"
115 _debug _domain
"$_domain"
117 _debug
"Getting txt records"
118 if _yc_rest GET
"zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
119 exists_txtvalue
=$
(echo "$response" | _normalizeJson | _egrep_o
"\"data\".*\][^,]*" | _egrep_o
"[^:]*$")
120 _debug exists_txtvalue
"$exists_txtvalue"
122 _err
"Error: $response"
126 if _yc_rest POST
"zones/$_domain_id:updateRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":$exists_txtvalue}]}"; then
127 if _contains
"$response" "\"done\": true"; then
131 _err
"Delete record error."
135 _err
"Delete record error."
139 #################### Private functions below ##################################
140 #_acme-challenge.www.domain.com
142 # _sub_domain=_acme-challenge.www
144 # _domain_id=sdjkglgdfewsdfg
150 # Use Zone ID directly if provided
151 if [ "$YC_Zone_ID" ]; then
152 if ! _yc_rest GET
"zones/$YC_Zone_ID"; then
155 if echo "$response" |
tr -d " " |
grep \"id
\":\"$YC_Zone_ID\" >/dev
/null
; then
156 _domain
=$
(echo "$response" | _egrep_o
"\"zone\": *\"[^\"]*\"" | cut
-d : -f 2 |
tr -d \" | _head_n
1 |
tr -d " ")
157 if [ "$_domain" ]; then
158 _cutlength
=$
((${#domain} - ${#_domain}))
159 _sub_domain
=$
(printf "%s" "$domain" | cut
-c "1-$_cutlength")
160 _domain_id
=$YC_Zone_ID
172 h
=$
(printf "%s" "$domain" | cut
-d .
-f $i-100)
178 if [ "$YC_Folder_ID" ]; then
179 if ! _yc_rest GET
"zones?folderId=$YC_Folder_ID"; then
183 echo "You didn't specify a Yandex Cloud Folder ID."
186 if _contains
"$response" "\"zone\": \"$h\""; then
187 _domain_id
=$
(echo "$response" | _normalizeJson | _egrep_o
"[^{]*\"zone\":\"$h\"[^}]*" | _egrep_o
"\"id\"[^,]*" | _egrep_o
"[^:]*$" |
tr -d '"')
188 _debug _domain_id
"$_domain_id"
189 if [ "$_domain_id" ]; then
190 _sub_domain
=$
(printf "%s" "$domain" | cut
-d .
-f 1-$p)
208 if [ ! "$YC_Token" ]; then
212 _debug
"Token already exists. Skip Login."
215 token_trimmed
=$
(echo "$YC_Token" |
tr -d '"')
217 export _H1
="Content-Type: application/json"
218 export _H2
="Authorization: Bearer $token_trimmed"
220 if [ "$m" != "GET" ]; then
222 response
="$(_post "$data" "$YC_Api/$ep" "" "$m")"
224 response
="$(_get "$YC_Api/$ep")"
227 if [ "$?" != "0" ]; then
231 _debug2 response
"$response"
236 header
=$
(echo "{\"typ\":\"JWT\",\"alg\":\"PS256\",\"kid\":\"$YC_SA_Key_ID\"}" | _normalizeJson | _base64 | _url_replace
)
237 _debug header
"$header"
239 _current_timestamp
=$
(_time
)
240 _expire_timestamp
=$
(_math
$_current_timestamp + 1200) # 20 minutes
241 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
)
242 _debug payload
"$payload"
244 #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 )
245 _signature
=$
(printf "%s.%s" "$header" "$payload" | _sign
"$YC_SA_Key_File" "sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _url_replace
)
246 _debug2 _signature
"$_signature"
248 _jwt
=$
(printf "{\"jwt\": \"%s.%s.%s\"}" "$header" "$payload" "$_signature")
251 export _H1
="Content-Type: application/json"
252 _iam_response
="$(_post "$_jwt" "https
://iam.api.cloud.yandex.net
/iam
/v
1/tokens
" "" "POST
")"
253 _debug3 _iam_response
"$(echo "$_iam_response" | _normalizeJson)"
255 YC_Token
="$(echo "$_iam_response" | _normalizeJson | _egrep_o "\"iamToken
\"[^
,]*" | _egrep_o "[^
:]*$
" | tr -d '"')"