Last active
August 11, 2025 15:39
-
-
Save Sly777/08fa7e454e5643cf78dc75f10a0607eb to your computer and use it in GitHub Desktop.
Adding UFW rules based on domains (Cloudflare, github and debian)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # UFW Domain Rules Updater - Enhanced Version | |
| # Updates UFW rules for domains with current IPs and timestamps | |
| # ============================================================================= | |
| # CONFIGURATION - UPDATE THIS SECTION TO ADD/REMOVE DOMAINS | |
| # ============================================================================= | |
| # Domain patterns to match in UFW rule comments (for cleanup) | |
| DOMAIN_PATTERNS=( | |
| "GitHub" # Matches: GitHub HTTPS, GitHub API, GitHub Raw Content, etc. | |
| "Debian" # Matches: Debian Repo, Debian Security, Debian FTP, etc. | |
| "Cloudflare" # Matches: Cloudflare Tunnel, Cloudflare API, etc. | |
| "Pushover" # Matches: Pushover API | |
| "Crowdsec" | |
| ) | |
| # Build grep pattern from array (don't modify this) | |
| GREP_PATTERN=$(IFS='|'; echo "(${DOMAIN_PATTERNS[*]})") | |
| # ============================================================================= | |
| # SCRIPT VARIABLES | |
| # ============================================================================= | |
| # Colors | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' # No Color | |
| # Global variables | |
| VERBOSE=false | |
| FORCE=false | |
| SCRIPT_NAME=$(basename "$0") | |
| LOG_FILE="/var/log/ufw-domain-updater.log" | |
| CURRENT_DATE=$(date '+%Y-%m-%d %H:%M') | |
| REMOVE_SCRIPT="/usr/local/bin/remove-domain-rules.sh" | |
| print_color() { | |
| echo -e "${1}${2}${NC}" | |
| } | |
| # Logging function | |
| log_message() { | |
| echo "[$CURRENT_DATE] $1" | tee -a "$LOG_FILE" | |
| } | |
| # Show help information | |
| show_help() { | |
| echo "UFW Domain Rules Updater" | |
| echo "Updates UFW rules for domains with current IPs and timestamps" | |
| echo "" | |
| echo "Current domain patterns:" | |
| for pattern in "${DOMAIN_PATTERNS[@]}"; do | |
| echo " • $pattern" | |
| done | |
| echo "" | |
| echo "Usage: sudo $SCRIPT_NAME [OPTIONS]" | |
| echo "" | |
| echo "OPTIONS:" | |
| echo " -h, --help Show this help message" | |
| echo " -v, --verbose Show detailed output for all operations" | |
| echo " -f, --force Skip confirmation prompts" | |
| echo " -d, --debug Show what domains/IPs would be added (dry run)" | |
| echo "" | |
| echo "Examples:" | |
| echo " sudo $SCRIPT_NAME # Interactive mode (default)" | |
| echo " sudo $SCRIPT_NAME --verbose # Show all operation details" | |
| echo " sudo $SCRIPT_NAME --force # No confirmation prompts" | |
| echo " sudo $SCRIPT_NAME --force --verbose # Force with detailed output" | |
| echo " sudo $SCRIPT_NAME --debug # Preview what would be added" | |
| echo "" | |
| echo "Notes:" | |
| echo " • Automatically removes old domain rules before adding new ones" | |
| echo " • Creates UFW rules with timestamps in comments" | |
| echo " • Supports both IPv4 and IPv6 addresses" | |
| echo " • Requires root privileges (use sudo)" | |
| echo "" | |
| echo "To modify domains: Edit DOMAIN_PATTERNS array at top of script" | |
| echo "To manually remove domain rules: remove-domain-rules.sh" | |
| } | |
| # Parse command line arguments | |
| parse_arguments() { | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| -h|--help) | |
| show_help | |
| exit 0 | |
| ;; | |
| -v|--verbose) | |
| VERBOSE=true | |
| shift | |
| ;; | |
| -f|--force) | |
| FORCE=true | |
| shift | |
| ;; | |
| -d|--debug) | |
| debug_mode | |
| exit 0 | |
| ;; | |
| *) | |
| print_color "$RED" "Unknown option: $1" | |
| echo "Use --help for usage information" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| } | |
| # Debug mode - show what would be added | |
| debug_mode() { | |
| print_color "$BLUE" "=== DEBUG MODE - Preview Only ===" | |
| print_color "$YELLOW" "This would resolve and add UFW rules for these domains:" | |
| echo "" | |
| print_color "$BLUE" "GitHub domains:" | |
| echo " • github.com:443 (HTTPS)" | |
| echo " • api.github.com:443 (API)" | |
| echo " • raw.githubusercontent.com:443 (Raw Content)" | |
| echo " • objects.githubusercontent.com:443 (Objects)" | |
| echo " • ghcr.io:443 (Container Registry)" | |
| print_color "$BLUE" "\nDebian domains:" | |
| echo " • deb.debian.org:443/80 (Repository)" | |
| echo " • security.debian.org:443/80 (Security)" | |
| echo " • ftp.debian.org:443/80 (FTP)" | |
| print_color "$BLUE" "\nCloudflare domains:" | |
| echo " • region1-4.v2.argotunnel.com:443/7844 (Tunnel Regions)" | |
| echo " • us-region1-4.v2.argotunnel.com:443/7844 (Tunnel Regions)" | |
| echo " • api.cloudflare.com:443 (API)" | |
| echo " • dash.cloudflare.com:443 (Dashboard)" | |
| echo " • update.argotunnel.com" | |
| echo " • cftunnel.com" | |
| echo " • h2.cftunnel.com" | |
| echo " • quic.cftunnel.com" | |
| echo " • _v2-origintunneld._tcp.argotunnel.com" | |
| echo " • cfd-v4.argotunnel.com:7844 (Edge Servers)" | |
| print_color "$BLUE" "\nPushover domains:" | |
| echo " • api.pushover.net:443" | |
| print_color "$YELLOW" "\nOld domain rules matching patterns: ${DOMAIN_PATTERNS[*]}" | |
| local existing_rules=$(ufw status numbered | grep -E "$GREP_PATTERN" || echo "None found") | |
| if [[ "$existing_rules" != "None found" ]]; then | |
| echo "$existing_rules" | |
| else | |
| print_color "$GREEN" "No existing domain rules found" | |
| fi | |
| print_color "$BLUE" "\nTo actually update these rules, run without --debug" | |
| } | |
| # Check if running as root | |
| check_root() { | |
| if [[ $EUID -ne 0 ]]; then | |
| print_color "$RED" "This script must be run as root" | |
| print_color "$YELLOW" "Usage: sudo $SCRIPT_NAME [OPTIONS]" | |
| print_color "$YELLOW" "Use --help for more information" | |
| exit 1 | |
| fi | |
| } | |
| # Check dependencies | |
| check_dependencies() { | |
| local missing_deps=() | |
| if ! command -v dig >/dev/null 2>&1; then | |
| missing_deps+=("dnsutils") | |
| fi | |
| if ! command -v ufw >/dev/null 2>&1; then | |
| missing_deps+=("ufw") | |
| fi | |
| if [[ ${#missing_deps[@]} -gt 0 ]]; then | |
| print_color "$RED" "Missing dependencies: ${missing_deps[*]}" | |
| print_color "$YELLOW" "Install with: apt install ${missing_deps[*]}" | |
| exit 1 | |
| fi | |
| if [[ ! -f "$REMOVE_SCRIPT" ]]; then | |
| print_color "$RED" "Required script not found: $REMOVE_SCRIPT" | |
| print_color "$YELLOW" "Please ensure remove-domain-rules.sh is installed" | |
| exit 1 | |
| fi | |
| } | |
| # Function to resolve domain and add UFW rules | |
| add_domain_rule() { | |
| local domain=$1 | |
| local port=$2 | |
| local protocol=${3:-tcp} | |
| local description=$4 | |
| local added_count=0 | |
| if [[ "$VERBOSE" == true ]]; then | |
| print_color "$BLUE" "Processing domain: $domain" | |
| fi | |
| log_message "Processing domain: $domain" | |
| # Resolve domain to IPs (both A and AAAA records) | |
| local ipv4_ips=$(dig +short $domain A 2>/dev/null | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -10) | |
| local ipv6_ips=$(dig +short $domain AAAA 2>/dev/null | grep -E '^[0-9a-fA-F:]+$' | head -5) | |
| if [[ -z "$ipv4_ips" && -z "$ipv6_ips" ]]; then | |
| print_color "$RED" "✗ No IPs resolved for $domain" | |
| log_message "WARNING: No IPs resolved for $domain" | |
| return 0 | |
| fi | |
| # Add IPv4 rules | |
| if [[ -n "$ipv4_ips" ]]; then | |
| for ip in $ipv4_ips; do | |
| local comment="$description - $domain ($CURRENT_DATE)" | |
| if [[ "$VERBOSE" == true ]]; then | |
| print_color "$YELLOW" " Adding IPv4: $ip:$port" | |
| fi | |
| if ufw allow out to "$ip" port "$port" proto "$protocol" comment "$comment" >/dev/null 2>&1; then | |
| if [[ "$VERBOSE" == true ]]; then | |
| print_color "$GREEN" " ✓ Added successfully" | |
| fi | |
| log_message "Added IPv4 rule: $ip:$port - $comment" | |
| ((added_count++)) | |
| return 0 | |
| else | |
| print_color "$RED" " ✗ Failed to add rule for $ip:$port" | |
| log_message "Failed to add rule for $ip:$port" | |
| fi | |
| done | |
| fi | |
| # Add IPv6 rules | |
| if [[ -n "$ipv6_ips" ]]; then | |
| for ip in $ipv6_ips; do | |
| local comment="$description - $domain IPv6 ($CURRENT_DATE)" | |
| if [[ "$VERBOSE" == true ]]; then | |
| print_color "$YELLOW" " Adding IPv6: $ip:$port" | |
| fi | |
| if ufw allow out to "$ip" port "$port" proto "$protocol" comment "$comment" >/dev/null 2>&1; then | |
| if [[ "$VERBOSE" == true ]]; then | |
| print_color "$GREEN" " ✓ Added successfully" | |
| fi | |
| log_message "Added IPv6 rule: $ip:$port - $comment" | |
| ((added_count++)) | |
| return 0 | |
| else | |
| print_color "$RED" " ✗ Failed to add IPv6 rule for $ip:$port" | |
| log_message "Failed to add IPv6 rule for $ip:$port" | |
| fi | |
| done | |
| fi | |
| if [[ "$VERBOSE" != true && $added_count -gt 0 ]]; then | |
| print_color "$GREEN" "✓ $domain ($added_count rules added)" | |
| fi | |
| return $added_count | |
| } | |
| # Remove old domain rules | |
| cleanup_old_rules() { | |
| print_color "$BLUE" "=== Cleaning up old domain rules ===" | |
| if [[ "$VERBOSE" == true ]]; then | |
| print_color "$YELLOW" "Using removal script: $REMOVE_SCRIPT" | |
| fi | |
| log_message "Calling $REMOVE_SCRIPT --force to remove old rules..." | |
| # Use appropriate flags for the removal script | |
| local remove_flags="--force" | |
| if [[ "$VERBOSE" == true ]]; then | |
| remove_flags="--force --verbose" | |
| fi | |
| if "$REMOVE_SCRIPT" $remove_flags; then | |
| print_color "$GREEN" "✓ Successfully removed old domain rules" | |
| log_message "Successfully removed old domain rules" | |
| else | |
| print_color "$YELLOW" "⚠ Domain removal script reported issues (continuing anyway)" | |
| log_message "Warning: Domain removal script reported issues (continuing anyway)" | |
| fi | |
| # Wait a moment for cleanup to complete | |
| sleep 2 | |
| } | |
| # Get confirmation from user | |
| get_confirmation() { | |
| if [[ "$FORCE" == true ]]; then | |
| print_color "$YELLOW" "Force mode: Skipping confirmation prompts" | |
| return 0 | |
| fi | |
| print_color "$YELLOW" "\nThis will:" | |
| print_color "$YELLOW" " • Remove all existing domain-based UFW rules" | |
| print_color "$YELLOW" " • Resolve current IPs for configured domains" | |
| print_color "$YELLOW" " • Add new UFW rules with current timestamps" | |
| echo "" | |
| read -p "Continue with domain rules update? (yes/no): " confirm | |
| if [[ "$confirm" != "yes" ]]; then | |
| print_color "$GREEN" "Operation cancelled." | |
| exit 0 | |
| fi | |
| return 0 | |
| } | |
| # Main update process | |
| perform_update() { | |
| ufw disable | |
| sleep 2 | |
| local total_rules=0 | |
| print_color "$BLUE" "\n=== Adding GitHub rules ===" | |
| add_domain_rule "github.com" 443 tcp "GitHub HTTPS" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "api.github.com" 443 tcp "GitHub API" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "raw.githubusercontent.com" 443 tcp "GitHub Raw Content" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "objects.githubusercontent.com" 443 tcp "GitHub Objects" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "ghcr.io" 443 tcp "GitHub Container Registry" | |
| total_rules=$((total_rules + $?)) | |
| print_color "$BLUE" "\n=== Adding Pushover rules ===" | |
| add_domain_rule "api.pushover.net" 443 tcp "Pushover API HTTPS" | |
| total_rules=$((total_rules + $?)) | |
| print_color "$BLUE" "\n=== Adding Debian repository rules ===" | |
| add_domain_rule "deb.debian.org" 443 tcp "Debian Repo HTTPS" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "deb.debian.org" 80 tcp "Debian Repo HTTP" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "security.debian.org" 443 tcp "Debian Security HTTPS" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "security.debian.org" 80 tcp "Debian Security HTTP" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "ftp.debian.org" 443 tcp "Debian FTP HTTPS" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "ftp.debian.org" 80 tcp "Debian FTP HTTP" | |
| total_rules=$((total_rules + $?)) | |
| print_color "$BLUE" "\n=== Adding Cloudflare Tunnel rules ===" | |
| add_domain_rule "api.cloudflare.com" 443 tcp "Cloudflare API" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "update.argotunnel.com" 443 tcp "Cloudflare Update" | |
| total_rules=$((total_rules + $?)) | |
| # Add Cloudflare edge servers (main ones) | |
| add_domain_rule "_v2-origintunneld._tcp.argotunnel.com" 7844 tcp "Cloudflare Tunnel Origin" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "cftunnel.com" 7844 tcp "Cloudflare Tunnel TCP" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "cftunnel.com" 7844 udp "Cloudflare Tunnel UDP" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "h2.cftunnel.com" 7844 tcp "Cloudflare Tunnel H2" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "quic.cftunnel.com" 7844 udp "Cloudflare Tunnel QUIC" | |
| total_rules=$((total_rules + $?)) | |
| # Cloudflare global tunnel endpoints | |
| for region in region1 region2 region3 region4; do | |
| add_domain_rule "$region.v2.argotunnel.com" 443 tcp "Cloudflare Tunnel $region" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "$region.v2.argotunnel.com" 7844 tcp "Cloudflare Tunnel $region" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "$region.v2.argotunnel.com" 7844 udp "Cloudflare Tunnel $region UDP" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "us-$region.v2.argotunnel.com" 7844 tcp "Cloudflare Tunnel US $region" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "us-$region.v2.argotunnel.com" 7844 udp "Cloudflare Tunnel US $region UDP" | |
| total_rules=$((total_rules + $?)) | |
| done | |
| print_color "$BLUE" "\n=== Adding Crowdsec Tunnel rules ===" | |
| add_domain_rule "api.crowdsec.net" 443 tcp "Crowdsec Hub API" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "cti-api.crowdsec.net" 443 tcp "Crowdsec Community API" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "hub.crowdsec.net" 443 tcp "Crowdsec Hub" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "hub-data.crowdsec.net" 443 tcp "Crowdsec Hub data" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "cdn-hub.crowdsec.net" 443 tcp "Crowdsec Hub CDN" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "version.crowdsec.net" 443 tcp "Crowdsec Version API" | |
| total_rules=$((total_rules + $?)) | |
| add_domain_rule "crowdsec.net" 443 tcp "Crowdsec Services" | |
| total_rules=$((total_rules + $?)) | |
| print_color "$GREEN" "\n=== Update Summary ===" | |
| print_color "$GREEN" "Total UFW rules added: $total_rules" | |
| log_message "Domain rules update completed. Total rules added: $total_rules" | |
| sleep 2 | |
| ufw enable | |
| return $total_rules | |
| } | |
| # Show final status | |
| show_final_status() { | |
| local rules_added=$1 | |
| if [[ $rules_added -gt 0 ]] && [[ "$VERBOSE" == true ]]; then | |
| print_color "$BLUE" "\n=== Current UFW Status (last 20 rules) ===" | |
| ufw status numbered | tail -20 | |
| fi | |
| print_color "$BLUE" "\n=== Operation Complete ===" | |
| print_color "$GREEN" "✓ Domain rules updated successfully!" | |
| print_color "$BLUE" "Last updated: $CURRENT_DATE" | |
| if [[ "$VERBOSE" != true ]]; then | |
| print_color "$YELLOW" "Tip: Use --verbose flag to see detailed operation output" | |
| fi | |
| print_color "$BLUE" "\nUseful commands:" | |
| print_color "$BLUE" " View log: tail -f $LOG_FILE" | |
| print_color "$BLUE" " Remove rules: $REMOVE_SCRIPT" | |
| print_color "$BLUE" " View UFW status: ufw status numbered" | |
| } | |
| # Main function | |
| main() { | |
| # Parse arguments first | |
| parse_arguments "$@" | |
| # Start logging | |
| log_message "=== Starting UFW Domain Rules Updater ===" | |
| # Check permissions and dependencies | |
| check_root | |
| check_dependencies | |
| print_color "$BLUE" "=== UFW Domain Rules Updater ===" | |
| print_color "$YELLOW" "Configured domains: ${DOMAIN_PATTERNS[*]}" | |
| # Get confirmation (unless force mode) | |
| get_confirmation | |
| # Remove old domain rules | |
| cleanup_old_rules | |
| # Perform the update | |
| perform_update | |
| local rules_added=$? | |
| # Show final status | |
| show_final_status $rules_added | |
| log_message "=== UFW Domain Rules Updater finished ===" | |
| } | |
| # Run main function with all arguments | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment