Skip to content

Instantly share code, notes, and snippets.

@leodido
Last active March 7, 2025 20:29
Show Gist options
  • Save leodido/1dfbbc19139c2c3d1a8fd31bff39671d to your computer and use it in GitHub Desktop.
Save leodido/1dfbbc19139c2c3d1a8fd31bff39671d to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -eu
# Define variables
INSTALL_DIR="/usr/local/bin"
NAME="update_cloudflare_ips"
SCRIPT_NAME="$NAME.sh"
SCRIPT_PATH="$INSTALL_DIR/$SCRIPT_NAME"
SERVICE_FILE="/etc/systemd/system/$NAME.service"
TIMER_FILE="/etc/systemd/system/$NAME.timer"
DATA_DIR="/var/lib/$NAME"
LOG_FILE="/var/log/$NAME.log"
# Create the data directory
echo "Creating data directory at $DATA_DIR..."
sudo mkdir -p "$DATA_DIR"
sudo chown root:root "$DATA_DIR"
sudo chmod 700 "$DATA_DIR"
# Create the log directory if it doesn't exist
echo "Creating log directory for $LOG_FILE..."
sudo mkdir -p "$(dirname "$LOG_FILE")"
sudo touch "$LOG_FILE"
sudo chown root:root "$LOG_FILE"
sudo chmod 600 "$LOG_FILE"
# Create update_cloudflare_ips.sh script with logging
cat << EOF | sudo tee "$SCRIPT_PATH" > /dev/null
#!/usr/bin/env bash
set -eu
LOG_FILE="$LOG_FILE"
DATA_DIR="$DATA_DIR"
# Ensure log directory exists
mkdir -p "\$(dirname "\$LOG_FILE")"
# Function to ensure file ends with a newline
ensure_trailing_newline() {
local file=\$1
# Check if the file ends with a newline
if [[ -n \$(tail -c 1 "\$file") ]]; then
# If the last character is not a newline, append one
echo "" >> "\$file"
fi
}
# Function to apply UFW rules
add_ufw_rules() {
local ip_list_file=\$1
local proto=\$2
while IFS= read -r ip; do
echo "Adding UFW rule: allow \$ip on port 443 proto \$proto" | tee -a "\$LOG_FILE"
ufw allow from "\$ip" to any port 443 proto "\$proto" || echo "Failed to add rule for \$ip" >> "\$LOG_FILE"
done < "\$ip_list_file"
}
# Function to remove UFW rules
remove_ufw_rules() {
local ip_list_file=\$1
local proto=\$2
while IFS= read -r ip; do
echo "Removing UFW rule: allow \$ip on port 443 proto \$proto" | tee -a "\$LOG_FILE"
ufw delete allow from "\$ip" to any port 443 proto "\$proto" || echo "Failed to remove rule for \$ip" >> "\$LOG_FILE"
done < "\$ip_list_file"
}
# Function to update UFW rules from the given URL
update_ufw_from_url() {
local url=\$1
local current_ips_file=\$2
local previous_ips_file=\$3
local proto=\$4
echo "Updating from URL: \$url" | tee -a "\$LOG_FILE"
curl -s "\$url" -o "\$current_ips_file"
# Ensure file ends with a newline
ensure_trailing_newline "\$current_ips_file"
# Add new IPs first
add_ufw_rules "\$current_ips_file" "\$proto"
# Remove old IPs that are not in the new list
if [[ -f \$previous_ips_file ]]; then
ensure_trailing_newline "\$previous_ips_file"
local ips_to_remove
ips_to_remove="\$(mktemp)"
comm -23 <(sort "\$previous_ips_file") <(sort "\$current_ips_file") > "\$ips_to_remove"
remove_ufw_rules "\$ips_to_remove" "\$proto"
rm "\$ips_to_remove"
fi
# Backup current IPs as previous IPs for the next run
mv "\$current_ips_file" "\$previous_ips_file"
}
# Update both IPv4 and IPv6
update_ufw_from_url "https://www.cloudflare.com/ips-v4" "\$DATA_DIR/current_ips_v4.txt" "\$DATA_DIR/previous_ips_v4.txt" "tcp"
update_ufw_from_url "https://www.cloudflare.com/ips-v6" "\$DATA_DIR/current_ips_v6.txt" "\$DATA_DIR/previous_ips_v6.txt" "tcp"
EOF
# Make the script executable
echo "Setting execute permissions for $SCRIPT_PATH..."
sudo chmod +x "$SCRIPT_PATH"
# Create systemd service file
echo "Writing systemd service file: $SERVICE_FILE..."
cat << EOF | sudo tee "$SERVICE_FILE" > /dev/null
[Unit]
Description=Update Cloudflare IPs in UFW
[Service]
ExecStart=$SCRIPT_PATH
[Install]
WantedBy=multi-user.target
EOF
# Create systemd timer file
echo "Writing systemd timer file: $TIMER_FILE..."
cat << EOF | sudo tee "$TIMER_FILE" > /dev/null
[Unit]
Description=Run $NAME.service daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
EOF
# Reload systemd daemon to recognize our new service and timer
echo "Reloading systemd daemon..."
sudo systemctl daemon-reload
# Enable the systemd timer
echo "Enabling systemd timer..."
sudo systemctl enable $NAME.timer
echo "Installation complete. You can now start the timer using: sudo systemctl start $NAME.timer"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment