Eldar Borge
3 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1460 additions and 278 deletions
-
1.github/workflows/DNS.yml
-
15.github/workflows/dockerhub.yml
-
22acme.sh
-
88deploy/ali_cdn.sh
-
88deploy/ali_dcdn.sh
-
6deploy/synology_dsm.sh
-
64deploy/unifi.sh
-
133dnsapi/dns_ali.sh
-
184dnsapi/dns_alviy.sh
-
3dnsapi/dns_anx.sh
-
3dnsapi/dns_doapi.sh
-
10dnsapi/dns_dynv6.sh
-
2dnsapi/dns_gcore.sh
-
4dnsapi/dns_huaweicloud.sh
-
13dnsapi/dns_inwx.sh
-
2dnsapi/dns_ispconfig.sh
-
9dnsapi/dns_miab.sh
-
16dnsapi/dns_nsupdate.sh
-
4dnsapi/dns_openprovider.sh
-
406dnsapi/dns_timeweb.sh
-
121dnsapi/dns_yandex.sh
-
352dnsapi/dns_yandex360.sh
-
101notify/bark.sh
-
69notify/teams.sh
@ -0,0 +1,88 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# shellcheck disable=SC2034,SC2154 |
||||
|
|
||||
|
# Script to create certificate to Alibaba Cloud CDN |
||||
|
# |
||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun |
||||
|
# |
||||
|
# This deployment required following variables |
||||
|
# export Ali_Key="ALIACCESSKEY" |
||||
|
# export Ali_Secret="ALISECRETKEY" |
||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi |
||||
|
# |
||||
|
# To specify the CDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates |
||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn.example.com" |
||||
|
# If you have multiple CDN domains using the same certificate, just |
||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn1.example.com cdn2.example.com" |
||||
|
# |
||||
|
# For DCDN, see ali_dcdn deploy hook |
||||
|
|
||||
|
Ali_CDN_API="https://cdn.aliyuncs.com/" |
||||
|
|
||||
|
ali_cdn_deploy() { |
||||
|
_cdomain="$1" |
||||
|
_ckey="$2" |
||||
|
_ccert="$3" |
||||
|
_cca="$4" |
||||
|
_cfullchain="$5" |
||||
|
|
||||
|
_debug _cdomain "$_cdomain" |
||||
|
_debug _ckey "$_ckey" |
||||
|
_debug _ccert "$_ccert" |
||||
|
_debug _cca "$_cca" |
||||
|
_debug _cfullchain "$_cfullchain" |
||||
|
|
||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes |
||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276 |
||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)" |
||||
|
# shellcheck source=/dev/null |
||||
|
if ! . "$dnsapi_ali"; then |
||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_prepare_ali_credentials || return 1 |
||||
|
|
||||
|
_getdeployconf DEPLOY_ALI_CDN_DOMAIN |
||||
|
if [ "$DEPLOY_ALI_CDN_DOMAIN" ]; then |
||||
|
_savedeployconf DEPLOY_ALI_CDN_DOMAIN "$DEPLOY_ALI_CDN_DOMAIN" |
||||
|
else |
||||
|
DEPLOY_ALI_CDN_DOMAIN="$_cdomain" |
||||
|
fi |
||||
|
|
||||
|
# read cert and key files and urlencode both |
||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain") |
||||
|
_key=$(_url_encode upper-hex <"$_ckey") |
||||
|
|
||||
|
_debug2 _cert "$_cert" |
||||
|
_debug2 _key "$_key" |
||||
|
|
||||
|
## update domain ssl config |
||||
|
for domain in $DEPLOY_ALI_CDN_DOMAIN; do |
||||
|
_set_cdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key" |
||||
|
if _ali_rest "Set CDN domain SSL certificate for $domain" "" POST; then |
||||
|
_info "Domain $domain certificate has been deployed successfully" |
||||
|
fi |
||||
|
done |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# domain pub pri |
||||
|
_set_cdn_domain_ssl_certificate_query() { |
||||
|
endpoint=$Ali_CDN_API |
||||
|
query='' |
||||
|
query=$query'AccessKeyId='$Ali_Key |
||||
|
query=$query'&Action=SetCdnDomainSSLCertificate' |
||||
|
query=$query'&CertType=upload' |
||||
|
query=$query'&DomainName='$1 |
||||
|
query=$query'&Format=json' |
||||
|
query=$query'&SSLPri='$3 |
||||
|
query=$query'&SSLProtocol=on' |
||||
|
query=$query'&SSLPub='$2 |
||||
|
query=$query'&SignatureMethod=HMAC-SHA1' |
||||
|
query=$query"&SignatureNonce=$(_ali_nonce)" |
||||
|
query=$query'&SignatureVersion=1.0' |
||||
|
query=$query'&Timestamp='$(_timestamp) |
||||
|
query=$query'&Version=2018-05-10' |
||||
|
} |
@ -0,0 +1,88 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# shellcheck disable=SC2034,SC2154 |
||||
|
|
||||
|
# Script to create certificate to Alibaba Cloud DCDN |
||||
|
# |
||||
|
# Docs: https://github.com/acmesh-official/acme.sh/wiki/deployhooks#33-deploy-your-certificate-to-cdn-or-dcdn-of-alibaba-cloud-aliyun |
||||
|
# |
||||
|
# This deployment required following variables |
||||
|
# export Ali_Key="ALIACCESSKEY" |
||||
|
# export Ali_Secret="ALISECRETKEY" |
||||
|
# The credentials are shared with all the Alibaba Cloud deploy hooks and dnsapi |
||||
|
# |
||||
|
# To specify the DCDN domain that is different from the certificate CN, usually used for multi-domain or wildcard certificates |
||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn.example.com" |
||||
|
# If you have multiple CDN domains using the same certificate, just |
||||
|
# export DEPLOY_ALI_DCDN_DOMAIN="dcdn1.example.com dcdn2.example.com" |
||||
|
# |
||||
|
# For regular CDN, see ali_cdn deploy hook |
||||
|
|
||||
|
Ali_DCDN_API="https://dcdn.aliyuncs.com/" |
||||
|
|
||||
|
ali_dcdn_deploy() { |
||||
|
_cdomain="$1" |
||||
|
_ckey="$2" |
||||
|
_ccert="$3" |
||||
|
_cca="$4" |
||||
|
_cfullchain="$5" |
||||
|
|
||||
|
_debug _cdomain "$_cdomain" |
||||
|
_debug _ckey "$_ckey" |
||||
|
_debug _ccert "$_ccert" |
||||
|
_debug _cca "$_cca" |
||||
|
_debug _cfullchain "$_cfullchain" |
||||
|
|
||||
|
# Load dnsapi/dns_ali.sh to reduce the duplicated codes |
||||
|
# https://github.com/acmesh-official/acme.sh/pull/5205#issuecomment-2357867276 |
||||
|
dnsapi_ali="$(_findHook "$_cdomain" "$_SUB_FOLDER_DNSAPI" dns_ali)" |
||||
|
# shellcheck source=/dev/null |
||||
|
if ! . "$dnsapi_ali"; then |
||||
|
_err "Error loading file $dnsapi_ali. Please check your API file and try again." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_prepare_ali_credentials || return 1 |
||||
|
|
||||
|
_getdeployconf DEPLOY_ALI_DCDN_DOMAIN |
||||
|
if [ "$DEPLOY_ALI_DCDN_DOMAIN" ]; then |
||||
|
_savedeployconf DEPLOY_ALI_DCDN_DOMAIN "$DEPLOY_ALI_DCDN_DOMAIN" |
||||
|
else |
||||
|
DEPLOY_ALI_DCDN_DOMAIN="$_cdomain" |
||||
|
fi |
||||
|
|
||||
|
# read cert and key files and urlencode both |
||||
|
_cert=$(_url_encode upper-hex <"$_cfullchain") |
||||
|
_key=$(_url_encode upper-hex <"$_ckey") |
||||
|
|
||||
|
_debug2 _cert "$_cert" |
||||
|
_debug2 _key "$_key" |
||||
|
|
||||
|
## update domain ssl config |
||||
|
for domain in $DEPLOY_ALI_DCDN_DOMAIN; do |
||||
|
_set_dcdn_domain_ssl_certificate_query "$domain" "$_cert" "$_key" |
||||
|
if _ali_rest "Set DCDN domain SSL certificate for $domain" "" POST; then |
||||
|
_info "Domain $domain certificate has been deployed successfully" |
||||
|
fi |
||||
|
done |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# domain pub pri |
||||
|
_set_dcdn_domain_ssl_certificate_query() { |
||||
|
endpoint=$Ali_DCDN_API |
||||
|
query='' |
||||
|
query=$query'AccessKeyId='$Ali_Key |
||||
|
query=$query'&Action=SetDcdnDomainSSLCertificate' |
||||
|
query=$query'&CertType=upload' |
||||
|
query=$query'&DomainName='$1 |
||||
|
query=$query'&Format=json' |
||||
|
query=$query'&SSLPri='$3 |
||||
|
query=$query'&SSLProtocol=on' |
||||
|
query=$query'&SSLPub='$2 |
||||
|
query=$query'&SignatureMethod=HMAC-SHA1' |
||||
|
query=$query"&SignatureNonce=$(_ali_nonce)" |
||||
|
query=$query'&SignatureVersion=1.0' |
||||
|
query=$query'&Timestamp='$(_timestamp) |
||||
|
query=$query'&Version=2018-01-15' |
||||
|
} |
@ -0,0 +1,184 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# Alviy domain api |
||||
|
# |
||||
|
# Get API key and secret from https://cloud.alviy.com/token |
||||
|
# |
||||
|
# Alviy_token="some-secret-key" |
||||
|
# |
||||
|
# Ex.: acme.sh --issue --staging --dns dns_alviy -d "*.s.example.com" -d "s.example.com" |
||||
|
|
||||
|
Alviy_Api="https://cloud.alviy.com/api/v1" |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
#Usage: dns_alviy_add _acme-challenge.www.domain.com "content" |
||||
|
dns_alviy_add() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}" |
||||
|
if [ -z "$Alviy_token" ]; then |
||||
|
Alviy_token="" |
||||
|
_err "Please specify Alviy token." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
#save the api key and email to the account conf file. |
||||
|
_saveaccountconf_mutable Alviy_token "$Alviy_token" |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
_debug "Getting existing records" |
||||
|
if _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then |
||||
|
_info "This record already exists, skipping" |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
_add_data="{\"content\":\"$txtvalue\",\"type\":\"TXT\"}" |
||||
|
_debug2 _add_data "$_add_data" |
||||
|
_info "Adding record" |
||||
|
if _alviy_rest POST "zone/$_domain/domain/$fulldomain/" "$_add_data"; then |
||||
|
_debug "Checking updated records of '${fulldomain}'" |
||||
|
|
||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then |
||||
|
_err "TXT record '${txtvalue}' for '${fulldomain}', value wasn't set!" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
else |
||||
|
_err "Add txt record error, value '${txtvalue}' for '${fulldomain}' was not set." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_sleep 10 |
||||
|
_info "Added TXT record '${txtvalue}' for '${fulldomain}'." |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
#fulldomain |
||||
|
dns_alviy_rm() { |
||||
|
fulldomain=$1 |
||||
|
txtvalue=$2 |
||||
|
|
||||
|
Alviy_token="${Alviy_token:-$(_readaccountconf_mutable Alviy_token)}" |
||||
|
|
||||
|
_debug "First detect the root zone" |
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
_err "invalid domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug _sub_domain "$_sub_domain" |
||||
|
_debug _domain "$_domain" |
||||
|
|
||||
|
if ! _alviy_txt_exists "$_domain" "$fulldomain" "$txtvalue"; then |
||||
|
_info "The record does not exist, skip" |
||||
|
return 0 |
||||
|
fi |
||||
|
|
||||
|
_add_data="" |
||||
|
uuid=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | tr "," "\n" | grep uuid | cut -d \" -f4) |
||||
|
# delete record |
||||
|
_debug "Delete TXT record for '${fulldomain}'" |
||||
|
if ! _alviy_rest DELETE "zone/$_domain/record/$uuid" "{\"confirm\":1}"; then |
||||
|
_err "Cannot delete empty TXT record for '$fulldomain'" |
||||
|
return 1 |
||||
|
fi |
||||
|
_info "The record '$fulldomain'='$txtvalue' deleted" |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
#_acme-challenge.www.domain.com |
||||
|
#returns |
||||
|
# _sub_domain=_acme-challenge.www |
||||
|
# _domain=domain.com |
||||
|
_get_root() { |
||||
|
domain=$1 |
||||
|
i=3 |
||||
|
a="init" |
||||
|
while [ -n "$a" ]; do |
||||
|
a=$(printf "%s" "$domain" | cut -d . -f $i-) |
||||
|
i=$((i + 1)) |
||||
|
done |
||||
|
n=$((i - 3)) |
||||
|
h=$(printf "%s" "$domain" | cut -d . -f $n-) |
||||
|
if [ -z "$h" ]; then |
||||
|
#not valid |
||||
|
_alviy_rest GET "zone/$domain/" |
||||
|
_debug "can't get host from $domain" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _alviy_rest GET "zone/$h/"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if _contains "$response" '"code":"NOT_FOUND"'; then |
||||
|
_debug "$h not found" |
||||
|
else |
||||
|
s=$((n - 1)) |
||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f -$s) |
||||
|
_domain="$h" |
||||
|
return 0 |
||||
|
fi |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_alviy_txt_exists() { |
||||
|
zone=$1 |
||||
|
domain=$2 |
||||
|
content_data=$3 |
||||
|
_debug "Getting existing records" |
||||
|
|
||||
|
if ! _alviy_rest GET "zone/$zone/domain/$domain/TXT/"; then |
||||
|
_info "The record does not exist" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _contains "$response" "$3"; then |
||||
|
_info "The record has other value" |
||||
|
return 1 |
||||
|
fi |
||||
|
# GOOD code return - TRUE function |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_alviy_rest() { |
||||
|
method=$1 |
||||
|
path="$2" |
||||
|
content_data="$3" |
||||
|
_debug "$path" |
||||
|
|
||||
|
export _H1="Authorization: Bearer $Alviy_token" |
||||
|
export _H2="Content-Type: application/json" |
||||
|
|
||||
|
if [ "$content_data" ] || [ "$method" = "DELETE" ]; then |
||||
|
_debug "data ($method): " "$content_data" |
||||
|
response="$(_post "$content_data" "$Alviy_Api/$path" "" "$method")" |
||||
|
else |
||||
|
response="$(_get "$Alviy_Api/$path")" |
||||
|
fi |
||||
|
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" |
||||
|
if [ "$_code" = "401" ]; then |
||||
|
_err "It seems that your api key or secret is not correct." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ "$_code" != "200" ]; then |
||||
|
_err "API call error ($method): $path Response code $_code" |
||||
|
fi |
||||
|
if [ "$?" != "0" ]; then |
||||
|
_err "error on rest call ($method): $path. Response:" |
||||
|
_err "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
_debug2 response "$response" |
||||
|
return 0 |
||||
|
} |
@ -0,0 +1,406 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
# acme.sh DNS API for Timeweb Cloud provider (https://timeweb.cloud). |
||||
|
# |
||||
|
# Author: https://github.com/nikolaypronchev. |
||||
|
# |
||||
|
# Prerequisites: |
||||
|
# Timeweb Cloud API JWT token. Obtain one from the Timeweb Cloud control panel |
||||
|
# ("API and Terraform" section: https://timeweb.cloud/my/api-keys). The JWT token |
||||
|
# must be provided to this script in one of two ways: |
||||
|
# 1. As the "TW_Token" variable, for example: "export TW_Token=eyJhbG...zUxMiIs"; |
||||
|
# 2. As a "TW_Token" config entry in acme.sh account config file |
||||
|
# (usually located at ~/.acme.sh/account.conf by default). |
||||
|
|
||||
|
TW_Api="https://api.timeweb.cloud/api/v1" |
||||
|
|
||||
|
################ Public functions ################ |
||||
|
|
||||
|
# Adds an ACME DNS-01 challenge DNS TXT record via the Timeweb Cloud API. |
||||
|
# |
||||
|
# Param1: The ACME DNS-01 challenge FQDN. |
||||
|
# Param2: The value of the ACME DNS-01 challenge TXT record. |
||||
|
# |
||||
|
# Example: dns_timeweb_add "_acme-challenge.sub.domain.com" "D-52Wm...4uYM" |
||||
|
dns_timeweb_add() { |
||||
|
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_add\" started." |
||||
|
|
||||
|
_timeweb_set_acme_fqdn "$1" || return 1 |
||||
|
_timeweb_set_acme_txt "$2" || return 1 |
||||
|
_timeweb_check_token || return 1 |
||||
|
_timeweb_split_acme_fqdn || return 1 |
||||
|
_timeweb_dns_txt_add || return 1 |
||||
|
|
||||
|
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_add\" finished." |
||||
|
} |
||||
|
|
||||
|
# Removes a DNS TXT record via the Timeweb Cloud API. |
||||
|
# |
||||
|
# Param1: The ACME DNS-01 challenge FQDN. |
||||
|
# Param2: The value of the ACME DNS-01 challenge TXT record. |
||||
|
# |
||||
|
# Example: dns_timeweb_rm "_acme-challenge.sub.domain.com" "D-52Wm...4uYM" |
||||
|
dns_timeweb_rm() { |
||||
|
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_rm\" started." |
||||
|
|
||||
|
_timeweb_set_acme_fqdn "$1" || return 1 |
||||
|
_timeweb_set_acme_txt "$2" || return 1 |
||||
|
_timeweb_check_token || return 1 |
||||
|
_timeweb_split_acme_fqdn || return 1 |
||||
|
_timeweb_get_dns_txt || return 1 |
||||
|
_timeweb_dns_txt_remove || return 1 |
||||
|
|
||||
|
_debug "$(__green "Timeweb DNS API"): \"dns_timeweb_rm\" finished." |
||||
|
} |
||||
|
|
||||
|
################ Private functions ################ |
||||
|
|
||||
|
# Checks and sets the ACME DNS-01 challenge FQDN. |
||||
|
# |
||||
|
# Param1: The ACME DNS-01 challenge FQDN. |
||||
|
# |
||||
|
# Example: _timeweb_set_acme_fqdn "_acme-challenge.sub.domain.com" |
||||
|
# |
||||
|
# Sets the "Acme_Fqdn" variable (_acme-challenge.sub.domain.com) |
||||
|
_timeweb_set_acme_fqdn() { |
||||
|
Acme_Fqdn=$1 |
||||
|
_debug "Setting ACME DNS-01 challenge FQDN \"$Acme_Fqdn\"." |
||||
|
[ -z "$Acme_Fqdn" ] && { |
||||
|
_err "ACME DNS-01 challenge FQDN is empty." |
||||
|
return 1 |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# Checks and sets the value of the ACME DNS-01 challenge TXT record. |
||||
|
# |
||||
|
# Param1: Value of the ACME DNS-01 challenge TXT record. |
||||
|
# |
||||
|
# Example: _timeweb_set_acme_txt "D-52Wm...4uYM" |
||||
|
# |
||||
|
# Sets the "Acme_Txt" variable to the provided value (D-52Wm...4uYM) |
||||
|
_timeweb_set_acme_txt() { |
||||
|
Acme_Txt=$1 |
||||
|
_debug "Setting the value of the ACME DNS-01 challenge TXT record to \"$Acme_Txt\"." |
||||
|
[ -z "$Acme_Txt" ] && { |
||||
|
_err "ACME DNS-01 challenge TXT record value is empty." |
||||
|
return 1 |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# Checks if the Timeweb Cloud API JWT token is present (refer to the script description). |
||||
|
# Adds or updates the token in the acme.sh account configuration. |
||||
|
_timeweb_check_token() { |
||||
|
_debug "Checking for the presence of the Timeweb Cloud API JWT token." |
||||
|
|
||||
|
TW_Token="${TW_Token:-$(_readaccountconf_mutable TW_Token)}" |
||||
|
|
||||
|
[ -z "$TW_Token" ] && { |
||||
|
_err "Timeweb Cloud API JWT token was not found." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_saveaccountconf_mutable TW_Token "$TW_Token" |
||||
|
} |
||||
|
|
||||
|
# Divides the ACME DNS-01 challenge FQDN into its main domain and subdomain components. |
||||
|
_timeweb_split_acme_fqdn() { |
||||
|
_debug "Trying to divide \"$Acme_Fqdn\" into its main domain and subdomain components." |
||||
|
|
||||
|
TW_Page_Limit=100 |
||||
|
TW_Page_Offset=0 |
||||
|
TW_Domains_Returned="" |
||||
|
|
||||
|
while [ -z "$TW_Domains_Returned" ] || [ "$TW_Domains_Returned" -ge "$TW_Page_Limit" ]; do |
||||
|
|
||||
|
_timeweb_list_domains "$TW_Page_Limit" "$TW_Page_Offset" || return 1 |
||||
|
|
||||
|
# Remove the 'subdomains' subarray to prevent confusion with FQDNs. |
||||
|
|
||||
|
TW_Domains=$( |
||||
|
echo "$TW_Domains" | |
||||
|
sed 's/"subdomains":\[[^]]*]//g' |
||||
|
) |
||||
|
|
||||
|
[ -z "$TW_Domains" ] && { |
||||
|
_err "Failed to parse the list of domains." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
while |
||||
|
TW_Domain=$( |
||||
|
echo "$TW_Domains" | |
||||
|
sed -n 's/.*{[^{]*"fqdn":"\([^"]*\)"[^}]*}.*/\1/p' |
||||
|
) |
||||
|
|
||||
|
[ -n "$TW_Domain" ] && { |
||||
|
_timeweb_is_main_domain "$TW_Domain" && return 0 |
||||
|
|
||||
|
TW_Domains=$( |
||||
|
echo "$TW_Domains" | |
||||
|
sed 's/{\([^{]*"fqdn":"'"$TW_Domain"'"[^}]*\)}//' |
||||
|
) |
||||
|
continue |
||||
|
} |
||||
|
do :; done |
||||
|
|
||||
|
TW_Page_Offset=$(_math "$TW_Page_Offset" + "$TW_Page_Limit") |
||||
|
done |
||||
|
|
||||
|
_err "Failed to divide \"$Acme_Fqdn\" into its main domain and subdomain components." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
# Searches for a previously added DNS TXT record. |
||||
|
# |
||||
|
# Sets the "TW_Dns_Txt_Id" variable. |
||||
|
_timeweb_get_dns_txt() { |
||||
|
_debug "Trying to locate a DNS TXT record with the value \"$Acme_Txt\"." |
||||
|
|
||||
|
TW_Page_Limit=100 |
||||
|
TW_Page_Offset=0 |
||||
|
TW_Dns_Records_Returned="" |
||||
|
|
||||
|
while [ -z "$TW_Dns_Records_Returned" ] || [ "$TW_Dns_Records_Returned" -ge "$TW_Page_Limit" ]; do |
||||
|
|
||||
|
_timeweb_list_dns_records "$TW_Page_Limit" "$TW_Page_Offset" || return 1 |
||||
|
|
||||
|
while |
||||
|
Dns_Record=$( |
||||
|
echo "$TW_Dns_Records" | |
||||
|
sed -n 's/.*{\([^{]*{[^{]*'"$Acme_Txt"'[^}]*}[^}]*\)}.*/\1/p' |
||||
|
) |
||||
|
|
||||
|
[ -n "$Dns_Record" ] && { |
||||
|
_timeweb_is_added_txt "$Dns_Record" && return 0 |
||||
|
|
||||
|
TW_Dns_Records=$( |
||||
|
echo "$TW_Dns_Records" | |
||||
|
sed 's/{\([^{]*{[^{]*'"$Acme_Txt"'[^}]*}[^}]*\)}//' |
||||
|
) |
||||
|
continue |
||||
|
} |
||||
|
do :; done |
||||
|
|
||||
|
TW_Page_Offset=$(_math "$TW_Page_Offset" + "$TW_Page_Limit") |
||||
|
done |
||||
|
|
||||
|
_err "DNS TXT record was not found." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
# Lists domains via the Timeweb Cloud API. |
||||
|
# |
||||
|
# Param 1: Limit for listed domains. |
||||
|
# Param 2: Offset for domains list. |
||||
|
# |
||||
|
# Sets the "TW_Domains" variable. |
||||
|
# Sets the "TW_Domains_Returned" variable. |
||||
|
_timeweb_list_domains() { |
||||
|
_debug "Listing domains via Timeweb Cloud API. Limit: $1, offset: $2." |
||||
|
|
||||
|
export _H1="Authorization: Bearer $TW_Token" |
||||
|
|
||||
|
if ! TW_Domains=$(_get "$TW_Api/domains?limit=$1&offset=$2"); then |
||||
|
_err "The request to the Timeweb Cloud API failed." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
[ -z "$TW_Domains" ] && { |
||||
|
_err "Empty response from the Timeweb Cloud API." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
TW_Domains_Returned=$( |
||||
|
echo "$TW_Domains" | |
||||
|
sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/' |
||||
|
) |
||||
|
|
||||
|
[ -z "$TW_Domains_Returned" ] && { |
||||
|
_err "Failed to extract the total count of domains." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
[ "$TW_Domains_Returned" -eq "0" ] && { |
||||
|
_err "Domains are missing." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_debug "Domains returned by Timeweb Cloud API: $TW_Domains_Returned." |
||||
|
} |
||||
|
|
||||
|
# Lists domain DNS records via the Timeweb Cloud API. |
||||
|
# |
||||
|
# Param 1: Limit for listed DNS records. |
||||
|
# Param 2: Offset for DNS records list. |
||||
|
# |
||||
|
# Sets the "TW_Dns_Records" variable. |
||||
|
# Sets the "TW_Dns_Records_Returned" variable. |
||||
|
_timeweb_list_dns_records() { |
||||
|
_debug "Listing domain DNS records via the Timeweb Cloud API. Limit: $1, offset: $2." |
||||
|
|
||||
|
export _H1="Authorization: Bearer $TW_Token" |
||||
|
|
||||
|
if ! TW_Dns_Records=$(_get "$TW_Api/domains/$TW_Main_Domain/dns-records?limit=$1&offset=$2"); then |
||||
|
_err "The request to the Timeweb Cloud API failed." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
[ -z "$TW_Dns_Records" ] && { |
||||
|
_err "Empty response from the Timeweb Cloud API." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
TW_Dns_Records_Returned=$( |
||||
|
echo "$TW_Dns_Records" | |
||||
|
sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/' |
||||
|
) |
||||
|
|
||||
|
[ -z "$TW_Dns_Records_Returned" ] && { |
||||
|
_err "Failed to extract the total count of DNS records." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
[ "$TW_Dns_Records_Returned" -eq "0" ] && { |
||||
|
_err "DNS records are missing." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_debug "DNS records returned by Timeweb Cloud API: $TW_Dns_Records_Returned." |
||||
|
} |
||||
|
|
||||
|
# Verifies whether the domain is the primary domain for the ACME DNS-01 challenge FQDN. |
||||
|
# The requirement is that the provided domain is the top-level domain |
||||
|
# for the ACME DNS-01 challenge FQDN. |
||||
|
# |
||||
|
# Param 1: Domain object returned by Timeweb Cloud API. |
||||
|
# |
||||
|
# Sets the "TW_Main_Domain" variable (e.g. "_acme-challenge.s1.domain.co.uk" → "domain.co.uk"). |
||||
|
# Sets the "TW_Subdomains" variable (e.g. "_acme-challenge.s1.domain.co.uk" → "_acme-challenge.s1"). |
||||
|
_timeweb_is_main_domain() { |
||||
|
_debug "Checking if \"$1\" is the main domain of the ACME DNS-01 challenge FQDN." |
||||
|
|
||||
|
[ -z "$1" ] && { |
||||
|
_debug "Failed to extract FQDN. Skipping domain." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
! echo ".$Acme_Fqdn" | grep -qi "\.$1$" && { |
||||
|
_debug "Domain does not match the ACME DNS-01 challenge FQDN. Skipping domain." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
TW_Main_Domain=$1 |
||||
|
TW_Subdomains=$( |
||||
|
echo "$Acme_Fqdn" | |
||||
|
sed "s/\.*.\{${#1}\}$//" |
||||
|
) |
||||
|
|
||||
|
_debug "Matched domain. ACME DNS-01 challenge FQDN split as [$TW_Subdomains].[$TW_Main_Domain]." |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# Verifies whether a DNS record was previously added based on the following criteria: |
||||
|
# - The value matches the ACME DNS-01 challenge TXT record value; |
||||
|
# - The record type is TXT; |
||||
|
# - The subdomain matches the ACME DNS-01 challenge FQDN. |
||||
|
# |
||||
|
# Param 1: DNS record object returned by Timeweb Cloud API. |
||||
|
# |
||||
|
# Sets the "TW_Dns_Txt_Id" variable. |
||||
|
_timeweb_is_added_txt() { |
||||
|
_debug "Checking if \"$1\" is a previously added DNS TXT record." |
||||
|
|
||||
|
echo "$1" | grep -qv '"type":"TXT"' && { |
||||
|
_debug "Not a TXT record. Skipping the record." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
if [ -n "$TW_Subdomains" ]; then |
||||
|
echo "$1" | grep -qvi "\"subdomain\":\"$TW_Subdomains\"" && { |
||||
|
_debug "Subdomains do not match. Skipping the record." |
||||
|
return 1 |
||||
|
} |
||||
|
else |
||||
|
echo "$1" | grep -q '"subdomain\":"..*"' && { |
||||
|
_debug "Subdomains do not match. Skipping the record." |
||||
|
return 1 |
||||
|
} |
||||
|
fi |
||||
|
|
||||
|
TW_Dns_Txt_Id=$( |
||||
|
echo "$1" | |
||||
|
sed 's/.*"id":\([0-9]*\)[^0-9].*/\1/' |
||||
|
) |
||||
|
|
||||
|
[ -z "$TW_Dns_Txt_Id" ] && { |
||||
|
_debug "Failed to extract the DNS record ID. Skipping the record." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_debug "Matching DNS TXT record ID is \"$TW_Dns_Txt_Id\"." |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
# Adds a DNS TXT record via the Timeweb Cloud API. |
||||
|
_timeweb_dns_txt_add() { |
||||
|
_debug "Adding a new DNS TXT record via the Timeweb Cloud API." |
||||
|
|
||||
|
export _H1="Authorization: Bearer $TW_Token" |
||||
|
export _H2="Content-Type: application/json" |
||||
|
|
||||
|
if ! TW_Response=$( |
||||
|
_post "{ |
||||
|
\"subdomain\":\"$TW_Subdomains\", |
||||
|
\"type\":\"TXT\", |
||||
|
\"value\":\"$Acme_Txt\" |
||||
|
}" \ |
||||
|
"$TW_Api/domains/$TW_Main_Domain/dns-records" |
||||
|
); then |
||||
|
_err "The request to the Timeweb Cloud API failed." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
[ -z "$TW_Response" ] && { |
||||
|
_err "An unexpected empty response was received from the Timeweb Cloud API." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
TW_Dns_Txt_Id=$( |
||||
|
echo "$TW_Response" | |
||||
|
sed 's/.*"id":\([0-9]*\)[^0-9].*/\1/' |
||||
|
) |
||||
|
|
||||
|
[ -z "$TW_Dns_Txt_Id" ] && { |
||||
|
_err "Failed to extract the DNS TXT Record ID." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_debug "DNS TXT record has been added. ID: \"$TW_Dns_Txt_Id\"." |
||||
|
} |
||||
|
|
||||
|
# Removes a DNS record via the Timeweb Cloud API. |
||||
|
_timeweb_dns_txt_remove() { |
||||
|
_debug "Removing DNS record via the Timeweb Cloud API." |
||||
|
|
||||
|
export _H1="Authorization: Bearer $TW_Token" |
||||
|
|
||||
|
if ! TW_Response=$( |
||||
|
_post \ |
||||
|
"" \ |
||||
|
"$TW_Api/domains/$TW_Main_Domain/dns-records/$TW_Dns_Txt_Id" \ |
||||
|
"" \ |
||||
|
"DELETE" |
||||
|
); then |
||||
|
_err "The request to the Timeweb Cloud API failed." |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
[ -n "$TW_Response" ] && { |
||||
|
_err "Received an unexpected response body from the Timeweb Cloud API." |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
_debug "DNS TXT record with ID \"$TW_Dns_Txt_Id\" has been removed." |
||||
|
} |
@ -1,121 +0,0 @@ |
|||||
#!/usr/bin/env sh |
|
||||
# shellcheck disable=SC2034 |
|
||||
dns_yandex_info='Yandex Domains |
|
||||
Site: tech.Yandex.com/domain/ |
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_yandex |
|
||||
Options: |
|
||||
PDD_Token API Token |
|
||||
Issues: github.com/non7top/acme.sh/issues |
|
||||
Author: <non7top@gmail.com> |
|
||||
' |
|
||||
|
|
||||
######## Public functions ##################### |
|
||||
|
|
||||
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" |
|
||||
dns_yandex_add() { |
|
||||
fulldomain="${1}" |
|
||||
txtvalue="${2}" |
|
||||
_debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'" |
|
||||
|
|
||||
_PDD_credentials || return 1 |
|
||||
|
|
||||
_PDD_get_domain || return 1 |
|
||||
_debug "Found suitable domain: $domain" |
|
||||
|
|
||||
_PDD_get_record_ids || return 1 |
|
||||
_debug "Record_ids: $record_ids" |
|
||||
|
|
||||
if [ -n "$record_ids" ]; then |
|
||||
_info "All existing $subdomain records from $domain will be removed at the very end." |
|
||||
fi |
|
||||
|
|
||||
data="domain=${domain}&type=TXT&subdomain=${subdomain}&ttl=300&content=${txtvalue}" |
|
||||
uri="https://pddimp.yandex.ru/api2/admin/dns/add" |
|
||||
result="$(_post "${data}" "${uri}" | _normalizeJson)" |
|
||||
_debug "Result: $result" |
|
||||
|
|
||||
if ! _contains "$result" '"success":"ok"'; then |
|
||||
if _contains "$result" '"success":"error"' && _contains "$result" '"error":"record_exists"'; then |
|
||||
_info "Record already exists." |
|
||||
else |
|
||||
_err "Can't add $subdomain to $domain." |
|
||||
return 1 |
|
||||
fi |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
#Usage: dns_myapi_rm _acme-challenge.www.domain.com |
|
||||
dns_yandex_rm() { |
|
||||
fulldomain="${1}" |
|
||||
_debug "Calling: dns_yandex_rm() '${fulldomain}'" |
|
||||
|
|
||||
_PDD_credentials || return 1 |
|
||||
|
|
||||
_PDD_get_domain "$fulldomain" || return 1 |
|
||||
_debug "Found suitable domain: $domain" |
|
||||
|
|
||||
_PDD_get_record_ids "${domain}" "${subdomain}" || return 1 |
|
||||
_debug "Record_ids: $record_ids" |
|
||||
|
|
||||
for record_id in $record_ids; do |
|
||||
data="domain=${domain}&record_id=${record_id}" |
|
||||
uri="https://pddimp.yandex.ru/api2/admin/dns/del" |
|
||||
result="$(_post "${data}" "${uri}" | _normalizeJson)" |
|
||||
_debug "Result: $result" |
|
||||
|
|
||||
if ! _contains "$result" '"success":"ok"'; then |
|
||||
_info "Can't remove $subdomain from $domain." |
|
||||
fi |
|
||||
done |
|
||||
} |
|
||||
|
|
||||
#################### Private functions below ################################## |
|
||||
|
|
||||
_PDD_get_domain() { |
|
||||
subdomain_start=1 |
|
||||
while true; do |
|
||||
domain_start=$(_math $subdomain_start + 1) |
|
||||
domain=$(echo "$fulldomain" | cut -d . -f "$domain_start"-) |
|
||||
subdomain=$(echo "$fulldomain" | cut -d . -f -"$subdomain_start") |
|
||||
|
|
||||
_debug "Checking domain $domain" |
|
||||
if [ -z "$domain" ]; then |
|
||||
return 1 |
|
||||
fi |
|
||||
|
|
||||
uri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=$domain" |
|
||||
result="$(_get "${uri}" | _normalizeJson)" |
|
||||
_debug "Result: $result" |
|
||||
|
|
||||
if _contains "$result" '"success":"ok"'; then |
|
||||
return 0 |
|
||||
fi |
|
||||
subdomain_start=$(_math $subdomain_start + 1) |
|
||||
done |
|
||||
} |
|
||||
|
|
||||
_PDD_credentials() { |
|
||||
if [ -z "${PDD_Token}" ]; then |
|
||||
PDD_Token="" |
|
||||
_err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx." |
|
||||
_err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token." |
|
||||
return 1 |
|
||||
else |
|
||||
_saveaccountconf PDD_Token "${PDD_Token}" |
|
||||
fi |
|
||||
export _H1="PddToken: $PDD_Token" |
|
||||
} |
|
||||
|
|
||||
_PDD_get_record_ids() { |
|
||||
_debug "Check existing records for $subdomain" |
|
||||
|
|
||||
uri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${domain}" |
|
||||
result="$(_get "${uri}" | _normalizeJson)" |
|
||||
_debug "Result: $result" |
|
||||
|
|
||||
if ! _contains "$result" '"success":"ok"'; then |
|
||||
return 1 |
|
||||
fi |
|
||||
|
|
||||
record_ids=$(echo "$result" | _egrep_o "{[^{]*\"subdomain\":\"${subdomain}\"[^}]*}" | sed -n -e 's#.*"record_id": \([0-9]*\).*#\1#p') |
|
||||
} |
|
@ -0,0 +1,352 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
# shellcheck disable=SC2034 |
||||
|
dns_yandex360_info='Yandex 360 for Business DNS API. |
||||
|
Yandex 360 for Business is a digital environment for effective collaboration. |
||||
|
Site: https://360.yandex.com/ |
||||
|
Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360 |
||||
|
Options: |
||||
|
YANDEX360_CLIENT_ID OAuth 2.0 ClientID |
||||
|
YANDEX360_CLIENT_SECRET OAuth 2.0 Client secret |
||||
|
OptionsAlt: |
||||
|
YANDEX360_ORG_ID Organization ID. Optional. |
||||
|
YANDEX360_ACCESS_TOKEN OAuth 2.0 Access token. Optional. |
||||
|
Issues: https://github.com/acmesh-official/acme.sh/issues/5213 |
||||
|
Author: <Als@admin.ru.net> |
||||
|
' |
||||
|
|
||||
|
YANDEX360_API_BASE='https://api360.yandex.net/directory/v1' |
||||
|
YANDEX360_OAUTH_BASE='https://oauth.yandex.ru' |
||||
|
|
||||
|
######## Public functions ##################### |
||||
|
|
||||
|
dns_yandex360_add() { |
||||
|
fulldomain="$(_idn "$1")" |
||||
|
txtvalue=$2 |
||||
|
_info 'Using Yandex 360 DNS API' |
||||
|
|
||||
|
if ! _check_variables; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
sub_domain=$(echo "$fulldomain" | sed "s/\.$root_domain$//") |
||||
|
|
||||
|
_debug 'Adding Yandex 360 DNS record for subdomain' "$sub_domain" |
||||
|
dns_api_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns" |
||||
|
data='{"name":"'"$sub_domain"'","type":"TXT","ttl":60,"text":"'"$txtvalue"'"}' |
||||
|
|
||||
|
response="$(_post "$data" "$dns_api_url" '' 'POST' 'application/json')" |
||||
|
|
||||
|
if _contains "$response" 'recordId'; then |
||||
|
return 0 |
||||
|
else |
||||
|
_debug 'Response' "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
dns_yandex360_rm() { |
||||
|
fulldomain="$(_idn "$1")" |
||||
|
txtvalue=$2 |
||||
|
_info 'Using Yandex 360 DNS API' |
||||
|
|
||||
|
if ! _check_variables; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
if ! _get_root "$fulldomain"; then |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug 'Retrieving 100 records from Yandex 360 DNS' |
||||
|
dns_api_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns?perPage=100" |
||||
|
response="$(_get "$dns_api_url" '' '')" |
||||
|
|
||||
|
if ! _contains "$response" "$txtvalue"; then |
||||
|
_info 'DNS record not found. Nothing to remove.' |
||||
|
_debug 'Response' "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
response="$(echo "$response" | _normalizeJson)" |
||||
|
|
||||
|
record_id=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '\{[^}]*'"${txtvalue}"'[^}]*\}' | |
||||
|
_egrep_o '"recordId":[0-9]*' | |
||||
|
cut -d':' -f2 |
||||
|
) |
||||
|
|
||||
|
if [ -z "$record_id" ]; then |
||||
|
_err 'Unable to get record ID to remove' |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
_debug 'Removing DNS record' "$record_id" |
||||
|
delete_url="${YANDEX360_API_BASE}/org/${YANDEX360_ORG_ID}/domains/${root_domain}/dns/${record_id}" |
||||
|
|
||||
|
response="$(_post '' "$delete_url" '' 'DELETE')" |
||||
|
|
||||
|
if _contains "$response" '{}'; then |
||||
|
return 0 |
||||
|
else |
||||
|
_debug 'Response' "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
#################### Private functions below ################################## |
||||
|
|
||||
|
_check_variables() { |
||||
|
YANDEX360_CLIENT_ID="${YANDEX360_CLIENT_ID:-$(_readaccountconf_mutable YANDEX360_CLIENT_ID)}" |
||||
|
YANDEX360_CLIENT_SECRET="${YANDEX360_CLIENT_SECRET:-$(_readaccountconf_mutable YANDEX360_CLIENT_SECRET)}" |
||||
|
YANDEX360_ORG_ID="${YANDEX360_ORG_ID:-$(_readaccountconf_mutable YANDEX360_ORG_ID)}" |
||||
|
YANDEX360_ACCESS_TOKEN="${YANDEX360_ACCESS_TOKEN:-$(_readaccountconf_mutable YANDEX360_ACCESS_TOKEN)}" |
||||
|
YANDEX360_REFRESH_TOKEN="${YANDEX360_REFRESH_TOKEN:-$(_readaccountconf_mutable YANDEX360_REFRESH_TOKEN)}" |
||||
|
|
||||
|
if [ -n "$YANDEX360_ACCESS_TOKEN" ]; then |
||||
|
_info '=========================================' |
||||
|
_info ' ATTENTION' |
||||
|
_info '=========================================' |
||||
|
_info 'A manually provided Yandex 360 access token has been detected, which is not recommended.' |
||||
|
_info 'Please note that this token is valid for a limited time after issuance.' |
||||
|
_info 'It is recommended to obtain the token interactively using acme.sh for one-time setup.' |
||||
|
_info 'Subsequent token renewals will be handled automatically.' |
||||
|
_info 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360' |
||||
|
_info '=========================================' |
||||
|
|
||||
|
_saveaccountconf_mutable YANDEX360_ACCESS_TOKEN "$YANDEX360_ACCESS_TOKEN" |
||||
|
export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN" |
||||
|
|
||||
|
elif [ -z "$YANDEX360_CLIENT_ID" ] || [ -z "$YANDEX360_CLIENT_SECRET" ]; then |
||||
|
_err '=========================================' |
||||
|
_err ' ERROR' |
||||
|
_err '=========================================' |
||||
|
_err 'The required environment variables YANDEX360_CLIENT_ID and YANDEX360_CLIENT_SECRET are not set.' |
||||
|
_err 'Alternatively, you can set YANDEX360_ACCESS_TOKEN environment variable.' |
||||
|
_err 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360' |
||||
|
_err '=========================================' |
||||
|
return 1 |
||||
|
|
||||
|
else |
||||
|
_saveaccountconf_mutable YANDEX360_CLIENT_ID "$YANDEX360_CLIENT_ID" |
||||
|
_saveaccountconf_mutable YANDEX360_CLIENT_SECRET "$YANDEX360_CLIENT_SECRET" |
||||
|
|
||||
|
if [ -n "$YANDEX360_REFRESH_TOKEN" ]; then |
||||
|
_debug 'Refresh token found. Attempting to refresh access token.' |
||||
|
fi |
||||
|
|
||||
|
_refresh_token || _get_token || return 1 |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$YANDEX360_ORG_ID" ]; then |
||||
|
org_response="$(_get "${YANDEX360_API_BASE}/org" '' '')" |
||||
|
|
||||
|
if _contains "$org_response" '"organizations"'; then |
||||
|
org_response="$(echo "$org_response" | _normalizeJson)" |
||||
|
YANDEX360_ORG_ID=$( |
||||
|
echo "$org_response" | |
||||
|
_egrep_o '"id":[[:space:]]*[0-9]+' | |
||||
|
cut -d':' -f2 |
||||
|
) |
||||
|
_debug 'Automatically retrieved YANDEX360_ORG_ID' "$YANDEX360_ORG_ID" |
||||
|
else |
||||
|
_err '=========================================' |
||||
|
_err ' ERROR' |
||||
|
_err '=========================================' |
||||
|
_err "Failed to retrieve YANDEX360_ORG_ID automatically." |
||||
|
_err 'For more details, please visit: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_yandex360' |
||||
|
_err '=========================================' |
||||
|
_debug 'Response' "$org_response" |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
_get_token() { |
||||
|
_info "$(__red '=========================================')" |
||||
|
_info "$(__red ' NOTICE')" |
||||
|
_info "$(__red '=========================================')" |
||||
|
_info "$(__red 'Before using the Yandex 360 API, you need to complete an authorization procedure.')" |
||||
|
_info "$(__red 'The initial access token is obtained interactively and is a one-time operation.')" |
||||
|
_info "$(__red 'Subsequent API requests will be handled automatically.')" |
||||
|
_info "$(__red '=========================================')" |
||||
|
|
||||
|
_info 'Initiating device authorization flow' |
||||
|
device_code_url="${YANDEX360_OAUTH_BASE}/device/code" |
||||
|
|
||||
|
hostname=$(uname -n) |
||||
|
data="client_id=$YANDEX360_CLIENT_ID&device_id=acme.sh ${hostname}&device_name=acme.sh ${hostname}" |
||||
|
|
||||
|
response="$(_post "$data" "$device_code_url" '' 'POST')" |
||||
|
|
||||
|
if ! _contains "$response" 'device_code'; then |
||||
|
_err 'Failed to get device code' |
||||
|
_debug 'Response' "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
response="$(echo "$response" | _normalizeJson)" |
||||
|
|
||||
|
device_code=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"device_code":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
_debug 'Device code' "$device_code" |
||||
|
|
||||
|
user_code=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"user_code":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
_debug 'User code' "$user_code" |
||||
|
|
||||
|
verification_url=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"verification_url":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
_debug 'Verification URL' "$verification_url" |
||||
|
|
||||
|
interval=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"interval":[[:space:]]*[0-9]+' | |
||||
|
cut -d':' -f2 |
||||
|
) |
||||
|
_debug 'Polling interval' "$interval" |
||||
|
|
||||
|
_info "$(__red 'Please visit '"$verification_url"' and log in as an organization administrator')" |
||||
|
_info "$(__red 'Once logged in, enter the code: '"$user_code"' on the page from the previous step')" |
||||
|
_info "$(__red 'Waiting for authorization...')" |
||||
|
|
||||
|
_debug 'Polling for token' |
||||
|
token_url="${YANDEX360_OAUTH_BASE}/token" |
||||
|
|
||||
|
while true; do |
||||
|
data="grant_type=device_code&code=$device_code&client_id=$YANDEX360_CLIENT_ID&client_secret=$YANDEX360_CLIENT_SECRET" |
||||
|
|
||||
|
response="$(_post "$data" "$token_url" '' 'POST')" |
||||
|
|
||||
|
if _contains "$response" 'access_token'; then |
||||
|
response="$(echo "$response" | _normalizeJson)" |
||||
|
YANDEX360_ACCESS_TOKEN=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"access_token":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
YANDEX360_REFRESH_TOKEN=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"refresh_token":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
|
||||
|
_secure_debug 'Obtained access token' "$YANDEX360_ACCESS_TOKEN" |
||||
|
_secure_debug 'Obtained refresh token' "$YANDEX360_REFRESH_TOKEN" |
||||
|
|
||||
|
_saveaccountconf_mutable YANDEX360_REFRESH_TOKEN "$YANDEX360_REFRESH_TOKEN" |
||||
|
|
||||
|
export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN" |
||||
|
|
||||
|
_info 'Access token obtained successfully' |
||||
|
return 0 |
||||
|
elif _contains "$response" 'authorization_pending'; then |
||||
|
_debug 'Response' "$response" |
||||
|
_debug "Authorization pending. Waiting $interval seconds before next attempt." |
||||
|
_sleep "$interval" |
||||
|
else |
||||
|
_debug 'Response' "$response" |
||||
|
_err 'Failed to get access token' |
||||
|
return 1 |
||||
|
fi |
||||
|
done |
||||
|
} |
||||
|
|
||||
|
_refresh_token() { |
||||
|
token_url="${YANDEX360_OAUTH_BASE}/token" |
||||
|
|
||||
|
data="grant_type=refresh_token&refresh_token=$YANDEX360_REFRESH_TOKEN&client_id=$YANDEX360_CLIENT_ID&client_secret=$YANDEX360_CLIENT_SECRET" |
||||
|
|
||||
|
response="$(_post "$data" "$token_url" '' 'POST')" |
||||
|
|
||||
|
if _contains "$response" 'access_token'; then |
||||
|
response="$(echo "$response" | _normalizeJson)" |
||||
|
YANDEX360_ACCESS_TOKEN=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"access_token":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
YANDEX360_REFRESH_TOKEN=$( |
||||
|
echo "$response" | |
||||
|
_egrep_o '"refresh_token":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
|
||||
|
_secure_debug 'Received access token' "$YANDEX360_ACCESS_TOKEN" |
||||
|
_secure_debug 'Received refresh token' "$YANDEX360_REFRESH_TOKEN" |
||||
|
|
||||
|
_saveaccountconf_mutable YANDEX360_REFRESH_TOKEN "$YANDEX360_REFRESH_TOKEN" |
||||
|
|
||||
|
export _H1="Authorization: OAuth $YANDEX360_ACCESS_TOKEN" |
||||
|
|
||||
|
_info 'Access token refreshed successfully' |
||||
|
return 0 |
||||
|
else |
||||
|
_info 'Failed to refresh token. Will attempt to obtain a new one.' |
||||
|
_debug 'Response' "$response" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
_get_root() { |
||||
|
domain="$1" |
||||
|
|
||||
|
for org_id in $YANDEX360_ORG_ID; do |
||||
|
_debug 'Checking organization ID' "$org_id" |
||||
|
domains_api_url="${YANDEX360_API_BASE}/org/${org_id}/domains" |
||||
|
|
||||
|
domains_response="$(_get "$domains_api_url" '' '')" |
||||
|
|
||||
|
if ! _contains "$domains_response" '"domains"'; then |
||||
|
_debug 'No domains found for organization' "$org_id" |
||||
|
_debug 'Response' "$domains_response" |
||||
|
continue |
||||
|
fi |
||||
|
|
||||
|
domains_response="$(echo "$domains_response" | _normalizeJson)" |
||||
|
domain_names=$( |
||||
|
echo "$domains_response" | |
||||
|
_egrep_o '"name":"[^"]*"' | |
||||
|
cut -d'"' -f4 |
||||
|
) |
||||
|
|
||||
|
for d in $domain_names; do |
||||
|
d="$(_idn "$d")" |
||||
|
_debug 'Checking domain' "$d" |
||||
|
|
||||
|
if _endswith "$domain" "$d"; then |
||||
|
root_domain="$d" |
||||
|
break |
||||
|
fi |
||||
|
done |
||||
|
|
||||
|
if [ -n "$root_domain" ]; then |
||||
|
_debug "Root domain found: $root_domain in organization $org_id" |
||||
|
|
||||
|
YANDEX360_ORG_ID="$org_id" |
||||
|
_saveaccountconf_mutable YANDEX360_ORG_ID "$YANDEX360_ORG_ID" |
||||
|
|
||||
|
return 0 |
||||
|
fi |
||||
|
done |
||||
|
|
||||
|
if [ -z "$root_domain" ]; then |
||||
|
_err "Could not find a matching root domain for $domain in any organization" |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue