414 lines
11 KiB
Bash
414 lines
11 KiB
Bash
#!/bin/bash
|
|
# WireGuard VPN Configuration Script for SAW
|
|
# Version: 1.0
|
|
# Date: 2026-04-02
|
|
#
|
|
# This script configures WireGuard VPN for the Secure Air-Gapped Workstation (SAW)
|
|
# It sets up the VPN tunnel, configures routing, and implements kill-switch functionality.
|
|
#
|
|
# Usage: sudo ./configure-vpn.sh
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# WireGuard is used because it provides:
|
|
# 1. Modern, fast encryption (ChaCha20-Poly1305, Curve25519)
|
|
# 2. Simple configuration
|
|
# 3. Kernel-native implementation (faster than userspace)
|
|
# 4. Strong security properties
|
|
# 5. Easy to audit and verify
|
|
#
|
|
# The kill-switch ensures:
|
|
# - No traffic leaks when VPN is down
|
|
# - No direct internet access
|
|
# - All traffic must go through encrypted VPN tunnel
|
|
#
|
|
# Reference: https://www.wireguard.com/install/
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
VPN_CONFIG_FILE="/etc/wireguard/wg0.conf"
|
|
VPN_INTERFACE="wg0"
|
|
VPN_RULE_FILE="/etc/iproute2/rt_tables.d/51820-wg0"
|
|
KILL_SWITCH_SCRIPT="/usr/local/bin/wg-kill-switch.sh"
|
|
LOG_FILE="/var/log/wg-vpn-config.log"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
# Print functions
|
|
log_info() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${GREEN}[$timestamp]${NC} ${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_warning() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${YELLOW}[$timestamp]${NC} ${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_error() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${RED}[$timestamp]${NC} ${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Check if running as root
|
|
check_root() {
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Check if WireGuard is installed
|
|
check_wireguard() {
|
|
log_info "Checking WireGuard installation..."
|
|
|
|
if ! command -v wg &> /dev/null; then
|
|
log_warning "WireGuard not installed"
|
|
log_info "Installing WireGuard..."
|
|
dnf install -y wireguard-dkms wireguard-tools
|
|
fi
|
|
|
|
# Check if kernel module is loaded
|
|
if ! lsmod | grep -q wireguard; then
|
|
log_info "Loading WireGuard kernel module..."
|
|
modprobe wireguard
|
|
fi
|
|
|
|
log_info "WireGuard is ready"
|
|
}
|
|
|
|
# Validate WireGuard configuration
|
|
validate_config() {
|
|
log_info "Validating WireGuard configuration..."
|
|
|
|
if [ ! -f "$VPN_CONFIG_FILE" ]; then
|
|
log_error "WireGuard configuration not found: $VPN_CONFIG_FILE"
|
|
log_info "Please create the configuration file first"
|
|
|
|
# Show example configuration
|
|
cat << 'EXAMPLE'
|
|
Example WireGuard Configuration:
|
|
|
|
[Interface]
|
|
# Your WireGuard private key (generated with: wg genkey)
|
|
PrivateKey = YOUR_PRIVATE_KEY_HERE
|
|
# VPN interface IP and subnet
|
|
Address = 10.0.0.2/24
|
|
# DNS servers (should be VPN gateway)
|
|
DNS = 10.0.0.1
|
|
|
|
[Peer]
|
|
# VPN server's public key
|
|
PublicKey = YOUR_SERVER_PUBLIC_KEY_HERE
|
|
# VPN server's endpoint (IP:port)
|
|
Endpoint = vpn.example.com:51820
|
|
# Which IPs to route through VPN (0.0.0.0/0 = all traffic)
|
|
AllowedIPs = 0.0.0.0/0, ::/0
|
|
EXAMPLE
|
|
|
|
exit 1
|
|
fi
|
|
|
|
# Check private key permissions
|
|
if [ -f "$VPN_CONFIG_FILE" ]; then
|
|
local perm=$(stat -c %a "$VPN_CONFIG_FILE")
|
|
if [ "$perm" != "600" ]; then
|
|
log_warning "WireGuard config permissions: $perm (should be 600)"
|
|
chmod 600 "$VPN_CONFIG_FILE"
|
|
log_info "Fixed permissions to 600"
|
|
fi
|
|
fi
|
|
|
|
# Validate configuration syntax
|
|
if ! wg-quick parse-config "$VPN_CONFIG_FILE" >/dev/null 2>&1; then
|
|
log_error "Invalid WireGuard configuration"
|
|
wg-quick parse-config "$VPN_CONFIG_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "WireGuard configuration validated"
|
|
}
|
|
|
|
# Create routing table
|
|
create_routing_table() {
|
|
log_info "Creating routing table for WireGuard..."
|
|
|
|
# Create routing table definition
|
|
cat > "$VPN_RULE_FILE" << 'ROUTES'
|
|
# WireGuard routing table
|
|
# Table 51820 is used for WireGuard traffic
|
|
51820 wg0
|
|
ROUTES
|
|
|
|
# Add default route through WireGuard interface
|
|
ip route add default dev "$VPN_INTERFACE" table 51820 2>/dev/null || true
|
|
|
|
log_info "Routing table created"
|
|
}
|
|
|
|
# Configure iptables rules
|
|
configure_firewall() {
|
|
log_info "Configuring firewall rules for WireGuard..."
|
|
|
|
# Check if iptables is available
|
|
if ! command -v iptables &> /dev/null; then
|
|
log_warning "iptables not available, skipping"
|
|
return
|
|
fi
|
|
|
|
# Allow WireGuard traffic
|
|
iptables -A OUTPUT -o "$VPN_INTERFACE" -j ACCEPT 2>/dev/null || true
|
|
iptables -A INPUT -i "$VPN_INTERFACE" -j ACCEPT 2>/dev/null || true
|
|
|
|
# Block all other outbound traffic
|
|
iptables -A OUTPUT ! -o lo ! -o "$VPN_INTERFACE" -j REJECT 2>/dev/null || true
|
|
|
|
# Allow loopback
|
|
iptables -A OUTPUT -o lo -j ACCEPT 2>/dev/null || true
|
|
|
|
log_info "Firewall rules configured"
|
|
}
|
|
|
|
# Create kill-switch script
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# The kill-switch is critical for security because it:
|
|
# 1. Prevents traffic leaks when VPN is down
|
|
# 2. Ensures all traffic goes through encrypted tunnel
|
|
# 3. Blocks DNS leaks (DNS only works through VPN)
|
|
# 4. Prevents data exfiltration when VPN is unavailable
|
|
# 5. Provides "fail closed" security model
|
|
#
|
|
# How it works:
|
|
# - Monitors VPN interface status
|
|
# - If VPN goes down, blocks all traffic (except loopback)
|
|
# - If VPN comes up, allows traffic through VPN
|
|
# - Can be configured to allow specific services (like update servers)
|
|
create_kill_switch() {
|
|
log_info "Creating kill-switch script..."
|
|
|
|
cat > "$KILL_SWITCH_SCRIPT" << 'KILLSCRIPT'
|
|
#!/bin/bash
|
|
# WireGuard Kill-Switch Script for SAW
|
|
# This script blocks all traffic when VPN is down
|
|
|
|
VPN_INTERFACE="wg0"
|
|
LOG_FILE="/var/log/wg-kill-switch.log"
|
|
|
|
# Function to enable kill-switch
|
|
enable_killswitch() {
|
|
log_info "Enabling kill-switch..."
|
|
|
|
# Flush existing rules
|
|
iptables -F OUTPUT 2>/dev/null || true
|
|
|
|
# Allow loopback
|
|
iptables -A OUTPUT -o lo -j ACCEPT
|
|
|
|
# Allow established connections (for cleanup)
|
|
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
|
|
# Block all other outbound traffic
|
|
iptables -A OUTPUT -j REJECT
|
|
|
|
log_info "Kill-switch enabled"
|
|
}
|
|
|
|
# Function to disable kill-switch
|
|
disable_killswitch() {
|
|
log_info "Disabling kill-switch..."
|
|
|
|
# Flush rules
|
|
iptables -F OUTPUT 2>/dev/null || true
|
|
|
|
# Allow all traffic (for VPN configuration)
|
|
iptables -A OUTPUT -j ACCEPT
|
|
|
|
log_info "Kill-switch disabled"
|
|
}
|
|
|
|
# Function to check VPN status
|
|
check_vpn() {
|
|
if ip link show "$VPN_INTERFACE" >/dev/null 2>&1; then
|
|
# VPN interface exists and is up
|
|
if ip addr show "$VPN_INTERFACE" | grep -q "state UP"; then
|
|
return 0 # VPN is up
|
|
fi
|
|
fi
|
|
return 1 # VPN is down
|
|
}
|
|
|
|
# Function to log
|
|
log_info() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
|
|
}
|
|
|
|
# Main logic
|
|
case "$1" in
|
|
enable)
|
|
enable_killswitch
|
|
;;
|
|
disable)
|
|
disable_killswitch
|
|
;;
|
|
status)
|
|
if check_vpn; then
|
|
echo "VPN is UP"
|
|
exit 0
|
|
else
|
|
echo "VPN is DOWN"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
# Check VPN and act accordingly
|
|
if check_vpn; then
|
|
disable_killswitch
|
|
else
|
|
enable_killswitch
|
|
fi
|
|
;;
|
|
esac
|
|
KILLSCRIPT
|
|
|
|
chmod +x "$KILL_SWITCH_SCRIPT"
|
|
log_info "Kill-switch script created"
|
|
}
|
|
|
|
# Configure systemd service for kill-switch
|
|
configure_killswitch_service() {
|
|
log_info "Configuring kill-switch systemd service..."
|
|
|
|
cat > /etc/systemd/system/wg-killswitch.service << 'KILLSERVICE'
|
|
[Unit]
|
|
Description=WireGuard Kill-Switch Service
|
|
After=network.target wg-quick@wg0.service
|
|
Wants=wg-quick@wg0.service
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/local/bin/wg-kill-switch.sh
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
KILLSERVICE
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable wg-killswitch
|
|
log_info "Kill-switch service configured"
|
|
}
|
|
|
|
# Configure DNS to use VPN gateway
|
|
configure_dns() {
|
|
log_info "Configuring DNS to use VPN gateway..."
|
|
|
|
# Read VPN config to get DNS settings
|
|
if [ -f "$VPN_CONFIG_FILE" ]; then
|
|
local dns_servers=$(grep "^DNS =" "$VPN_CONFIG_FILE" | cut -d'=' -f2 | tr -d ' ')
|
|
|
|
if [ -n "$dns_servers" ]; then
|
|
# Configure resolv.conf
|
|
echo "# WireGuard DNS configuration" > /etc/resolv.conf
|
|
echo "# Generated by configure-vpn.sh" >> /etc/resolv.conf
|
|
|
|
# Add DNS servers
|
|
for server in $dns_servers; do
|
|
echo "nameserver $server" >> /etc/resolv.conf
|
|
done
|
|
|
|
log_info "DNS configured to use VPN gateway"
|
|
else
|
|
log_warning "No DNS servers specified in WireGuard config"
|
|
fi
|
|
fi
|
|
|
|
# Configure systemd-resolved to use VPN DNS
|
|
if command -v systemctl &> /dev/null; then
|
|
systemctl restart systemd-resolved 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
# Test VPN connectivity
|
|
test_vpn() {
|
|
log_info "Testing VPN connectivity..."
|
|
|
|
# Check if VPN interface is up
|
|
if ip link show "$VPN_INTERFACE" | grep -q "state UP"; then
|
|
log_info "VPN interface is UP"
|
|
|
|
# Check connectivity
|
|
if ping -c 3 -I "$VPN_INTERFACE" 8.8.8.8 >/dev/null 2>&1; then
|
|
log_info "VPN connectivity test passed"
|
|
else
|
|
log_warning "VPN connectivity test failed (may need to wait for routing)"
|
|
fi
|
|
else
|
|
log_warning "VPN interface is not UP"
|
|
log_info "Run 'wg-quick up wg0' to start VPN"
|
|
fi
|
|
}
|
|
|
|
# Display configuration
|
|
show_config() {
|
|
echo ""
|
|
echo "=== WireGuard Configuration ==="
|
|
echo ""
|
|
echo "Configuration file: $VPN_CONFIG_FILE"
|
|
echo "VPN interface: $VPN_INTERFACE"
|
|
echo ""
|
|
|
|
if [ -f "$VPN_CONFIG_FILE" ]; then
|
|
echo "Current configuration:"
|
|
echo "----------------------------------------"
|
|
grep -v "^#" "$VPN_CONFIG_FILE" | grep -v "^$" | sed 's/^/ /'
|
|
echo "----------------------------------------"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Kill-switch script: $KILL_SWITCH_SCRIPT"
|
|
echo ""
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " WireGuard VPN Configuration"
|
|
echo " Fedora Kinoite SAW"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
check_root
|
|
check_wireguard
|
|
validate_config
|
|
create_routing_table
|
|
configure_firewall
|
|
create_kill_switch
|
|
configure_killswitch_service
|
|
configure_dns
|
|
show_config
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " VPN Configuration Complete!"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo "1. Edit WireGuard config: sudo nano $VPN_CONFIG_FILE"
|
|
echo "2. Start VPN: sudo wg-quick up wg0"
|
|
echo "3. Test VPN: ping -I wg0 8.8.8.8"
|
|
echo "4. Check status: wg show"
|
|
echo ""
|
|
echo "To start VPN automatically on boot:"
|
|
echo " sudo systemctl enable wg-quick@wg0"
|
|
echo ""
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |