From 04558d57ded7ccac35bb5ded75ca177a72a897ec Mon Sep 17 00:00:00 2001 From: Andrew Stowell Date: Thu, 30 Nov 2023 12:19:12 -0500 Subject: [PATCH] new cloudflare script, to ease using their api. --- cloudflare.sh | 180 ++++++++++++++++++++++++++++++++++++++++++++++ config.example.sh | 4 ++ discord.sh | 2 +- 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100755 cloudflare.sh diff --git a/cloudflare.sh b/cloudflare.sh new file mode 100755 index 0000000..5b22685 --- /dev/null +++ b/cloudflare.sh @@ -0,0 +1,180 @@ +#!/bin/bash + +quiet=false +dryrun=false + +print_usage() { + echo '"-t": test (dry-run) flag, display what would be updated, without committing the change' + echo '"-q": quiet flag, to suppress "already set to" messages' + echo '"-f": query filters, see: https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-list-dns-records#Query-Parameters' + echo 'when using regular expressions, use "\1" for first capture group, no look-forward or look-behind, nor non-greedy wildcards' + echo 'content can use the following tags: and which will be replaced with equivalent ip address, each domain tag can be used once' + echo "Usage: [token=] [zone=] [prefix=] $0 [-q] [-f 'query-filters'] ('content' | 'regex-replace' 'regex-find')" +} + +# handle all arguments provided +while getopts 'f:qt' flag; do + case "$flag" in + f) + filters="$OPTARG" + ;; + q) + quiet=true + ;; + t) + dryrun=true + ;; + *) + echo "unknown option provided '$flag'" + print_usage + exit 1 + ;; + esac +done +shift "$((OPTIND-1))" + +content="$1" +regex="$2" + +# load in defaults from config +scriptRoot=$(dirname "$0") +configFile="$scriptRoot/config.sh" +if [[ -f "$configFile" ]]; then + source "$configFile" + + # you can have a prefix in your config, and override it for a specific call + if [[ -z "$prefix" ]]; then + prefix="$CLOUDFLARE_FILTER_PREFIX" + fi + if [[ -n "$prefix" ]]; then + filters="$prefix$filters" + fi + if [[ -z "$zone" ]]; then + zone="$CLOUDFLARE_ZONE" + fi + if [[ -z "$token" ]]; then + token="$CLOUDFLARE_TOKEN" + fi +fi + +# stop if we do not have the required information +if [ -z "$zone" -o -z "$content" -o -z "$token" ]; then + echo 'token, zone, and content are all required' + print_usage + exit 1 +fi + +# --- update content --- +# "" becomes current public ipv4 +if [[ "$content" = *''* ]]; then + ipv4=$("$scriptRoot/4-public-ip.sh") + content=$(echo "$content" | sed "s//$ipv4/g") +fi + +# limit 1 +# "" becomes first ipv4 of given domain +if [[ "$content" = *']+(?=\>)') + ipv4=$(dig +short A "$domain" | head -n 1) + content=$(echo "$content" | sed "s//$ipv4/g") +fi + +# "" becomes current public ipv6 +if [[ "$content" = *''* ]]; then + ipv6=$("$scriptRoot/6-public-ip.sh") + content=$(echo "$content" | sed "s//$ipv6/g") +fi + +# limit 1 +# "" becomes first ipv6 of given domain +if [[ "$content" = *']+(?=\>)') + ipv6=$(dig +short AAAA "$domain" | head -n 1) + content=$(echo "$content" | sed "s//$ipv6/g") +fi + +# --- update regex --- +# "" becomes current public ipv4 +if [[ "$regex" = *''* ]]; then + ipv4=$("$scriptRoot/4-public-ip.sh") + regex=$(echo "$regex" | sed "s//$ipv4/g") +fi + +# limit 1 +# "" becomes first ipv4 of given domain +if [[ "$regex" = *']+(?=\>)') + ipv4=$(dig +short A "$domain" | head -n 1) + regex=$(echo "$regex" | sed "s//$ipv4/g") +fi + +# "" becomes current public ipv6 +if [[ "$regex" = *''* ]]; then + ipv6=$("$scriptRoot/6-public-ip.sh") + regex=$(echo "$regex" | sed "s//$ipv6/g") +fi + +# limit 1 +# "" becomes first ipv6 of given domain +if [[ "$regex" = *']+(?=\>)') + ipv6=$(dig +short AAAA "$domain" | head -n 1) + regex=$(echo "$regex" | sed "s//$ipv6/g") +fi + +# --- get matching dns records --- +results=$(curl --silent --request GET \ + --url "https://api.cloudflare.com/client/v4/zones/$zone/dns_records?$filters" \ + --header 'Content-Type: application/json' \ + --header "Authorization: Bearer $token" ) + +if [[ -z "$regex" ]]; then + newContent="$content" +fi + +while read -r result; do + if [[ -z "$result" ]]; then + echo 'No DNS Records found to update, stopping.' + exit 1 + fi + # echo "$result" | jq -C '.' + + id=$(echo "$result" | jq -r '.id') + oldContent=$(echo "$result" | jq -r '.content') + type=$(echo "$result" | jq -r '.type') + name=$(echo "$result" | jq -r '.name') + if [[ -n "$regex" ]]; then + newContent=$(echo "$oldContent" | sed -E "s/$regex/$content/g") + # echo "current '$oldContent'" + # echo "find '$regex'" + # echo "replace '$content'" + # echo "new '$newContent'" + fi + + if [[ "$newContent" = "$oldContent" ]]; then + if [[ "$quiet" = false ]]; then + echo "$type:$name is already set to '$newContent'" + fi + else + echo "updating $type:$name" + echo "now '$newContent'" + echo "was '$oldContent'" + + + if [[ "$dryrun" = true ]]; then + echo 'true *actually just a dry-run*' + elif [[ -z "$newContent" ]]; then + echo 'false empty content has no effect, update skipped' + else + update=$(curl --silent --request PATCH \ + --url "https://api.cloudflare.com/client/v4/zones/$zone/dns_records/$id" \ + --header 'Content-Type: application/json' \ + --header "Authorization: Bearer $token" \ + --data "{\"content\": \"$newContent\"}" ) + + # display if successful, and any messages + echo "$update" | jq -r '.success,.messages[]' + fi + fi +done <<< $(echo "$results" | jq -c '.result[]') + diff --git a/config.example.sh b/config.example.sh index eefe9a4..b3af44e 100644 --- a/config.example.sh +++ b/config.example.sh @@ -23,6 +23,10 @@ PRUNE_DOWN_TO=0 SERVER_DOMAIN="localhost" +CLOUDFLARE_TOKEN="" +CLOUDFLARE_ZONE="" +CLOUDFLARE_FILTER_PREFIX="comment.contains=managed+by+$SERVER_DOMAIN&" + DISCORD_SERVER_NAME="" DISCORD_TITLE_SUFFIX="$DISCORD_SERVER_NAME" diff --git a/discord.sh b/discord.sh index 037583d..1ed3cdd 100755 --- a/discord.sh +++ b/discord.sh @@ -20,7 +20,7 @@ colors=( ) print_usage() { - echo "Usage: $0 [-h hook-url], [-c color], [-d [distinct-name]] [-t title] (message... | -z | < file.msg)" + echo "Usage: $0 [-h hook-url] [-c color] [-d [distinct-name]] [-t title] (message... | -z | < file.msg)" } # handle all arguments provided