#!/bin/bash # WireGuard Configuration Validator # This script validates WireGuard configuration files for common issues set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' print_status() { echo -e "${GREEN}[PASS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARN]${NC} $1" } print_error() { echo -e "${RED}[FAIL]${NC} $1" } print_header() { echo -e "${BLUE}================================${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}================================${NC}" } # Function to validate IP address format validate_ip() { local ip=$1 if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then return 0 else return 1 fi } # Function to validate port number validate_port() { local port=$1 if [[ $port =~ ^[0-9]+$ ]] && [ $port -ge 1 ] && [ $port -le 65535 ]; then return 0 else return 1 fi } # Function to validate WireGuard key format validate_wg_key() { local key=$1 # WireGuard keys are base64 encoded and typically 44 characters long if [[ $key =~ ^[A-Za-z0-9+/]{43}=$ ]]; then return 0 else return 1 fi } # Function to validate endpoint format validate_endpoint() { local endpoint=$1 if [[ $endpoint =~ ^[a-zA-Z0-9.-]+:[0-9]+$ ]]; then return 0 else return 1 fi } # Function to validate a single configuration file validate_config_file() { local config_file=$1 local errors=0 local warnings=0 print_header "Validating: $config_file" if [ ! -f "$config_file" ]; then print_error "Configuration file not found: $config_file" return 1 fi # Check file permissions local perms=$(stat -c %a "$config_file") if [ "$perms" != "600" ]; then print_warning "File permissions should be 600, current: $perms" ((warnings++)) fi # Parse the configuration file local in_interface=0 local in_peer=0 local has_interface=0 local has_private_key=0 local has_address=0 local peer_count=0 while IFS= read -r line; do # Skip comments and empty lines if [[ $line =~ ^[[:space:]]*# ]] || [[ -z "${line// }" ]]; then continue fi # Check for Interface section if [[ $line =~ ^\[Interface\] ]]; then in_interface=1 in_peer=0 has_interface=1 continue fi # Check for Peer section if [[ $line =~ ^\[Peer\] ]]; then in_interface=0 in_peer=1 ((peer_count++)) continue fi # Parse Interface section if [ $in_interface -eq 1 ]; then if [[ $line =~ ^PrivateKey[[:space:]]*=[[:space:]]*(.+)$ ]]; then local private_key="${BASH_REMATCH[1]}" if validate_wg_key "$private_key"; then print_status "Valid private key found" has_private_key=1 else print_error "Invalid private key format" ((errors++)) fi elif [[ $line =~ ^Address[[:space:]]*=[[:space:]]*(.+)$ ]]; then local address="${BASH_REMATCH[1]}" if validate_ip "$address"; then print_status "Valid address: $address" has_address=1 else print_error "Invalid address format: $address" ((errors++)) fi elif [[ $line =~ ^ListenPort[[:space:]]*=[[:space:]]*(.+)$ ]]; then local port="${BASH_REMATCH[1]}" if validate_port "$port"; then print_status "Valid listen port: $port" else print_error "Invalid listen port: $port" ((errors++)) fi fi fi # Parse Peer section if [ $in_peer -eq 1 ]; then if [[ $line =~ ^PublicKey[[:space:]]*=[[:space:]]*(.+)$ ]]; then local public_key="${BASH_REMATCH[1]}" if validate_wg_key "$public_key"; then print_status "Valid peer public key" else print_error "Invalid peer public key format" ((errors++)) fi elif [[ $line =~ ^AllowedIPs[[:space:]]*=[[:space:]]*(.+)$ ]]; then local allowed_ips="${BASH_REMATCH[1]}" # Split multiple IPs if present IFS=',' read -ra IP_ARRAY <<< "$allowed_ips" for ip in "${IP_ARRAY[@]}"; do ip=$(echo "$ip" | xargs) # Trim whitespace if validate_ip "$ip"; then print_status "Valid allowed IP: $ip" else print_error "Invalid allowed IP format: $ip" ((errors++)) fi done elif [[ $line =~ ^Endpoint[[:space:]]*=[[:space:]]*(.+)$ ]]; then local endpoint="${BASH_REMATCH[1]}" if validate_endpoint "$endpoint"; then print_status "Valid endpoint: $endpoint" else print_error "Invalid endpoint format: $endpoint" ((errors++)) fi elif [[ $line =~ ^PersistentKeepalive[[:space:]]*=[[:space:]]*(.+)$ ]]; then local keepalive="${BASH_REMATCH[1]}" if validate_port "$keepalive"; then print_status "Valid persistent keepalive: $keepalive" else print_error "Invalid persistent keepalive: $keepalive" ((errors++)) fi fi fi done < "$config_file" # Check required fields if [ $has_interface -eq 0 ]; then print_error "Missing [Interface] section" ((errors++)) fi if [ $has_private_key -eq 0 ]; then print_error "Missing PrivateKey in [Interface] section" ((errors++)) fi if [ $has_address -eq 0 ]; then print_error "Missing Address in [Interface] section" ((errors++)) fi if [ $peer_count -eq 0 ]; then print_warning "No [Peer] sections found" ((warnings++)) fi # Summary echo "" if [ $errors -eq 0 ] && [ $warnings -eq 0 ]; then print_status "Configuration file is valid!" return 0 elif [ $errors -eq 0 ]; then print_warning "Configuration file has $warnings warning(s)" return 0 else print_error "Configuration file has $errors error(s) and $warnings warning(s)" return 1 fi } # Function to validate all config files in a directory validate_directory() { local dir=$1 local total_errors=0 local total_files=0 print_header "Validating all config files in: $dir" if [ ! -d "$dir" ]; then print_error "Directory not found: $dir" return 1 fi for config_file in "$dir"/*.conf; do if [ -f "$config_file" ]; then ((total_files++)) if ! validate_config_file "$config_file"; then ((total_errors++)) fi echo "" fi done if [ $total_files -eq 0 ]; then print_warning "No .conf files found in $dir" else print_header "Validation Summary" echo "Total files: $total_files" echo "Files with errors: $total_errors" echo "Files without errors: $((total_files - total_errors))" fi } # Show usage show_usage() { echo "Usage: $0 [OPTIONS] " echo "" echo "Options:" echo " -h, --help Show this help message" echo "" echo "Examples:" echo " $0 wireguard_configs/test.conf # Validate single file" echo " $0 wireguard_configs/ # Validate all files in directory" echo " $0 TESTING/ # Validate TESTING directory" } # Main logic if [ $# -eq 0 ] || [[ $1 =~ ^- ]]; then show_usage exit 0 fi target=$1 if [ -f "$target" ]; then validate_config_file "$target" elif [ -d "$target" ]; then validate_directory "$target" else print_error "Target not found: $target" exit 1 fi