From 770277c7ad72b7c78ebcfdec8ba5a7ac4a5089c8 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Thu, 16 Jan 2020 18:27:11 +0100 Subject: [PATCH 1/4] Add support for CloudDNS --- dns_scripts/dns_add_clouddns | 94 ++++++++++++++++++++++++++++++++ dns_scripts/dns_del_clouddns | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100755 dns_scripts/dns_add_clouddns create mode 100755 dns_scripts/dns_del_clouddns diff --git a/dns_scripts/dns_add_clouddns b/dns_scripts/dns_add_clouddns new file mode 100755 index 0000000..5236269 --- /dev/null +++ b/dns_scripts/dns_add_clouddns @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# Need to add your email address and API key to clouddns below or set as env variables +email=${CLOUDDNS_EMAIL:-''} +password=${CLOUDDNS_PASSWORD:-''} +client=${CLOUDDNS_CLIENT:-''} + +# This script adds a token to clouddns DNS for the ACME challenge +# usage dns_add_clouddns "domain name" "token" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in clouddns etc) + +fulldomain="${1}" +token="${2}" +API='https://admin.vshosting.cloud/clouddns' +LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$token" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi +if [[ -z "$email" ]]; then + echo "CLOUDDNS_EMAIL (email) parameter not set" + exit 1 +fi +if [[ -z "$password" ]]; then + echo "CLOUDDNS_PASSWORD (password) parameter not set" + exit 1 +fi +if [[ -z "$client" ]]; then + echo "CLOUDDNS_CLIENT (id) parameter not set" + exit 1 +fi + +# Login to clouddns to get accessToken +resp=$(curl --silent -X POST -H 'Content-Type: application/json' "$LOGIN_API" \ + --data "{\"email\": \"$email\", \"password\": \"$password\"}") +re='"accessToken":"([^,]*)",' # Match access token +if [[ "${resp// }" =~ $re ]]; then + access_token="${BASH_REMATCH[1]}" +fi +if [[ -z "$access_token" ]]; then + echo 'Could not get access token; check your credentials' + exit 3 +fi +curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) + +# Get main domain +domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') + +# Get domain id +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ + --data "{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}, {\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}") +re='domainType":"[^"]*","id":"([^,]*)",' # Match domain id +if [[ "${resp//[$'\t\r\n ']}" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your CloudDNS account' + exit 3 +fi + +# Add challenge record +txt_record="_acme-challenge.$domain_root." +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/record-txt" \ + --data "{\"type\":\"TXT\",\"name\":\"$txt_record\",\"value\":\"$token\",\"domainId\":\"$domain_id\"}") + +# If adding record failed (error:) then print error message +if [[ "${resp// }" == *'"error"'* ]]; then + if [[ "${resp// }" == *'"code":4136'* ]]; then + echo "DNS challenge token already exists" + exit + fi + re='"message":"([^"]+)"' + if [[ "$resp" =~ $re ]]; then + echo "Error: DNS challenge not added: ${BASH_REMATCH[1]}" + exit 3 + else + echo "Error: DNS challenge not added: unknown error - ${resp}" + exit 3 + fi +fi + +# Publish challenge record +resp=$(curl --silent "${curl_params[@]}" -X PUT "$API/domain/$domain_id/publish" \ + --data "{\"soaTtl\":300}") diff --git a/dns_scripts/dns_del_clouddns b/dns_scripts/dns_del_clouddns new file mode 100755 index 0000000..ddac2b6 --- /dev/null +++ b/dns_scripts/dns_del_clouddns @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# Need to add your email address and API key to clouddns below or set as env variables +email=${CLOUDDNS_EMAIL:-''} +password=${CLOUDDNS_PASSWORD:-''} +client=${CLOUDDNS_CLIENT:-''} + +# This script adds a token to clouddns DNS for the ACME challenge +# usage dns_add_clouddns "domain name" "token" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in clouddns etc) + +fulldomain="${1}" +token="${2}" +API='https://admin.vshosting.cloud/clouddns' +LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$token" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi +if [[ -z "$email" ]]; then + echo "CLOUDDNS_EMAIL (email) parameter not set" + exit 1 +fi +if [[ -z "$password" ]]; then + echo "CLOUDDNS_PASSWORD (password) parameter not set" + exit 1 +fi +if [[ -z "$client" ]]; then + echo "CLOUDDNS_CLIENT (id) parameter not set" + exit 1 +fi + +# Login to clouddns to get accessToken +resp=$(curl --silent -X POST -H 'Content-Type: application/json' "$LOGIN_API" \ + --data "{\"email\": \"$email\", \"password\": \"$password\"}") +re='"accessToken":"([^,]*)",' # Match access token +if [[ "${resp// }" =~ $re ]]; then + access_token="${BASH_REMATCH[1]}" +fi +if [[ -z "$access_token" ]]; then + echo 'Could not get access token; check your credentials' + exit 3 +fi +curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) + +# Get main domain and challenge record +domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') +txt_record="_acme-challenge.$domain_root." + +# Get domain id +curl_domainid_body="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}, {\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}" +resp=$(curl --silent "${curl_params[@]}" -X POST -d "$curl_domainid_body" "$API/domain/search") +re='domainType":"[^"]*","id":"([^,]*)",' # Find result section +if [[ "${resp//[$'\t\r\n ']}" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your CloudDNS account' + exit 3 +fi + +# Get challenge record ID +resp=$(curl --silent "${curl_params[@]}" -X GET "$API/domain/$domain_id" ) +re="\"lastDomainRecordList\".*\"id\":\"([^,]*)\"[^}]*\"name\":\"$txt_record\"," # Match domain id +if [[ "${resp//[$'\t\r\n ']}" =~ $re ]]; then + record_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$record_id" ]]; then + echo 'Challenge record does not exist' + exit 3 +fi + +# Remove challenge record +resp=$(curl --silent "${curl_params[@]}" -X DELETE "$API/record/$record_id") + +# If removing record failed (error:) then print error message +if [[ "${resp// }" == *'"error"'* ]]; then + re='"message":"([^"]+)"' + if [[ "$resp" =~ $re ]]; then + echo "Error: DNS challenge not removed: ${BASH_REMATCH[1]}" + exit 3 + else + echo "Error: DNS challenge not removed: unknown error - ${resp}" + exit 3 + fi +fi + +# Publish challenge record deletion +resp=$(curl --silent "${curl_params[@]}" -X PUT "$API/domain/$domain_id/publish" \ + --data "{\"soaTtl\":300}") From 6c9f0e7655beb760b33a764f60b4ebd5c49ee445 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Thu, 16 Jan 2020 18:30:24 +0100 Subject: [PATCH 2/4] Update changelog --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index e52b3d0..f1e2989 100755 --- a/getssl +++ b/getssl @@ -196,6 +196,7 @@ # 2020-01-07 #464 and #486 "json was blank" (change all curl request to use POST-as-GET) # 2020-01-08 Error and exit if rate limited, exit if curl returns nothing # 2020-01-10 Change domain and getssl templates to v2 (2.15) +# 2020-01-16 Add support for CloudDNS # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From b8b70f7ceea852bc6194a32cb9959d68c81efeb1 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Thu, 16 Jan 2020 18:49:08 +0100 Subject: [PATCH 3/4] Support certificates with SANs --- dns_scripts/dns_add_clouddns | 2 +- dns_scripts/dns_del_clouddns | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dns_scripts/dns_add_clouddns b/dns_scripts/dns_add_clouddns index 5236269..a4e3f81 100755 --- a/dns_scripts/dns_add_clouddns +++ b/dns_scripts/dns_add_clouddns @@ -69,7 +69,7 @@ if [[ -z "$domain_id" ]]; then fi # Add challenge record -txt_record="_acme-challenge.$domain_root." +txt_record="_acme-challenge.$fulldomain." resp=$(curl --silent "${curl_params[@]}" -X POST "$API/record-txt" \ --data "{\"type\":\"TXT\",\"name\":\"$txt_record\",\"value\":\"$token\",\"domainId\":\"$domain_id\"}") diff --git a/dns_scripts/dns_del_clouddns b/dns_scripts/dns_del_clouddns index ddac2b6..ec22c91 100755 --- a/dns_scripts/dns_del_clouddns +++ b/dns_scripts/dns_del_clouddns @@ -54,7 +54,7 @@ curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: applica # Get main domain and challenge record domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') -txt_record="_acme-challenge.$domain_root." +txt_record="_acme-challenge.$fulldomain." # Get domain id curl_domainid_body="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}, {\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}" From b1e177f45ea50e84be5f979cad967d823c37a637 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Wed, 22 Jan 2020 05:26:01 +0100 Subject: [PATCH 4/4] Handle domains of .co.uk type --- dns_scripts/dns_add_clouddns | 11 ++++++++++- dns_scripts/dns_del_clouddns | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/dns_scripts/dns_add_clouddns b/dns_scripts/dns_add_clouddns index a4e3f81..f20d1ab 100755 --- a/dns_scripts/dns_add_clouddns +++ b/dns_scripts/dns_add_clouddns @@ -53,7 +53,16 @@ fi curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) # Get main domain -domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ + --data "{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}]}") +domain_slice="$fulldomain" +while [[ -z "$domain_root" ]]; do + if [[ "${resp// }" =~ domainName\":\"$domain_slice ]]; then + domain_root="$domain_slice" + _debug domain_root "$domain_root" + fi + domain_slice="${domain_slice#[^\.]*.}" +done # Get domain id resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ diff --git a/dns_scripts/dns_del_clouddns b/dns_scripts/dns_del_clouddns index ec22c91..0b25121 100755 --- a/dns_scripts/dns_del_clouddns +++ b/dns_scripts/dns_del_clouddns @@ -53,7 +53,16 @@ fi curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) # Get main domain and challenge record -domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ + --data "{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}]}") +domain_slice="$fulldomain" +while [[ -z "$domain_root" ]]; do + if [[ "${resp// }" =~ domainName\":\"$domain_slice ]]; then + domain_root="$domain_slice" + _debug domain_root "$domain_root" + fi + domain_slice="${domain_slice#[^\.]*.}" +done txt_record="_acme-challenge.$fulldomain." # Get domain id