mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2026-06-03 21:52:07 -04:00
[dnsapi] add ServerCore DNS API hook
This commit is contained in:
parent
15a0f52577
commit
29330a97bb
1 changed files with 345 additions and 0 deletions
345
dnsapi/dns_servercore.sh
Normal file
345
dnsapi/dns_servercore.sh
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_servercore_info='Servercore.com
|
||||
Site: Servercore.com
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_serevercore
|
||||
Options:
|
||||
SCore_Login_ID Account ID
|
||||
SCore_Project_Name Project name
|
||||
SCore_Login_Name Service user name
|
||||
SCore_Pswd Service user password
|
||||
SCore_Expire Token lifetime. In minutes (0-1440). Default "1400"
|
||||
'
|
||||
|
||||
SCore_Api="https://api.servercore.com/domains"
|
||||
auth_uri="https://cloud.api.servercore.com/identity/v3/auth/tokens"
|
||||
_score_sep='#'
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
dns_servercore_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if ! _score_init_vars; then
|
||||
return 1
|
||||
fi
|
||||
_debug2 SCore_Expire "$SCore_Expire"
|
||||
_debug2 SCore_Login_Name "$SCore_Login_Name"
|
||||
_debug2 SCore_Login_ID "$SCore_Login_ID"
|
||||
_debug2 SCore_Project_Name "$SCore_Project_Name"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_info "Adding record"
|
||||
_ext_srv1="/zones/"
|
||||
_ext_srv2="/rrset/"
|
||||
_text_tmp=$(printf '%s' "$txtvalue" | sed -En "s/[\"]*([^\"]*)/\1/p")
|
||||
_text_tmp='\"'$_text_tmp'\"'
|
||||
_data="{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"${fulldomain}.\", \"records\": [{\"content\":\"$_text_tmp\"}]}"
|
||||
_ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}"
|
||||
_debug _ext_uri "$_ext_uri"
|
||||
_debug _data "$_data"
|
||||
|
||||
if _score_rest POST "$_ext_uri" "$_data"; then
|
||||
if _contains "$response" "$txtvalue"; then
|
||||
_info "Added, OK"
|
||||
return 0
|
||||
fi
|
||||
if _contains "$response" "already_exists"; then
|
||||
# record TXT with $fulldomain already exists
|
||||
# It is necessary to add one more content to the comments
|
||||
# read all records rrset
|
||||
_debug "Getting txt records"
|
||||
_score_rest GET "${_ext_uri}"
|
||||
# There is already a $txtvalue value, no need to add it
|
||||
if _contains "$response" "$txtvalue"; then
|
||||
_info "Added, OK"
|
||||
_info "Txt record ${fulldomain} with value ${txtvalue} already exists"
|
||||
return 0
|
||||
fi
|
||||
# group \1 - full record rrset; group \2 - records attribute value, exactly {"content":"\"value1\""},{"content":"\"value2\""}",...
|
||||
_record_seg="$(printf '%s' "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\1/p")"
|
||||
_record_array="$(printf '%s' "$response" | sed -En "s/.*(\{\"id\"[^}]*${fulldomain}[^}]*records[^}]*\[(\{[^]]*\})\][^}]*}).*/\2/p")"
|
||||
# record id
|
||||
_record_id="$(printf '%s' "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")"
|
||||
# preparing _data
|
||||
_tmp_str="${_record_array},{\"content\":\"${_text_tmp}\"}"
|
||||
_data="{\"ttl\": 60, \"records\": [${_tmp_str}]}"
|
||||
_debug2 _record_seg "$_record_seg"
|
||||
_debug2 _record_array "$_record_array"
|
||||
_debug2 _record_array "$_record_id"
|
||||
_debug "New data for record" "$_data"
|
||||
if _score_rest PATCH "${_ext_uri}${_record_id}" "$_data"; then
|
||||
_info "Added, OK"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
}
|
||||
|
||||
#fulldomain txtvalue
|
||||
dns_servercore_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
if ! _score_init_vars "nosave"; then
|
||||
return 1
|
||||
fi
|
||||
_debug2 SCore_Expire "$SCore_Expire"
|
||||
_debug2 SCore_Login_Name "$SCore_Login_Name"
|
||||
_debug2 SCore_Login_ID "$SCore_Login_ID"
|
||||
_debug2 SCore_Project_Name "$SCore_Project_Name"
|
||||
#
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
#
|
||||
_ext_srv1="/zones/"
|
||||
_ext_srv2="/rrset/"
|
||||
#
|
||||
_debug "Getting txt records"
|
||||
_ext_uri="${_ext_srv1}$_domain_id${_ext_srv2}"
|
||||
_debug _ext_uri "$_ext_uri"
|
||||
_score_rest GET "${_ext_uri}"
|
||||
#
|
||||
if ! _contains "$response" "$txtvalue"; then
|
||||
_err "Txt record not found"
|
||||
return 1
|
||||
fi
|
||||
#
|
||||
_record_seg="$(printf '%s' "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\1/gp")"
|
||||
_record_arr="$(printf '%s' "$response" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/p")"
|
||||
_debug2 "_record_seg" "$_record_seg"
|
||||
if [ -z "$_record_seg" ]; then
|
||||
_err "can not find _record_seg"
|
||||
return 1
|
||||
fi
|
||||
# record id
|
||||
# the following lines change the algorithm for deleting records with the value $txtvalue
|
||||
# if you use the 1st line, then all such records are deleted at once
|
||||
# if you use the 2nd line, then only the first entry from them is deleted
|
||||
#_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"")"
|
||||
_record_id="$(printf '%s' "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2 | tr -d "\"" | sed '1!d')"
|
||||
if [ -z "$_record_id" ]; then
|
||||
_err "can not find _record_id"
|
||||
return 1
|
||||
fi
|
||||
_debug2 "_record_id" "$_record_id"
|
||||
# delete all record type TXT with text $txtvalue
|
||||
# actual
|
||||
_new_arr="$(printf '%s' "$_record_seg" | sed -En "s/.*(\{\"id\"[^}]*records[^[]*(\[(\{[^]]*${txtvalue}[^]]*)\])[^}]*}).*/\3/gp" | sed -En "s/(\},\{)/}\n{/gp" | sed "/${txtvalue}/d" | sed ":a;N;s/\n/,/;ta")"
|
||||
# uri record for DEL or PATCH
|
||||
_del_uri="${_ext_uri}${_record_id}"
|
||||
_debug _del_uri "$_del_uri"
|
||||
if [ -z "$_new_arr" ]; then
|
||||
# remove record
|
||||
if ! _score_rest DELETE "${_del_uri}"; then
|
||||
_err "Delete record error: ${_del_uri}."
|
||||
else
|
||||
info "Delete record success: ${_del_uri}."
|
||||
fi
|
||||
else
|
||||
# update a record by removing one element in content
|
||||
_data="{\"ttl\": 60, \"records\": [${_new_arr}]}"
|
||||
_debug2 _data "$_data"
|
||||
# REST API PATCH call
|
||||
if _score_rest PATCH "${_del_uri}" "$_data"; then
|
||||
_info "Patched, OK: ${_del_uri}"
|
||||
else
|
||||
_err "Patched record error: ${_del_uri}."
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_get_root() {
|
||||
domain=$1
|
||||
# version API 2
|
||||
_ext_uri='/zones/'
|
||||
domain="${domain}."
|
||||
_debug "domain:: " "$domain"
|
||||
# read records of all domains
|
||||
if ! _score_rest GET "$_ext_uri"; then
|
||||
_err "Error read records of all domains"
|
||||
return 1
|
||||
fi
|
||||
i=1
|
||||
p=1
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||
_debug h "$h"
|
||||
if [ -z "$h" ]; then
|
||||
_err "The domain was not found among the registered ones"
|
||||
return 1
|
||||
fi
|
||||
_domain_record=$(printf '%s' "$response" | sed -En "s/.*(\{[^}]*id[^}]*\"name\" *: *\"$h\"[^}]*}).*/\1/p")
|
||||
_debug "_domain_record:: " "$_domain_record"
|
||||
if [ -n "$_domain_record" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||
_domain=$h
|
||||
_debug "Getting domain id for $h"
|
||||
_domain_id=$(printf '%s' "$_domain_record" | sed -En "s/\{[^}]*\"id\" *: *\"([^\"]*)\"[^}]*\}/\1/p")
|
||||
return 0
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
#################################################################
|
||||
# use: method add_url body
|
||||
_score_rest() {
|
||||
m=$1
|
||||
ep="$2"
|
||||
data="$3"
|
||||
|
||||
_token=$(_get_auth_token)
|
||||
if [ -z "$_token" ]; then
|
||||
_err "BAD key or token $ep"
|
||||
return 1
|
||||
fi
|
||||
_h1_name="X-Auth-Token"
|
||||
export _H1="${_h1_name}: ${_token}"
|
||||
export _H2="Content-Type: application/json"
|
||||
_debug2 "Full URI: " "$SCore_Api/v2${ep}"
|
||||
_debug2 "_H1:" "$_H1"
|
||||
_debug2 "_H2:" "$_H2"
|
||||
if [ "$m" != "GET" ]; then
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$SCore_Api/v2${ep}" "" "$m")"
|
||||
else
|
||||
response="$(_get "$SCore_Api/v2${ep}")"
|
||||
fi
|
||||
# shellcheck disable=SC2181
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
|
||||
_get_auth_token() {
|
||||
# token for v2. Get a token for calling the API
|
||||
_debug "Keystone Token v2"
|
||||
token_v2=$(_readaccountconf_mutable SCoreToken_V2)
|
||||
if [ -n "$token_v2" ]; then
|
||||
# The structure with the token was considered. Let's check its validity
|
||||
# field 1 - SCore_Login_Name
|
||||
# field 2 - token keystone
|
||||
# field 3 - SCore_Login_ID
|
||||
# field 4 - SCore_Project_Name
|
||||
# field 5 - Receipt time
|
||||
# separator - '$_score_sep'
|
||||
_login_name=$(_getfield "$token_v2" 1 "$_score_sep")
|
||||
_token_keystone=$(_getfield "$token_v2" 2 "$_score_sep")
|
||||
_project_name=$(_getfield "$token_v2" 4 "$_score_sep")
|
||||
_receipt_time=$(_getfield "$token_v2" 5 "$_score_sep")
|
||||
_login_id=$(_getfield "$token_v2" 3 "$_score_sep")
|
||||
_debug2 _login_name "$_login_name"
|
||||
_debug2 _login_id "$_login_id"
|
||||
_debug2 _project_name "$_project_name"
|
||||
# check the validity of the token for the user and the project and its lifetime
|
||||
_dt_diff_minute=$((($(date +%s) - _receipt_time) / 60))
|
||||
_debug2 _dt_diff_minute "$_dt_diff_minute"
|
||||
[ "$_dt_diff_minute" -gt "$SCore_Expire" ] && unset _token_keystone
|
||||
if [ "$_project_name" != "$SCore_Project_Name" ] || [ "$_login_name" != "$SCore_Login_Name" ] || [ "$_login_id" != "$SCore_Login_ID" ]; then
|
||||
unset _token_keystone
|
||||
fi
|
||||
_debug "Get exists token"
|
||||
fi
|
||||
if [ -z "$_token_keystone" ]; then
|
||||
# the previous token is incorrect or was not received, get a new one
|
||||
_debug "Update (get new) token"
|
||||
_data_auth="{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"name\":\"${SCore_Login_Name}\",\"domain\":{\"name\":\"${SCore_Login_ID}\"},\"password\":\"${SCore_Pswd}\"}}},\"scope\":{\"project\":{\"name\":\"${SCore_Project_Name}\",\"domain\":{\"name\":\"${SCore_Login_ID}\"}}}}}"
|
||||
export _H1="Content-Type: application/json"
|
||||
_result=$(_post "$_data_auth" "$auth_uri")
|
||||
_token_keystone=$(grep 'x-subject-token' "$HTTP_HEADER" | sed -nE "s/[[:space:]]*x-subject-token:[[:space:]]*([[:print:]]*)(\r*)/\1/p")
|
||||
_dt_curr=$(date +%s)
|
||||
SCoreToken_V2="${SCore_Login_Name}${_score_sep}${_token_keystone}${_score_sep}${SCore_Login_ID}${_score_sep}${SCore_Project_Name}${_score_sep}${_dt_curr}"
|
||||
_saveaccountconf_mutable SCoreToken_V2 "$SCoreToken_V2"
|
||||
fi
|
||||
printf -- "%s" "$_token_keystone"
|
||||
}
|
||||
|
||||
#################################################################
|
||||
# use: [non_save]
|
||||
_score_init_vars() {
|
||||
_non_save="${1}"
|
||||
_debug2 _non_save "$_non_save"
|
||||
|
||||
_debug "First init variables"
|
||||
# time expire token
|
||||
SCore_Expire="${SCore_Expire:-$(_readaccountconf_mutable SCore_Expire)}"
|
||||
if [ -z "$SCore_Expire" ]; then
|
||||
SCore_Expire=1400 # 23h 20 min
|
||||
fi
|
||||
if [ -z "$_non_save" ]; then
|
||||
_saveaccountconf_mutable SCore_Expire "$SCore_Expire"
|
||||
fi
|
||||
# login service user
|
||||
SCore_Login_Name="${SCore_Login_Name:-$(_readaccountconf_mutable SCore_Login_Name)}"
|
||||
if [ -z "$SCore_Login_Name" ]; then
|
||||
SCore_Login_Name=''
|
||||
_err "You did not specify the servercore.com API service user name."
|
||||
_err "Please provide a service user name and try again."
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$_non_save" ]; then
|
||||
_saveaccountconf_mutable SCore_Login_Name "$SCore_Login_Name"
|
||||
fi
|
||||
# user ID
|
||||
SCore_Login_ID="${SCore_Login_ID:-$(_readaccountconf_mutable SCore_Login_ID)}"
|
||||
if [ -z "$SCore_Login_ID" ]; then
|
||||
SCore_Login_ID=''
|
||||
_err "You did not specify the servercore.com API user ID."
|
||||
_err "Please provide a user ID and try again."
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$_non_save" ]; then
|
||||
_saveaccountconf_mutable SCore_Login_ID "$SCore_Login_ID"
|
||||
fi
|
||||
# project name
|
||||
SCore_Project_Name="${SCore_Project_Name:-$(_readaccountconf_mutable SCore_Project_Name)}"
|
||||
if [ -z "$SCore_Project_Name" ]; then
|
||||
SCore_Project_Name=''
|
||||
_err "You did not specify the project name."
|
||||
_err "Please provide a project name and try again."
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$_non_save" ]; then
|
||||
_saveaccountconf_mutable SCore_Project_Name "$SCore_Project_Name"
|
||||
fi
|
||||
# service user password
|
||||
SCore_Pswd="${SCore_Pswd:-$(_readaccountconf_mutable SCore_Pswd)}"
|
||||
if [ -z "$SCore_Pswd" ]; then
|
||||
SCore_Pswd=''
|
||||
_err "You did not specify the service user password."
|
||||
_err "Please provide a service user password and try again."
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$_non_save" ]; then
|
||||
_saveaccountconf_mutable SCore_Pswd "$SCore_Pswd" "12345678"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
Loading…
Reference in a new issue