commit
fb27261568
1
.github/workflows/DNS.yml
vendored
1
.github/workflows/DNS.yml
vendored
@ -1,5 +1,6 @@
|
|||||||
name: DNS
|
name: DNS
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'dnsapi/*.sh'
|
- 'dnsapi/*.sh'
|
||||||
|
157
deploy/ali_cdn.sh
Normal file
157
deploy/ali_cdn.sh
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Script to create certificate to Alibaba Cloud CDN
|
||||||
|
#
|
||||||
|
# This deployment required following variables
|
||||||
|
# export Ali_Key="ALIACCESSKEY"
|
||||||
|
# export Ali_Secret="ALISECRETKEY"
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn.example.com"
|
||||||
|
# If you have more than one domain, just
|
||||||
|
# export DEPLOY_ALI_CDN_DOMAIN="cdn1.example.com cdn2.example.com"
|
||||||
|
#
|
||||||
|
# The credentials are shared with all domains, also shared with dns_ali api
|
||||||
|
|
||||||
|
Ali_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"
|
||||||
|
|
||||||
|
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
|
||||||
|
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
|
||||||
|
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
|
||||||
|
Ali_Key=""
|
||||||
|
Ali_Secret=""
|
||||||
|
_err "You don't specify aliyun api key and secret yet."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and secret to the account conf file.
|
||||||
|
_saveaccountconf_mutable Ali_Key "$Ali_Key"
|
||||||
|
_saveaccountconf_mutable Ali_Secret "$Ali_Secret"
|
||||||
|
|
||||||
|
_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 <"$_cfullchain")
|
||||||
|
_key=$(_url_encode_upper <"$_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
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
# act ign mtd
|
||||||
|
_ali_rest() {
|
||||||
|
act="$1"
|
||||||
|
ign="$2"
|
||||||
|
mtd="$3"
|
||||||
|
|
||||||
|
signature=$(printf "%s" "$mtd&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
|
||||||
|
signature=$(_ali_urlencode "$signature")
|
||||||
|
url="$Ali_API?$query&Signature=$signature"
|
||||||
|
|
||||||
|
if [ "$mtd" = "GET" ]; then
|
||||||
|
response="$(_get "$url")"
|
||||||
|
else
|
||||||
|
# post payload is not supported yet because of signature
|
||||||
|
response="$(_post "" "$url")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_ret="$?"
|
||||||
|
_debug2 response "$response"
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
|
_err "Error <$act>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ign" ]; then
|
||||||
|
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
|
||||||
|
if [ "$message" ]; then
|
||||||
|
_err "$message"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_ali_urlencode() {
|
||||||
|
_str="$1"
|
||||||
|
_str_len=${#_str}
|
||||||
|
_u_i=1
|
||||||
|
while [ "$_u_i" -le "$_str_len" ]; do
|
||||||
|
_str_c="$(printf "%s" "$_str" | cut -c "$_u_i")"
|
||||||
|
case $_str_c in [a-zA-Z0-9.~_-])
|
||||||
|
printf "%s" "$_str_c"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "%%%02X" "'$_str_c"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
_u_i="$(_math "$_u_i" + 1)"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_ali_nonce() {
|
||||||
|
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
|
||||||
|
#Not so good...
|
||||||
|
date +"%s%N" | sed 's/%N//g'
|
||||||
|
}
|
||||||
|
|
||||||
|
_timestamp() {
|
||||||
|
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
|
||||||
|
}
|
||||||
|
|
||||||
|
# stdin stdout
|
||||||
|
_url_encode_upper() {
|
||||||
|
encoded=$(_url_encode)
|
||||||
|
|
||||||
|
for match in $(echo "$encoded" | _egrep_o '%..' | sort -u); do
|
||||||
|
upper=$(echo "$match" | _upper_case)
|
||||||
|
encoded=$(echo "$encoded" | sed "s/$match/$upper/g")
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "$encoded"
|
||||||
|
}
|
||||||
|
|
||||||
|
# domain pub pri
|
||||||
|
_set_cdn_domain_ssl_certificate_query() {
|
||||||
|
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'
|
||||||
|
}
|
184
dnsapi/dns_alviy.sh
Normal file
184
dnsapi/dns_alviy.sh
Normal file
@ -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
|
||||||
|
}
|
@ -130,8 +130,6 @@ _get_root() {
|
|||||||
i=1
|
i=1
|
||||||
p=1
|
p=1
|
||||||
|
|
||||||
_anx_rest GET "zone.json"
|
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
_debug h "$h"
|
_debug h "$h"
|
||||||
@ -140,6 +138,7 @@ _get_root() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
_anx_rest GET "zone.json/${h}"
|
||||||
if _contains "$response" "\"name\":\"$h\""; then
|
if _contains "$response" "\"name\":\"$h\""; then
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
_domain=$h
|
_domain=$h
|
||||||
|
@ -210,7 +210,7 @@ _get_recordset_id() {
|
|||||||
_zoneid=$3
|
_zoneid=$3
|
||||||
export _H1="X-Auth-Token: ${_token}"
|
export _H1="X-Auth-Token: ${_token}"
|
||||||
|
|
||||||
response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}")
|
response=$(_get "${dns_api}/v2/zones/${_zoneid}/recordsets?name=${_domain}&status=ACTIVE")
|
||||||
if _contains "${response}" '"id"'; then
|
if _contains "${response}" '"id"'; then
|
||||||
_id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")"
|
_id="$(echo "${response}" | _egrep_o "\"id\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | tr -d " ")"
|
||||||
printf "%s" "${_id}"
|
printf "%s" "${_id}"
|
||||||
@ -227,7 +227,7 @@ _add_record() {
|
|||||||
|
|
||||||
# Get Existing Records
|
# Get Existing Records
|
||||||
export _H1="X-Auth-Token: ${_token}"
|
export _H1="X-Auth-Token: ${_token}"
|
||||||
response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}")
|
response=$(_get "${dns_api}/v2/zones/${zoneid}/recordsets?name=${_domain}&status=ACTIVE")
|
||||||
|
|
||||||
_debug2 "${response}"
|
_debug2 "${response}"
|
||||||
_exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g')
|
_exist_record=$(echo "${response}" | _egrep_o '"records":[^]]*' | sed 's/\"records\"\:\[//g')
|
||||||
|
@ -16,8 +16,9 @@ Author: Darven Dissek, William Gertz
|
|||||||
#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
dns_miab_add() {
|
dns_miab_add() {
|
||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
# Added "value=" and "&ttl=300" to accomodate the new TXT record format used by the MIAB/PMIAB API
|
||||||
_info "Using miab challange add"
|
txtvalue="value=$2&ttl=300"
|
||||||
|
_info "Using miab challenge add"
|
||||||
_debug fulldomain "$fulldomain"
|
_debug fulldomain "$fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ dns_miab_add() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#check domain and seperate into doamin and host
|
#check domain and seperate into domain and host
|
||||||
if ! _get_root "$fulldomain"; then
|
if ! _get_root "$fulldomain"; then
|
||||||
_err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
|
_err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
|
||||||
return 1
|
return 1
|
||||||
@ -55,7 +56,7 @@ dns_miab_rm() {
|
|||||||
fulldomain=$1
|
fulldomain=$1
|
||||||
txtvalue=$2
|
txtvalue=$2
|
||||||
|
|
||||||
_info "Using miab challage delete"
|
_info "Using miab challenge delete"
|
||||||
_debug fulldomain "$fulldomain"
|
_debug fulldomain "$fulldomain"
|
||||||
_debug txtvalue "$txtvalue"
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ dns_nsupdate_add() {
|
|||||||
NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
|
NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
|
||||||
NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
|
NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
|
||||||
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
|
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
|
||||||
|
NSUPDATE_OPT="${NSUPDATE_OPT:-$(_readaccountconf_mutable NSUPDATE_OPT)}"
|
||||||
|
|
||||||
_checkKeyFile || return 1
|
_checkKeyFile || return 1
|
||||||
|
|
||||||
@ -28,21 +29,23 @@ dns_nsupdate_add() {
|
|||||||
_saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}"
|
_saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}"
|
||||||
_saveaccountconf_mutable NSUPDATE_KEY "${NSUPDATE_KEY}"
|
_saveaccountconf_mutable NSUPDATE_KEY "${NSUPDATE_KEY}"
|
||||||
_saveaccountconf_mutable NSUPDATE_ZONE "${NSUPDATE_ZONE}"
|
_saveaccountconf_mutable NSUPDATE_ZONE "${NSUPDATE_ZONE}"
|
||||||
|
_saveaccountconf_mutable NSUPDATE_OPT "${NSUPDATE_OPT}"
|
||||||
|
|
||||||
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
|
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
|
||||||
[ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
|
[ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
|
||||||
|
[ -n "${NSUPDATE_OPT}" ] || NSUPDATE_OPT=""
|
||||||
|
|
||||||
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
|
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
|
||||||
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
|
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
|
||||||
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
|
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
|
||||||
if [ -z "${NSUPDATE_ZONE}" ]; then
|
if [ -z "${NSUPDATE_ZONE}" ]; then
|
||||||
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
|
nsupdate -k "${NSUPDATE_KEY}" $nsdebug "${NSUPDATE_OPT}" <<EOF
|
||||||
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
||||||
update add ${fulldomain}. 60 in txt "${txtvalue}"
|
update add ${fulldomain}. 60 in txt "${txtvalue}"
|
||||||
send
|
send
|
||||||
EOF
|
EOF
|
||||||
else
|
else
|
||||||
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
|
nsupdate -k "${NSUPDATE_KEY}" $nsdebug "${NSUPDATE_OPT}" <<EOF
|
||||||
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
||||||
zone ${NSUPDATE_ZONE}.
|
zone ${NSUPDATE_ZONE}.
|
||||||
update add ${fulldomain}. 60 in txt "${txtvalue}"
|
update add ${fulldomain}. 60 in txt "${txtvalue}"
|
||||||
@ -65,6 +68,7 @@ dns_nsupdate_rm() {
|
|||||||
NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
|
NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
|
||||||
NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
|
NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
|
||||||
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
|
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
|
||||||
|
NSUPDATE_OPT="${NSUPDATE_OPT:-$(_readaccountconf_mutable NSUPDATE_OPT)}"
|
||||||
|
|
||||||
_checkKeyFile || return 1
|
_checkKeyFile || return 1
|
||||||
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
|
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
|
||||||
@ -73,13 +77,13 @@ dns_nsupdate_rm() {
|
|||||||
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
|
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
|
||||||
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
|
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
|
||||||
if [ -z "${NSUPDATE_ZONE}" ]; then
|
if [ -z "${NSUPDATE_ZONE}" ]; then
|
||||||
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
|
nsupdate -k "${NSUPDATE_KEY}" $nsdebug "${NSUPDATE_OPT}" <<EOF
|
||||||
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
||||||
update delete ${fulldomain}. txt
|
update delete ${fulldomain}. txt
|
||||||
send
|
send
|
||||||
EOF
|
EOF
|
||||||
else
|
else
|
||||||
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
|
nsupdate -k "${NSUPDATE_KEY}" $nsdebug "${NSUPDATE_OPT}" <<EOF
|
||||||
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
|
||||||
zone ${NSUPDATE_ZONE}.
|
zone ${NSUPDATE_ZONE}.
|
||||||
update delete ${fulldomain}. txt
|
update delete ${fulldomain}. txt
|
||||||
|
405
dnsapi/dns_timeweb.sh
Normal file
405
dnsapi/dns_timeweb.sh
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
while [ -z "$TW_Domains_Total" ] ||
|
||||||
|
[ "$((TW_Domains_Total + TW_Page_Limit))" -gt "$((TW_Page_Offset + 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
|
||||||
|
|
||||||
|
while [ -z "$TW_Dns_Records_Total" ] ||
|
||||||
|
[ "$((TW_Dns_Records_Total + TW_Page_Limit))" -gt "$((TW_Page_Offset + 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_Total" 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_Total=$(
|
||||||
|
echo "$TW_Domains" |
|
||||||
|
sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/'
|
||||||
|
)
|
||||||
|
|
||||||
|
[ -z "$TW_Domains_Total" ] && {
|
||||||
|
_err "Failed to extract the total count of domains."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$TW_Domains_Total" -eq "0" ] && {
|
||||||
|
_err "Domains are missing."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_debug "Total count of domains in the Timeweb Cloud account: $TW_Domains_Total."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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_Total" 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_Total=$(
|
||||||
|
echo "$TW_Dns_Records" |
|
||||||
|
sed 's/.*"meta":{"total":\([0-9]*\)[^0-9].*/\1/'
|
||||||
|
)
|
||||||
|
|
||||||
|
[ -z "$TW_Dns_Records_Total" ] && {
|
||||||
|
_err "Failed to extract the total count of DNS records."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$TW_Dns_Records_Total" -eq "0" ] && {
|
||||||
|
_err "DNS records are missing."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_debug "Total count of DNS records: $TW_Dns_Records_Total."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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')
|
|
||||||
}
|
|
352
dnsapi/dns_yandex360.sh
Normal file
352
dnsapi/dns_yandex360.sh
Normal file
@ -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
|
||||||
|
}
|
69
notify/teams.sh
Normal file → Executable file
69
notify/teams.sh
Normal file → Executable file
@ -3,10 +3,6 @@
|
|||||||
#Support Microsoft Teams webhooks
|
#Support Microsoft Teams webhooks
|
||||||
|
|
||||||
#TEAMS_WEBHOOK_URL=""
|
#TEAMS_WEBHOOK_URL=""
|
||||||
#TEAMS_THEME_COLOR=""
|
|
||||||
#TEAMS_SUCCESS_COLOR=""
|
|
||||||
#TEAMS_ERROR_COLOR=""
|
|
||||||
#TEAMS_SKIP_COLOR=""
|
|
||||||
|
|
||||||
teams_send() {
|
teams_send() {
|
||||||
_subject="$1"
|
_subject="$1"
|
||||||
@ -14,9 +10,9 @@ teams_send() {
|
|||||||
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
|
_statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
|
||||||
_debug "_statusCode" "$_statusCode"
|
_debug "_statusCode" "$_statusCode"
|
||||||
|
|
||||||
_color_success="2cbe4e" # green
|
_color_success="Good"
|
||||||
_color_danger="cb2431" # red
|
_color_danger="Attention"
|
||||||
_color_muted="586069" # gray
|
_color_muted="Accent"
|
||||||
|
|
||||||
TEAMS_WEBHOOK_URL="${TEAMS_WEBHOOK_URL:-$(_readaccountconf_mutable TEAMS_WEBHOOK_URL)}"
|
TEAMS_WEBHOOK_URL="${TEAMS_WEBHOOK_URL:-$(_readaccountconf_mutable TEAMS_WEBHOOK_URL)}"
|
||||||
if [ -z "$TEAMS_WEBHOOK_URL" ]; then
|
if [ -z "$TEAMS_WEBHOOK_URL" ]; then
|
||||||
@ -26,26 +22,6 @@ teams_send() {
|
|||||||
fi
|
fi
|
||||||
_saveaccountconf_mutable TEAMS_WEBHOOK_URL "$TEAMS_WEBHOOK_URL"
|
_saveaccountconf_mutable TEAMS_WEBHOOK_URL "$TEAMS_WEBHOOK_URL"
|
||||||
|
|
||||||
TEAMS_THEME_COLOR="${TEAMS_THEME_COLOR:-$(_readaccountconf_mutable TEAMS_THEME_COLOR)}"
|
|
||||||
if [ -n "$TEAMS_THEME_COLOR" ]; then
|
|
||||||
_saveaccountconf_mutable TEAMS_THEME_COLOR "$TEAMS_THEME_COLOR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TEAMS_SUCCESS_COLOR="${TEAMS_SUCCESS_COLOR:-$(_readaccountconf_mutable TEAMS_SUCCESS_COLOR)}"
|
|
||||||
if [ -n "$TEAMS_SUCCESS_COLOR" ]; then
|
|
||||||
_saveaccountconf_mutable TEAMS_SUCCESS_COLOR "$TEAMS_SUCCESS_COLOR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TEAMS_ERROR_COLOR="${TEAMS_ERROR_COLOR:-$(_readaccountconf_mutable TEAMS_ERROR_COLOR)}"
|
|
||||||
if [ -n "$TEAMS_ERROR_COLOR" ]; then
|
|
||||||
_saveaccountconf_mutable TEAMS_ERROR_COLOR "$TEAMS_ERROR_COLOR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TEAMS_SKIP_COLOR="${TEAMS_SKIP_COLOR:-$(_readaccountconf_mutable TEAMS_SKIP_COLOR)}"
|
|
||||||
if [ -n "$TEAMS_SKIP_COLOR" ]; then
|
|
||||||
_saveaccountconf_mutable TEAMS_SKIP_COLOR "$TEAMS_SKIP_COLOR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
export _H1="Content-Type: application/json"
|
export _H1="Content-Type: application/json"
|
||||||
|
|
||||||
_subject=$(echo "$_subject" | _json_encode)
|
_subject=$(echo "$_subject" | _json_encode)
|
||||||
@ -63,16 +39,35 @@ teams_send() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
_color=$(echo "$_color" | tr -cd 'a-fA-F0-9')
|
_data="{
|
||||||
if [ -z "$_color" ]; then
|
\"type\": \"message\",
|
||||||
_color=$(echo "${TEAMS_THEME_COLOR:-$_color_muted}" | tr -cd 'a-fA-F0-9')
|
\"attachments\": [
|
||||||
fi
|
{
|
||||||
|
\"contentType\": \"application/vnd.microsoft.card.adaptive\",
|
||||||
_data="{\"title\": \"$_subject\","
|
\"contentUrl\": null,
|
||||||
if [ -n "$_color" ]; then
|
\"content\": {
|
||||||
_data="$_data\"themeColor\": \"$_color\", "
|
\"schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",
|
||||||
fi
|
\"type\": \"AdaptiveCard\",
|
||||||
_data="$_data\"text\": \"$_content\"}"
|
\"version\": \"1.2\",
|
||||||
|
\"body\": [
|
||||||
|
{
|
||||||
|
\"type\": \"TextBlock\",
|
||||||
|
\"size\": \"large\",
|
||||||
|
\"weight\": \"bolder\",
|
||||||
|
\"wrap\": true,
|
||||||
|
\"color\": \"$_color\",
|
||||||
|
\"text\": \"$_subject\"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"type\": \"TextBlock\",
|
||||||
|
\"text\": \"$_content\",
|
||||||
|
\"wrap\": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"
|
||||||
|
|
||||||
if response=$(_post "$_data" "$TEAMS_WEBHOOK_URL"); then
|
if response=$(_post "$_data" "$TEAMS_WEBHOOK_URL"); then
|
||||||
if ! _contains "$response" error; then
|
if ! _contains "$response" error; then
|
||||||
|
Loading…
Reference in New Issue
Block a user