SAW-Kinoite/post-install/configure-vpn.sh
2026-04-02 17:23:23 -05:00

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 "$@"