295 lines
8.3 KiB
Bash
Executable File
295 lines
8.3 KiB
Bash
Executable File
#!/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] <config_file_or_directory>"
|
|
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 |