Skip to content

Instantly share code, notes, and snippets.

@troykelly
Created October 7, 2024 01:59
Show Gist options
  • Save troykelly/37b2209c38871f84ae1d24dafd9045a5 to your computer and use it in GitHub Desktop.
Save troykelly/37b2209c38871f84ae1d24dafd9045a5 to your computer and use it in GitHub Desktop.
Bulk Cloudflare Encryption Mode Management Script

Cloudflare Encryption Mode Management Script

This script retrieves the list of domains from a Cloudflare account using the provided API credentials, checks their current encryption modes, and optionally updates them based on user input. A markdown report is generated displaying previous and updated statuses.

Requirements

  • Bash (Unix shell)
  • curl command-line tool
  • jq JSON processor

Environment Variables

Ensure the following environment variables are set before running the script:

  • CF_API_EMAIL: Your Cloudflare account email address.
  • CF_API_KEY: Your Cloudflare API key.

Example:

export CF_API_EMAIL="[email protected]"
export CF_API_KEY="your_api_key"

Usage

./script.sh [--change-to MODE] [--verbose]

Options

  • --change-to MODE: Desired encryption mode to set for domains not already set to this mode. Valid modes are off, flexible, full, strict, or origin_pull.

  • --verbose, -v: Enable verbose output to show detailed interactions with the Cloudflare API.

Examples

  • Get the current encryption modes for all domains:

    ./script.sh
  • Change encryption mode to full for all domains not already set to full:

    ./script.sh --change-to full
  • Change encryption mode to strict with verbose output:

    ./script.sh --change-to strict --verbose

Output

The script outputs a markdown-formatted table displaying each domain's previous and updated encryption modes.

Example output:

+------------------------------+-----------------+-----------------+
|           Domain             |  Previous Mode  |  Updated Mode   |
+------------------------------+-----------------+-----------------+
|          example.com         |     flexible    |      full       |
|          example.org         |      full       |      full       |
|          example.net         |      strict     |      full       |
+------------------------------+-----------------+-----------------+

Notes and Warnings

  • Slow Performance with Many Domains: If your Cloudflare account has a large number of domains, the script may take some time to complete as it processes each domain individually. The script may seem unresponsive during this time. To monitor progress, use the --verbose option to enable detailed output.

  • Changes to Origin Server Security Configuration: This script modifies the encryption mode between Cloudflare and your origin servers. Changing encryption modes can have significant security implications for how data is transmitted between Cloudflare and your servers. Ensure you understand the risks before making changes, and verify that your origin servers are configured correctly to support the specified encryption mode.

Author

Code History

  • 07 October 2024: Version 1.3 - Adjusted function outputs to redirect messages to STDERR.
  • Previous versions:
    • Version 1.2: Improved table formatting with padded and centered values.
    • Version 1.1: Added verbosity switch to output server interactions.
    • Version 1.0: Initial script creation.

Licence

This project is licensed under the Apache License 2.0. See the LICENSE file for details.

