From 6c38e4210acef1deb2f17b1b6539c798bcbfcb48 Mon Sep 17 00:00:00 2001 From: sanj <67624670+iodrift@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:10:44 -0700 Subject: [PATCH] added cf and ddns for deploying new cloudflare subdomains behind a Caddy reverse-proxy and updating Cloudflare DDNS, respectively --- .DS_Store | Bin 6148 -> 6148 bytes cf | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ddns | 55 ++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 cf create mode 100644 ddns diff --git a/.DS_Store b/.DS_Store index 4e95e0ae875a336ce09224eb64cb6eda6a12d29a..a7ec040ea41fffd2ad97576c27066ea2ee4c1631 100644 GIT binary patch delta 113 zcmZoMXfc?uEOya)1_lNe20ey!hD?T%+ZoZ34QcivnP>jQlvuAsP$T3G$`4qhJ1sR6H c$@#ejKs^i$OcOQ>GJa#5SirZLo#QV*077;gfdBvi diff --git a/cf b/cf new file mode 100644 index 0000000..e47b26c --- /dev/null +++ b/cf @@ -0,0 +1,113 @@ +#!/bin/bash +if [ "$EUID" -ne 0 ]; then + echo "This script must be run as root. Try using 'sudo'." + exit 1 +fi +source /home/sij/.zshrc +source /home/sij/.GLOBAL_VARS +ddns + +# Initialize variables +full_domain=$1 +shift # Shift the arguments to left so we can get remaining arguments as before +caddyIP="" # Optional IP for Caddyfile +port="" + +# Fixed IP for Cloudflare from ip.txt +cloudflareIP=$(cat /home/sij/.services/ip.txt) +api_key=$CF_API_KEY +cf_domains_file=/home/sij/.services/cf_domains.json + +# Usage message +usage() { + echo "Usage: $0 [--ip ] --port " + echo "Note: is required and can be a subdomain or a full domain." + exit 1 +} + +# Parse command-line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --ip|-i) + caddyIP="$2" + shift 2 + ;; + --port|-p) + port="$2" + shift 2 + ;; + *) + usage + ;; + esac +done + +# Check required parameter +if [[ -z "$full_domain" ]] || [[ -z "$port" ]]; then + usage +fi + +# Extract subdomain and domain +subdomain=$(echo "$full_domain" | awk -F"." '{print $1}') +remaining_parts=$(echo "$full_domain" | awk -F"." '{print NF}') +if [ "$remaining_parts" -eq 2 ]; then + # Handle root domain (e.g., env.esq) + domain=$full_domain + subdomain="@" # Use "@" for root domain +else + # Handle subdomain (e.g., sub.env.esq) + domain=$(echo "$full_domain" | sed "s/^$subdomain\.//") +fi + +# Default to localhost for Caddyfile if IP is not provided via --ip +if [[ -z "$caddyIP" ]]; then + caddyIP="localhost" +fi + +# Extract zone_id from JSON file +zone_id=$(jq -r ".\"$domain\".zone_id" "$cf_domains_file") + +# Check if zone_id was successfully retrieved +if [ "$zone_id" == "null" ] || [ -z "$zone_id" ]; then + echo "Error: Zone ID for $domain could not be found." + exit 1 +fi + +# API call setup for Cloudflare A record using the fixed IP from ip.txt +endpoint="https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records" +data="{\"type\":\"A\",\"name\":\"$subdomain\",\"content\":\"$cloudflareIP\",\"ttl\":120,\"proxied\":true}" + +# Make API call +response=$(curl -s -X POST "$endpoint" -H "Authorization: Bearer $api_key" -H "Content-Type: application/json" --data "$data") + +# Parse response +record_id=$(echo "$response" | jq -r '.result.id') +success=$(echo "$response" | jq -r '.success') +error_message=$(echo "$response" | jq -r '.errors[0].message') +error_code=$(echo "$response" | jq -r '.errors[0].code') + +# Function to update Caddyfile with correct indentation +update_caddyfile() { + echo "$full_domain { + reverse_proxy $caddyIP:$port + tls { + dns cloudflare {env.CLOUDFLARE_API_TOKEN} + } +}" >> /etc/caddy/Caddyfile + echo "Configuration appended to /etc/caddy/Caddyfile with correct formatting." +} + +# Check for success or specific error to update Caddyfile +if [ "$success" == "true" ]; then + jq ".\"$domain\".subdomains[\"$full_domain\"] = \"$record_id\"" "$cf_domains_file" > temp.json && mv temp.json "$cf_domains_file" + echo "A record created and cf_domains.json updated successfully." + update_caddyfile +elif [ "$error_message" == "Record already exists." ]; then + echo "Record already exists. Updating Caddyfile anyway." + update_caddyfile +else + echo "Failed to create A record. Error: $error_message (Code: $error_code)" +fi + +echo "Restarting caddy!" +sudo systemctl restart caddy diff --git a/ddns b/ddns new file mode 100644 index 0000000..6a78dff --- /dev/null +++ b/ddns @@ -0,0 +1,55 @@ +#!/bin/bash +source /home/sij/.GLOBAL_VARS + +service="https://am.i.mullvad.net/ip" +# Obtain the current public IP address +#current_ip=$(ssh -n sij@10.13.37.10 curl -s $service) +current_ip=$(curl -s $service) +last_ip=$(cat /home/sij/.services/ip.txt) +api_token=$CF_API_KEY + +# Path to the JSON file with zone IDs, subdomains, and DNS IDs mappings +json_file="/home/sij/.services/cf_domains.json" + +force_update=false + +# Parse command line arguments for --force flag +while [[ "$#" -gt 0 ]]; do + case $1 in + -f|--force) force_update=true ;; + *) echo "Unknown parameter passed: $1"; exit 1 ;; + esac + shift +done + +# Temporary file to store update results +temp_file=$(mktemp) + +# Function to update DNS records +update_dns_record() { + zone_id=$1 + subdomain=$2 + dns_id=$3 + update_result=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$dns_id" \ + -H "Authorization: Bearer $api_token" \ + -H "Content-Type: application/json" \ + --data "{\"type\":\"A\",\"name\":\"$subdomain\",\"content\":\"$current_ip\",\"ttl\":120,\"proxied\":true}") + echo "$update_result" >> "$temp_file" +} + +# Check if IP has changed or --force flag is used +if [ "$current_ip" != "$last_ip" ] || [ "$force_update" = true ]; then + echo $current_ip > /home/sij/.services/ip.txt + # Iterate through each domain in the JSON + /home/sij/miniforge3/bin/jq -r '.[] | .zone_id as $zone_id | .subdomains | to_entries[] | [$zone_id, .key, .value] | @tsv' $json_file | + while IFS=$'\t' read -r zone_id subdomain dns_id; do + update_dns_record "$zone_id" "$subdomain" "$dns_id" + done + # Combine all update results into a single JSON array + /home/sij/miniforge3/bin/jq -s '.' "$temp_file" + # Remove the temporary file + rm "$temp_file" +else + echo "IP address has not changed from ${last_ip}. No action taken." +fi +