From bb3eecfa4af6293760b11917e7f78916b895fa83 Mon Sep 17 00:00:00 2001 From: Maksim Gaidai Date: Fri, 11 Oct 2024 08:59:35 +0300 Subject: [PATCH 1/2] Update debug function Remove double-echo; add enchanced otion for debug echo --- getssl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/getssl b/getssl index 122bd4ea..25b55c13 100755 --- a/getssl +++ b/getssl @@ -1293,8 +1293,7 @@ debug() { # write out debug info if the debug flag has been set if [[ -n ${BATS_RUN_TMPDIR} ]]; then echo "$(date "+%b %d %T") ${FUNCNAME[1]}:${BASH_LINENO[1]}" "$@" else - echo " " - echo "$@" + echo -e "\n$@" fi fi } From df38d77ce54ecce16b012ecef50c8a25152a66c0 Mon Sep 17 00:00:00 2001 From: Maksim Gaidai Date: Fri, 11 Oct 2024 20:07:10 +0300 Subject: [PATCH 2/2] Yandex DNS script added --- dns_scripts/Yandex-README.md | 45 +++++++++++ dns_scripts/dns_yandex | 144 +++++++++++++++++++++++++++++++++++ getssl | 9 ++- 3 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 dns_scripts/Yandex-README.md create mode 100755 dns_scripts/dns_yandex diff --git a/dns_scripts/Yandex-README.md b/dns_scripts/Yandex-README.md new file mode 100644 index 00000000..989e2464 --- /dev/null +++ b/dns_scripts/Yandex-README.md @@ -0,0 +1,45 @@ +# Yandex DNS management via Yandex API + +## Requrements + +- [JQ](https://jqlang.github.io/jq/) command-line JSON processor needed + +## Useful links + +- [Yandex API Access](https://yandex.ru/dev/api360/doc/concepts/access.html) +- [Yandex DNS API](https://yandex.ru/dev/api360/doc/ref/DomainDNSService.html) + +## Yandex Auth parameters in domain config file + +0. Authorize in Your Yandex Account in web browser. You must have Administartor rights +1. Get `OrgId` from Your Comapny Profile - (You have Your first important variable `YANDEX_ORGID` now!) +2. Create Your new application for DNS management - +3. Get `ClientID` for this application on application page +4. Get `OAuthToken` for this application from URL in authtorised browser where `ClientID` code from previous step. This is Your second secret variable `YANDEX_OAUTH`! +5. Define this variables in domain config file + +```bash +# Use the following 3 variables if you want to validate via DNS +VALIDATE_VIA_DNS="true" +DNS_ADD_COMMAND="/usr/share/getssl/dns_scripts/dns_yandex add" +DNS_DEL_COMMAND="/usr/share/getssl/dns_scripts/dns_yandex del" +DNS_WAIT="600" # Waiting 10 minutes. Very-very-very slow distrbution + +# Yandex base DNS +AUTH_DNS_SERVER="77.88.8.8" + +# Yandex Authentication credintals +export YANDEX_ORGID="" +export YANDEX_OAUTH="" +``` + +## Manual run + +- `/usr/share/getssl/dns_scripts/dns_yandex add domain.tld ` - add ` TXT _acme-challenge.domain.tld` record +- `/usr/share/getssl/dns_scripts/dns_yandex add subdomain.domain.tld ` - add ` TXT _acme-challenge.subdomain.domain.tld` record +- `/usr/share/getssl/dns_scripts/dns_yandex del domain.tld ` - delete ` TXT _acme-challenge.domain.tld` record +- `/usr/share/getssl/dns_scripts/dns_yandex del subdomain.domain.tld ` - delete ` TXT _acme-challenge.subdomain.domain.tld` record + +- `/usr/share/getssl/dns_scripts/dns_yandex cleanup domain.tld` - cleanup all dangling `_acme-challenge` records from DNS for +- `/usr/share/getssl/dns_scripts/dns_yandex cleanup subdomain.domain.tld` - cleanup all dangling `_acme-challenge.subdomain` records from DNS for +- `/usr/share/getssl/dns_scripts/dns_yandex cleanup [subdomain.]domain.tld ` - cleanup all dangling `_acme-challenge` domain or subdomain records with `` diff --git a/dns_scripts/dns_yandex b/dns_scripts/dns_yandex new file mode 100755 index 00000000..6bcebc7c --- /dev/null +++ b/dns_scripts/dns_yandex @@ -0,0 +1,144 @@ +#!/bin/bash + +### Functions +check() { # Verify that required parameters are set + IFS=$'\n' + for var in ${check_vars[@]}; do + var_name=$(echo $var | awk -F, '{ print $1 }') + err_msg=$( echo $var | awk -F, '{ print $2 }') + [ -z "${!var_name}" ] && error_exit "variable '${var_name}' empty. Error:$err_msg" + done +} + +add() { # Add challenge domain to DNS + debug "create domain '$acme_domain'" + response=`curl --silent \ + -X POST https://api360.yandex.net/directory/v1/org/${orgId}/domains/${domain}/dns \ + -H "Authorization: OAuth ${OAuth}" \ + -H "Content-Type: application/json" \ + -d '{"name":"'"$acme_domain"'","type":"TXT","ttl":"3600","text":"'"$token"'"}'` + debug "Asked record: {\"name\":\"$acme_domain\",\"type\":\"TXT\",\"ttl\":\"3600\",\"text\":\"$token\"}" + debug "Recieved response: \"$response\"" + recordId=$(echo $response | jq .recordId) + if [ "$recordId" != "null" ]; then + # Save recordId for cleanup later + mkdir -p -m 0700 $(dirname $recordIdFile) + echo $response >> $recordIdFile + debug "File '$recordIdFile' created/updated:\n$(sed -E '/^#/d;/^\s*$/d' $recordIdFile)" + else + err_code=$(echo $response | jq .code) + err_message=$(echo $response | jq .message) + error_exit "error code: $err_code -- $err_message" + fi +} + +del() { # Delete all or specific challenge domains from DNS + if [[ "$1" == "cleanup" ]]; then # Cleanup records for specific domain + if [ -z "$subdomain" ]; then # Delete all records for domain if 'domain.tld' specimied + # Get ALL recordIDs from file + debug "all '$domain' domain records will be removed" + recordIds=`sed -E '/^#/d;/^\s*$/d' $recordIdFile | jq .recordId` + else # Delete all records for specific subdomain only if 'subdomain.domain.tld' specified + # Get ALL recordIDs from file for specific domain + debug "all '$acme_domain' domain records will be removed" + recordIds=`sed -E '/^#/d;/^\s*$/d' $recordIdFile | jq "select(.name == \"$acme_domain\") | .recordId"` + fi + else # Delete specific records for specific domain or subdomain specified challenge token + # Get specific combinations of domain and token + debug "'$acme_domain' domain records with token '$token' will be removed" + recordIds=`sed -E '/^#/d;/^\s*$/d' $recordIdFile | jq "select(.name == \"$acme_domain\" and .text == \"$token\") | .recordId"` + fi + # Check selected exist records + [ -z "$recordIds" ] && error_exit "can't parse '$recordIdFile' file or matched recordIds not found" + # Delete selected records if exist + unset not_removed + for recordId in $recordIds; do + debug "removeing record '$recordId'" + del_result=`curl -H "Authorization: OAuth ${OAuth}" --silent \ + -X DELETE https://api360.yandex.net/directory/v1/org/${orgId}/domains/${domain}/dns/${recordId}` + if [ "$del_result" == "{}" ]; then + sed -i "/$recordId/d" $recordIdFile # Remove record from file if it was removed from DNS + unset del_msg + else + not_removed=${not_removed:+$not_removed, }$recordId + del_msg=' NOT' + fi + debug "'$recordId' was$rm_msg removed from DNS and file '$recordIdFile'" + done + [ -n "$not_removed" ] && error_exit "Something went wrong. Server says: '$del_result' This records was NOT removed: '$not_removed'" +} + +### Requires +requires jq + +### Presets +# Yandex Authentication credintals +orgId=${YANDEX_ORGID:-''} +OAuth=${YANDEX_OAUTH:-''} + +# Let's Encrypt DNS validtaion token +token=$3 + +# Split FQDN to domain and subdomain. Specific for Yandex API +fqdn=`echo $2 | grep -Po '^(?:(?!-)[a-z0-9-]{0,62}[a-z0-9]\.)+[a-z]{2,}$'` +domain=`echo $fqdn | grep -Po '(?!-)[a-z0-9-]{0,62}[a-z0-9]\.[a-z]{2,}$'` +subdomain=`echo $fqdn | sed -E 's/(\.?[^.]+){2}$//'` + +acme_domain="_acme-challenge${subdomain:+.$subdomain}" +recordIdFile=${WORKING_DIR:-/tmp}/getssl_yandex_dns_records + +debug "FQDN: '$fqdn' +Validation token: '$token' +Asked domain: '$domain' +Asked subdomain: '$subdomain' +Record ID file: '$recordIdFile'" + +# Yandex API returns JSON with recordId as successful result +# It's required for remove this record later. Yandex API cant't remove records by names, by recordId only +[ -f "$recordIdFile" ] || echo -n " +# DON'T EDIT! DON'T DELETE! +# This is active Yandex DNS challenge records +# Don't remove manually +# Just run \`$(cd "$(dirname "$0")" || exit; pwd -P;) cleanup [subdomain.]domain.tld\` + +" > "$recordIdFile" + +### Main process +case "$1" in + "add" ) + check_vars=( + "fqdn, No or invalid FQDN" + "domain, No or invalid DOMAIN" + "token, No challenge token" + "orgId, No Yandex OrgID" + "OAuth, No Yandex OAuth" + ) + check + add + ;; + + "del" ) + check_vars=( + "domain, No or invalid DOMAIN" + "token, No challenge token" + "orgId, No Yandex OrgID" + "OAuth, No Yandex OAuth" + ) + check + del + ;; + + "cleanup" ) + check_vars=( + "domain, No or invalid DOMAIN" + "orgId, No Yandex OrgID" + "OAuth, No Yandex OAuth" + ) + check + del cleanup + ;; + + * ) + echo "Unknown command '$1'. Valid commands: (add|del|cleanup)" + ;; +esac diff --git a/getssl b/getssl index 25b55c13..05f92a86 100755 --- a/getssl +++ b/getssl @@ -972,6 +972,7 @@ clean_up() { # Perform pre-exit housekeeping fi fi } +export -f clean_up # for use in DNS scripts copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. cert=$1 # descriptive name, just used for display @@ -1297,6 +1298,7 @@ debug() { # write out debug info if the debug flag has been set fi fi } +export -f debug # for use in DNS scripts error_exit() { # give error message on error exit echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2 @@ -1306,6 +1308,7 @@ error_exit() { # give error message on error exit clean_up exit 1 } +export -f error_exit # for use in DNS scripts find_dns_utils() { HAS_NSLOOKUP=false @@ -1365,7 +1368,7 @@ add_dns_rr() { # shellcheck disable=SC2018,SC2019 lower_d=$(printf '%s' "${d#\*.}" | tr 'A-Z' 'a-z') debug "adding DNS RR via command: ${DNS_ADD_COMMAND} ${lower_d} ${auth_key}" - eval "${DNS_ADD_COMMAND}" "${lower_d}" "${auth_key}" + eval "PROGNAME=$PROGNAME _USE_DEBUG=$_USE_DEBUG WORKING_DIR=$WORKING_DIR ${DNS_ADD_COMMAND}" "${lower_d}" "${auth_key}" } del_dns_rr() { @@ -1375,7 +1378,7 @@ del_dns_rr() { # shellcheck disable=SC2018,SC2019 lower_d=$(printf '%s' "${d#\*.}" | tr 'A-Z' 'a-z') debug "removing DNS RR via command: ${DNS_DEL_COMMAND} ${lower_d} ${auth_key}" - eval "${DNS_DEL_COMMAND}" "${lower_d}" "${auth_key}" + eval "PROGNAME=$PROGNAME _USE_DEBUG=$_USE_DEBUG WORKING_DIR=$WORKING_DIR ${DNS_DEL_COMMAND}" "${lower_d}" "${auth_key}" } fulfill_challenges() { @@ -2437,6 +2440,7 @@ requires() { # check if required function is available fi fi } +export -f requires # for use in DNS scripts set_server_type() { # uses SERVER_TYPE to set REMOTE_PORT and REMOTE_EXTRA if [[ ${SERVER_TYPE} == "https" ]] || [[ ${SERVER_TYPE} == "webserver" ]]; then @@ -2679,6 +2683,7 @@ traceback() { # Print function traceback done return 0 } +export -f traceback # for use in DNS scripts urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and '/' replaced with '_' openssl base64 -e | tr -d '\n\r' | os_esed -e 's:=*$::g' -e 'y:+/:-_:'