#!/usr/bin/env bash
#
# Script Name: cloudflare_encryption_mode.sh
# Description: This script retrieves the list of domains from a Cloudflare account using the provided API credentials, checks their current encryption modes, and optionally updates them based on user input. A markdown report is generated displaying previous and updated statuses.
# Author: Troy Kelly
# Contact: [email protected]
# Date: 07 October 2024
# Version: 1.3
#
# History:
# - Version 1.0: Initial script creation.
# - Version 1.1: Added verbosity switch to output server interactions.
# - Version 1.2: Improved table formatting with padded and centered values.
# - Version 1.3: Adjusted function outputs to redirect messages to STDERR.
# Exit immediately if a command exits with a non-zero status.
set -e
# Trap signals and handle cleanup
trap 'cleanup' EXIT
trap 'echo "Script interrupted." >&2; exit 1' INT TERM
# Functions
# Function to perform cleanup on exit
cleanup() {
# Placeholder for any cleanup tasks
:
}
# Function to display usage information
usage() {
echo "Usage: $0 [--change-to MODE] [--verbose]" >&2
echo "Options:" >&2
echo " --change-to MODE Desired encryption mode (off, flexible, full, strict, origin_pull)" >&2
echo " --verbose, -v Enable verbose output" >&2
exit 1
}
# Check for required environment variables
if [[ -z "$CF_API_EMAIL" ]] || [[ -z "$CF_API_KEY" ]]; then
echo "Error: CF_API_EMAIL and CF_API_KEY environment variables must be set." >&2
exit 1
fi
# Parse command-line arguments
CHANGE_TO=""
VERBOSE=false
while [[ $# -gt 0 ]]; do
case "$1" in
--change-to)
if [[ -n "$2" ]]; then
CHANGE_TO="$2"
shift 2
else
echo "Error: --change-to option requires an argument." >&2
usage
fi
;;
--verbose|-v)
VERBOSE=true
shift
;;
*)
echo "Error: Invalid option $1" >&2
usage
;;
esac
done
# Validate the CHANGE_TO value if provided
VALID_MODES=("off" "flexible" "full" "strict" "origin_pull")
if [[ -n "$CHANGE_TO" ]] && [[ ! " ${VALID_MODES[*]} " =~ " ${CHANGE_TO} " ]]; then
echo "Error: Invalid encryption mode '${CHANGE_TO}'. Valid modes are: ${VALID_MODES[*]}." >&2
exit 1
fi
# Variables
API_ENDPOINT="https://api.cloudflare.com/client/v4"
AUTH_HEADERS=(
-H "X-Auth-Email: $CF_API_EMAIL"
-H "X-Auth-Key: $CF_API_KEY"
-H "Content-Type: application/json"
)
DOMAINS=()
REPORT_LINES=()
# Column widths
DOMAIN_WIDTH=30
MODE_WIDTH=15
# Function to center text within a given width
center_text() {
local width=$1
local text="$2"
local text_length=${#text}
if [[ $text_length -ge $width ]]; then
echo "$text"
else
local padding_total=$((width - text_length))
local padding_left=$((padding_total / 2))
local padding_right=$((padding_total - padding_left))
printf "%*s%s%*s" $padding_left "" "$text" $padding_right ""
fi
}
# Function to generate table separator
generate_separator() {
local width=$1
printf '+%0.s-' $(seq 1 $width)
}
# Function to get list of zones (domains)
get_zones() {
local page=1
while true; do
if [[ "$VERBOSE" == true ]]; then
echo "Fetching zones: page $page" >&2
fi
local response
response=$(curl -s "${AUTH_HEADERS[@]}" -G "${API_ENDPOINT}/zones" --data-urlencode "page=${page}" --data-urlencode "per_page=50")
if [[ "$VERBOSE" == true ]]; then
echo "Response from fetching zones (page $page):" >&2
echo "$response" | jq '.' >&2
fi
local success
success=$(echo "$response" | jq -r '.success')
if [[ "$success" != "true" ]]; then
echo "Error retrieving zones: $(echo "$response" | jq -r '.errors[]?.message')" >&2
exit 1
fi
local result_count
result_count=$(echo "$response" | jq '.result | length')
if [[ "$result_count" -eq 0 ]]; then
break
fi
local zone_ids
zone_ids=$(echo "$response" | jq -r '.result[] | {id, name} | @base64')
while IFS= read -r zone; do
DOMAINS+=("$zone")
done <<< "$zone_ids"
((page++))
done
}
# Function to get current encryption mode for a zone
get_encryption_mode() {
local zone_id="$1"
if [[ "$VERBOSE" == true ]]; then
echo "Getting encryption mode for zone ID: $zone_id" >&2
fi
local response
response=$(curl -s "${AUTH_HEADERS[@]}" -X GET "${API_ENDPOINT}/zones/${zone_id}/settings/ssl")
if [[ "$VERBOSE" == true ]]; then
echo "Response from getting encryption mode for zone ID $zone_id:" >&2
echo "$response" | jq '.' >&2
fi
echo "$response" | jq -r '.result.value'
}
# Function to update encryption mode for a zone
update_encryption_mode() {
local zone_id="$1"
local mode="$2"
if [[ "$VERBOSE" == true ]]; then
echo "Updating encryption mode for zone ID: $zone_id to '$mode'" >&2
fi
local response
response=$(curl -s "${AUTH_HEADERS[@]}" -X PATCH "${API_ENDPOINT}/zones/${zone_id}/settings/ssl" --data "{\"value\":\"${mode}\"}")
if [[ "$VERBOSE" == true ]]; then
echo "Response from updating encryption mode for zone ID $zone_id:" >&2
echo "$response" | jq '.' >&2
fi
local success
success=$(echo "$response" | jq -r '.success')
if [[ "$success" != "true" ]]; then
echo "Error updating encryption mode for zone ${zone_id}: $(echo "$response" | jq -r '.errors[]?.message')" >&2
exit 1
fi
}
# Main script execution
main() {
get_zones
# Table header
local header_domain=$(center_text $DOMAIN_WIDTH "Domain")
local header_prev_mode=$(center_text $MODE_WIDTH "Previous Mode")
local header_updated_mode=$(center_text $MODE_WIDTH "Updated Mode")
local separator="+$(printf '%0.s-' $(seq 1 $((DOMAIN_WIDTH + 2))))+$(printf '%0.s-' $(seq 1 $((MODE_WIDTH + 2))))+$(printf '%0.s-' $(seq 1 $((MODE_WIDTH + 2))))+"
REPORT_LINES+=("$separator")
REPORT_LINES+=("| $header_domain | $header_prev_mode | $header_updated_mode |")
REPORT_LINES+=("$separator")
for zone_base64 in "${DOMAINS[@]}"; do
# Decode the base64 encoded JSON
local zone_json
zone_json=$(echo "$zone_base64" | base64 --decode)
local zone_id
zone_id=$(echo "$zone_json" | jq -r '.id')
local zone_name
zone_name=$(echo "$zone_json" | jq -r '.name')
if [[ "$VERBOSE" == true ]]; then
echo "Processing zone: $zone_name (ID: $zone_id)" >&2
fi
# Get current encryption mode
local current_mode
current_mode=$(get_encryption_mode "$zone_id")
local updated_mode="$current_mode"
# Update encryption mode if necessary
if [[ -n "$CHANGE_TO" ]] && [[ "$current_mode" != "$CHANGE_TO" ]]; then
update_encryption_mode "$zone_id" "$CHANGE_TO"
updated_mode="$CHANGE_TO"
fi
# Center values
local centered_domain=$(center_text $DOMAIN_WIDTH "$zone_name")
local centered_current_mode=$(center_text $MODE_WIDTH "$current_mode")
local centered_updated_mode=$(center_text $MODE_WIDTH "$updated_mode")
# Append to report
REPORT_LINES+=("| $centered_domain | $centered_current_mode | $centered_updated_mode |")
done
# Add table footer
REPORT_LINES+=("$separator")
# Output the report
for line in "${REPORT_LINES[@]}"; do
echo "$line"
done
}
# Execute the main function
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment