1201 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
		
		
			
		
	
	
			1201 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
|  | #!/bin/bash | ||
|  | 
 | ||
|  | ############################################################# | ||
|  | #                                                           # | ||
|  | #        Mullvad VPN Gateway Installer for LXC             # | ||
|  | #        Secure VPN Gateway with Permanent Killswitch      # | ||
|  | #                                                           # | ||
|  | #        Usage: curl -sSL https://your-domain/install.sh | bash  # | ||
|  | #                                                           # | ||
|  | ############################################################# | ||
|  | 
 | ||
|  | set -e  # Exit on error | ||
|  | 
 | ||
|  | # Colors for output | ||
|  | RED='\033[0;31m' | ||
|  | GREEN='\033[0;32m' | ||
|  | YELLOW='\033[1;33m' | ||
|  | BLUE='\033[0;34m' | ||
|  | PURPLE='\033[0;35m' | ||
|  | CYAN='\033[0;36m' | ||
|  | BOLD='\033[1m' | ||
|  | NC='\033[0m' # No Color | ||
|  | 
 | ||
|  | # Configuration variables | ||
|  | INSTALL_DIR="/opt/vpn-gateway" | ||
|  | LOG_FILE="/var/log/vpn-gateway-install.log" | ||
|  | GITHUB_REPO="https://raw.githubusercontent.com/yourusername/vpn-gateway/main" | ||
|  | VERSION="1.0.0" | ||
|  | 
 | ||
|  | # System variables (will be auto-detected) | ||
|  | LAN_INTERFACE="" | ||
|  | LAN_IP="" | ||
|  | LAN_NETWORK="" | ||
|  | CONTAINER_TYPE="" | ||
|  | MULLVAD_ACCOUNT="" | ||
|  | 
 | ||
|  | # VPN Provider variables | ||
|  | VPN_PROVIDER="" | ||
|  | WG_CONFIG_PATH="" | ||
|  | WG_IMPORTED_CONFIG="" | ||
|  | WG_CUSTOM_LOCATION="" | ||
|  | WG_CUSTOM_NAME="" | ||
|  | WG_ENDPOINT="" | ||
|  | WG_SERVER_PUBKEY="" | ||
|  | WG_CLIENT_PRIVKEY="" | ||
|  | WG_CLIENT_IP="" | ||
|  | WG_DNS="" | ||
|  | WG_ALLOWED_IPS="" | ||
|  | BACKUP_SERVERS=() | ||
|  | 
 | ||
|  | # ASCII Art Banner | ||
|  | show_banner() { | ||
|  |     clear | ||
|  |     echo -e "${CYAN}" | ||
|  |     cat << "EOF" | ||
|  |  __  __       _ _                 _  __     ______  _   _  | ||
|  | |  \/  |     | | |               | | \ \   / /  _ \| \ | | | ||
|  | | \  / |_   _| | |_   ____ _  ___| |  \ \ / /| |_) |  \| | | ||
|  | | |\/| | | | | | \ \ / / _` |/ _ | |   \ V / |  __/| . ` | | ||
|  | | |  | | |_| | | |\ V | (_| |  __| |    | |  | |   | |\  | | ||
|  | |_|  |_|\__,_|_|_| \_/ \__,_|\___|_|    |_|  |_|   |_| \_| | ||
|  |                                                             | ||
|  |             Secure Gateway with Permanent Killswitch | ||
|  | EOF | ||
|  |     echo -e "${NC}" | ||
|  |     echo -e "${BOLD}Version:${NC} ${VERSION}" | ||
|  |     echo -e "${BOLD}GitHub:${NC} github.com/yourusername/vpn-gateway" | ||
|  |     echo "" | ||
|  | } | ||
|  | 
 | ||
|  | # Logging functions | ||
|  | log() { | ||
|  |     echo -e "${GREEN}[+]${NC} $1" | ||
|  |     echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" | ||
|  | } | ||
|  | 
 | ||
|  | error() { | ||
|  |     echo -e "${RED}[!]${NC} $1" | ||
|  |     echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >> "$LOG_FILE" | ||
|  | } | ||
|  | 
 | ||
|  | warning() { | ||
|  |     echo -e "${YELLOW}[*]${NC} $1" | ||
|  |     echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARNING: $1" >> "$LOG_FILE" | ||
|  | } | ||
|  | 
 | ||
|  | info() { | ||
|  |     echo -e "${BLUE}[i]${NC} $1" | ||
|  |     echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" >> "$LOG_FILE" | ||
|  | } | ||
|  | 
 | ||
|  | # Progress spinner | ||
|  | spinner() { | ||
|  |     local pid=$1 | ||
|  |     local delay=0.1 | ||
|  |     local spinstr='|/-\' | ||
|  |     while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do | ||
|  |         local temp=${spinstr#?} | ||
|  |         printf " [%c]  " "$spinstr" | ||
|  |         local spinstr=$temp${spinstr%"$temp"} | ||
|  |         sleep $delay | ||
|  |         printf "\b\b\b\b\b\b" | ||
|  |     done | ||
|  |     printf "    \b\b\b\b" | ||
|  | } | ||
|  | 
 | ||
|  | # Check if running as root | ||
|  | check_root() { | ||
|  |     if [[ $EUID -ne 0 ]]; then | ||
|  |         error "This script must be run as root" | ||
|  |         exit 1 | ||
|  |     fi | ||
|  | } | ||
|  | 
 | ||
|  | # Detect container type | ||
|  | detect_container() { | ||
|  |     info "Detecting container type..." | ||
|  |      | ||
|  |     if [ -f /run/systemd/container ]; then | ||
|  |         CONTAINER_TYPE=$(cat /run/systemd/container) | ||
|  |     elif [ -f /proc/1/environ ]; then | ||
|  |         if grep -q lxc /proc/1/environ; then | ||
|  |             CONTAINER_TYPE="lxc" | ||
|  |         elif grep -q docker /proc/1/environ; then | ||
|  |             CONTAINER_TYPE="docker" | ||
|  |         fi | ||
|  |     fi | ||
|  |      | ||
|  |     if [ -n "$CONTAINER_TYPE" ]; then | ||
|  |         log "Container type: $CONTAINER_TYPE" | ||
|  |     else | ||
|  |         warning "Could not detect container type, assuming LXC" | ||
|  |         CONTAINER_TYPE="lxc" | ||
|  |     fi | ||
|  | } | ||
|  | 
 | ||
|  | # Auto-detect network configuration | ||
|  | detect_network() { | ||
|  |     info "Auto-detecting network configuration..." | ||
|  |      | ||
|  |     # Find primary network interface (excluding lo and wg*) | ||
|  |     LAN_INTERFACE=$(ip route | grep default | awk '{print $5}' | head -n1) | ||
|  |      | ||
|  |     if [ -z "$LAN_INTERFACE" ]; then | ||
|  |         # Fallback: find first UP interface | ||
|  |         LAN_INTERFACE=$(ip link show | grep "state UP" | grep -v "lo:" | awk -F': ' '{print $2}' | head -n1) | ||
|  |     fi | ||
|  |      | ||
|  |     if [ -z "$LAN_INTERFACE" ]; then | ||
|  |         error "Could not detect network interface" | ||
|  |         read -p "Please enter your network interface (e.g., eth0): " LAN_INTERFACE | ||
|  |     fi | ||
|  |      | ||
|  |     # Get IP address | ||
|  |     LAN_IP=$(ip -4 addr show "$LAN_INTERFACE" | grep inet | awk '{print $2}' | cut -d/ -f1) | ||
|  |      | ||
|  |     # Get network subnet | ||
|  |     LAN_NETWORK=$(ip route | grep "$LAN_INTERFACE" | grep -v default | awk '{print $1}' | head -n1) | ||
|  |      | ||
|  |     if [ -z "$LAN_NETWORK" ]; then | ||
|  |         # Calculate from IP | ||
|  |         LAN_NETWORK=$(echo "$LAN_IP" | cut -d. -f1-3).0/24 | ||
|  |     fi | ||
|  |      | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Detected Network Configuration:${NC}" | ||
|  |     echo -e "  Interface: ${BOLD}$LAN_INTERFACE${NC}" | ||
|  |     echo -e "  IP Address: ${BOLD}$LAN_IP${NC}" | ||
|  |     echo -e "  Network: ${BOLD}$LAN_NETWORK${NC}" | ||
|  |     echo "" | ||
|  |      | ||
|  |     read -p "Is this correct? (Y/n): " -n 1 -r | ||
|  |     echo "" | ||
|  |     if [[ ! $REPLY =~ ^[Yy]$ ]] && [ -n "$REPLY" ]; then | ||
|  |         read -p "Enter network interface: " LAN_INTERFACE | ||
|  |         read -p "Enter network subnet (e.g., 192.168.1.0/24): " LAN_NETWORK | ||
|  |         LAN_IP=$(ip -4 addr show "$LAN_INTERFACE" | grep inet | awk '{print $2}' | cut -d/ -f1) | ||
|  |     fi | ||
|  | } | ||
|  | 
 | ||
|  | # Choose VPN provider | ||
|  | choose_vpn_provider() { | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}VPN Provider Selection${NC}" | ||
|  |     echo "" | ||
|  |     echo -e "  ${BOLD}1)${NC} Mullvad VPN (Commercial Provider)" | ||
|  |     echo -e "  ${BOLD}2)${NC} Custom WireGuard (Own Server/VPS)" | ||
|  |     echo -e "  ${BOLD}3)${NC} Import existing WireGuard config" | ||
|  |     echo "" | ||
|  |      | ||
|  |     while true; do | ||
|  |         read -p "Select provider [1-3]: " VPN_PROVIDER_CHOICE | ||
|  |         case $VPN_PROVIDER_CHOICE in | ||
|  |             1) | ||
|  |                 VPN_PROVIDER="mullvad" | ||
|  |                 get_mullvad_account | ||
|  |                 break | ||
|  |                 ;; | ||
|  |             2) | ||
|  |                 VPN_PROVIDER="custom" | ||
|  |                 get_custom_wireguard_details | ||
|  |                 break | ||
|  |                 ;; | ||
|  |             3) | ||
|  |                 VPN_PROVIDER="import" | ||
|  |                 import_wireguard_config | ||
|  |                 break | ||
|  |                 ;; | ||
|  |             *) | ||
|  |                 error "Invalid choice. Please select 1, 2, or 3." | ||
|  |                 ;; | ||
|  |         esac | ||
|  |     done | ||
|  |      | ||
|  |     log "VPN provider selected: $VPN_PROVIDER" | ||
|  | } | ||
|  | 
 | ||
|  | # Get Mullvad account | ||
|  | get_mullvad_account() { | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Mullvad VPN Account Setup${NC}" | ||
|  |     echo -e "You need a Mullvad account number to continue." | ||
|  |     echo -e "Get one at: ${BOLD}https://mullvad.net${NC}" | ||
|  |     echo "" | ||
|  |      | ||
|  |     while [ -z "$MULLVAD_ACCOUNT" ]; do | ||
|  |         read -p "Enter your Mullvad account number: " MULLVAD_ACCOUNT | ||
|  |          | ||
|  |         # Validate format (16 digits) | ||
|  |         if [[ ! "$MULLVAD_ACCOUNT" =~ ^[0-9]{16}$ ]]; then | ||
|  |             error "Invalid account format. Should be 16 digits." | ||
|  |             MULLVAD_ACCOUNT="" | ||
|  |         fi | ||
|  |     done | ||
|  |      | ||
|  |     log "Mullvad account configured" | ||
|  | } | ||
|  | 
 | ||
|  | # Get custom WireGuard details | ||
|  | get_custom_wireguard_details() { | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Custom WireGuard Configuration${NC}" | ||
|  |     echo -e "Configure your own WireGuard server/VPS" | ||
|  |     echo "" | ||
|  |      | ||
|  |     # Server endpoint | ||
|  |     while [ -z "$WG_ENDPOINT" ]; do | ||
|  |         read -p "Server endpoint (IP:Port or domain:port): " WG_ENDPOINT | ||
|  |         if [[ ! "$WG_ENDPOINT" =~ ^[^:]+:[0-9]+$ ]]; then | ||
|  |             error "Invalid format. Use IP:Port or domain:port (e.g., 1.2.3.4:51820)" | ||
|  |             WG_ENDPOINT="" | ||
|  |         fi | ||
|  |     done | ||
|  |      | ||
|  |     # Server public key | ||
|  |     while [ -z "$WG_SERVER_PUBKEY" ]; do | ||
|  |         read -p "Server public key: " WG_SERVER_PUBKEY | ||
|  |         if [[ ! "$WG_SERVER_PUBKEY" =~ ^[A-Za-z0-9+/]{43}=$ ]]; then | ||
|  |             error "Invalid public key format" | ||
|  |             WG_SERVER_PUBKEY="" | ||
|  |         fi | ||
|  |     done | ||
|  |      | ||
|  |     # Client private key (optional - generate if not provided) | ||
|  |     read -p "Client private key (leave empty to generate): " WG_CLIENT_PRIVKEY | ||
|  |     if [ -z "$WG_CLIENT_PRIVKEY" ]; then | ||
|  |         WG_CLIENT_PRIVKEY=$(wg genkey) | ||
|  |         WG_CLIENT_PUBKEY=$(echo "$WG_CLIENT_PRIVKEY" | wg pubkey) | ||
|  |         echo -e "${GREEN}Generated keypair:${NC}" | ||
|  |         echo -e "  Private key: ${BOLD}$WG_CLIENT_PRIVKEY${NC}" | ||
|  |         echo -e "  Public key:  ${BOLD}$WG_CLIENT_PUBKEY${NC}" | ||
|  |         echo -e "${YELLOW}Add this public key to your server's peer configuration!${NC}" | ||
|  |         read -p "Press Enter when you've added the key to your server..." | ||
|  |     fi | ||
|  |      | ||
|  |     # Client IP in VPN | ||
|  |     read -p "Client VPN IP (e.g., 10.0.0.2/32): " WG_CLIENT_IP | ||
|  |     if [ -z "$WG_CLIENT_IP" ]; then | ||
|  |         WG_CLIENT_IP="10.0.0.2/32" | ||
|  |     fi | ||
|  |      | ||
|  |     # DNS servers | ||
|  |     read -p "DNS servers (comma-separated, default: 1.1.1.1,1.0.0.1): " WG_DNS | ||
|  |     if [ -z "$WG_DNS" ]; then | ||
|  |         WG_DNS="1.1.1.1,1.0.0.1" | ||
|  |     fi | ||
|  |      | ||
|  |     # Allowed IPs | ||
|  |     read -p "Allowed IPs (default: 0.0.0.0/0): " WG_ALLOWED_IPS | ||
|  |     if [ -z "$WG_ALLOWED_IPS" ]; then | ||
|  |         WG_ALLOWED_IPS="0.0.0.0/0" | ||
|  |     fi | ||
|  |      | ||
|  |     # Ask about kill switch bypass | ||
|  |     echo "" | ||
|  |     read -p "Allow direct access to server IP (bypass killswitch)? (Y/n): " -n 1 -r | ||
|  |     echo "" | ||
|  |      | ||
|  |     # AllowedIPs | ||
|  |     read -p "Route all traffic through VPN? (Y/n): " -n 1 -r | ||
|  |     echo "" | ||
|  |     if [[ $REPLY =~ ^[Nn]$ ]]; then | ||
|  |         read -p "Allowed IPs (comma-separated): " WG_ALLOWED_IPS | ||
|  |     else | ||
|  |         WG_ALLOWED_IPS="0.0.0.0/0,::/0" | ||
|  |     fi | ||
|  |      | ||
|  |     # Optional: Multiple servers for failover | ||
|  |     read -p "Add backup servers? (y/N): " -n 1 -r | ||
|  |     echo "" | ||
|  |     if [[ $REPLY =~ ^[Yy]$ ]]; then | ||
|  |         BACKUP_SERVERS=() | ||
|  |         while true; do | ||
|  |             read -p "Backup server endpoint (or 'done' to finish): " BACKUP | ||
|  |             if [ "$BACKUP" = "done" ] || [ -z "$BACKUP" ]; then | ||
|  |                 break | ||
|  |             fi | ||
|  |             BACKUP_SERVERS+=("$BACKUP") | ||
|  |         done | ||
|  |     fi | ||
|  |      | ||
|  |     # Save custom server name for later use | ||
|  |     WG_CUSTOM_NAME="${WG_ENDPOINT%:*}_custom" | ||
|  |     WG_CUSTOM_LOCATION="Custom Location" | ||
|  |      | ||
|  |     log "Custom WireGuard configuration complete" | ||
|  | } | ||
|  | 
 | ||
|  | # Import existing WireGuard config | ||
|  | import_wireguard_config() { | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Import WireGuard Configuration${NC}" | ||
|  |     echo "" | ||
|  |      | ||
|  |     read -p "Path to WireGuard config file: " WG_CONFIG_PATH | ||
|  |      | ||
|  |     if [ ! -f "$WG_CONFIG_PATH" ]; then | ||
|  |         error "File not found: $WG_CONFIG_PATH" | ||
|  |         choose_vpn_provider | ||
|  |         return | ||
|  |     fi | ||
|  |      | ||
|  |     # Validate config | ||
|  |     if ! grep -q "\[Interface\]" "$WG_CONFIG_PATH" || ! grep -q "\[Peer\]" "$WG_CONFIG_PATH"; then | ||
|  |         error "Invalid WireGuard configuration file" | ||
|  |         choose_vpn_provider | ||
|  |         return | ||
|  |     fi | ||
|  |      | ||
|  |     # Copy config | ||
|  |     cp "$WG_CONFIG_PATH" /tmp/imported_wg.conf | ||
|  |     WG_IMPORTED_CONFIG="/tmp/imported_wg.conf" | ||
|  |      | ||
|  |     log "WireGuard config imported successfully" | ||
|  | } | ||
|  | 
 | ||
|  | # Choose VPN provider | ||
|  | choose_vpn_provider() { | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Choose your VPN provider:${NC}" | ||
|  |     echo "1) Mullvad VPN" | ||
|  |     echo "2) Custom WireGuard Server" | ||
|  |     echo "3) Import existing WireGuard config" | ||
|  |     echo "" | ||
|  |      | ||
|  |     while true; do | ||
|  |         read -p "Select option (1-3): " choice | ||
|  |         case $choice in | ||
|  |             1) | ||
|  |                 VPN_PROVIDER="mullvad" | ||
|  |                 get_mullvad_account | ||
|  |                 break | ||
|  |                 ;; | ||
|  |             2) | ||
|  |                 VPN_PROVIDER="custom" | ||
|  |                 get_custom_wireguard_details | ||
|  |                 break | ||
|  |                 ;; | ||
|  |             3) | ||
|  |                 VPN_PROVIDER="import" | ||
|  |                 import_wireguard_config | ||
|  |                 break | ||
|  |                 ;; | ||
|  |             *) | ||
|  |                 error "Invalid choice. Please select 1, 2, or 3." | ||
|  |                 ;; | ||
|  |         esac | ||
|  |     done | ||
|  | } | ||
|  | 
 | ||
|  | # Check system requirements | ||
|  | check_requirements() { | ||
|  |     info "Checking system requirements..." | ||
|  |      | ||
|  |     # Check OS | ||
|  |     if [ -f /etc/os-release ]; then | ||
|  |         . /etc/os-release | ||
|  |         OS=$NAME | ||
|  |         VER=$VERSION_ID | ||
|  |     fi | ||
|  |      | ||
|  |     if [[ ! "$OS" =~ "Ubuntu" ]] && [[ ! "$OS" =~ "Debian" ]]; then | ||
|  |         warning "This script is tested on Ubuntu/Debian. Continue at your own risk." | ||
|  |         read -p "Continue anyway? (y/N): " -n 1 -r | ||
|  |         echo "" | ||
|  |         if [[ ! $REPLY =~ ^[Yy]$ ]]; then | ||
|  |             exit 1 | ||
|  |         fi | ||
|  |     fi | ||
|  |      | ||
|  |     # Check kernel modules for WireGuard | ||
|  |     if ! lsmod | grep -q wireguard; then | ||
|  |         warning "WireGuard kernel module not loaded" | ||
|  |         modprobe wireguard 2>/dev/null || true | ||
|  |     fi | ||
|  |      | ||
|  |     log "System requirements checked" | ||
|  | } | ||
|  | 
 | ||
|  | # Install dependencies | ||
|  | install_dependencies() { | ||
|  |     log "Installing dependencies..." | ||
|  |      | ||
|  |     export DEBIAN_FRONTEND=noninteractive | ||
|  |      | ||
|  |     # Update package lists | ||
|  |     apt-get update &>/dev/null & | ||
|  |     spinner $! | ||
|  |      | ||
|  |     # Install required packages | ||
|  |     local packages=( | ||
|  |         wireguard | ||
|  |         wireguard-tools | ||
|  |         iptables | ||
|  |         iptables-persistent | ||
|  |         python3 | ||
|  |         python3-pip | ||
|  |         python3-venv | ||
|  |         nginx | ||
|  |         curl | ||
|  |         wget | ||
|  |         git | ||
|  |         resolvconf | ||
|  |         net-tools | ||
|  |         jq | ||
|  |     ) | ||
|  |      | ||
|  |     for package in "${packages[@]}"; do | ||
|  |         echo -n "  Installing $package..." | ||
|  |         apt-get install -y "$package" &>/dev/null & | ||
|  |         spinner $! | ||
|  |         echo " ✓" | ||
|  |     done | ||
|  |      | ||
|  |     log "Dependencies installed successfully" | ||
|  | } | ||
|  | 
 | ||
|  | # Create directory structure | ||
|  | create_directories() { | ||
|  |     log "Creating directory structure..." | ||
|  |      | ||
|  |     mkdir -p "$INSTALL_DIR"/{scripts,config,static,logs} | ||
|  |     mkdir -p /etc/wireguard | ||
|  |     mkdir -p /var/log | ||
|  |      | ||
|  |     log "Directories created" | ||
|  | } | ||
|  | 
 | ||
|  | # Install killswitch script | ||
|  | install_killswitch() { | ||
|  |     log "Installing permanent killswitch..." | ||
|  |     warning "⚠️  KILLSWITCH WILL BLOCK ALL INTERNET ACCESS AFTER ACTIVATION!" | ||
|  |     warning "⚠️  Only VPN connections will be allowed!" | ||
|  |     echo "" | ||
|  |     read -p "Activate killswitch now? This cannot be undone! (y/N): " -n 1 -r | ||
|  |     echo "" | ||
|  |     if [[ ! $REPLY =~ ^[Yy]$ ]]; then | ||
|  |         warning "Killswitch installation skipped. System is NOT protected!" | ||
|  |         warning "Run 'systemctl start vpn-killswitch' to activate later." | ||
|  |         return | ||
|  |     fi | ||
|  |      | ||
|  |     # Create killswitch script | ||
|  |     cat > /usr/local/bin/vpn-killswitch.sh << 'EOFSCRIPT' | ||
|  | #!/bin/bash | ||
|  | 
 | ||
|  | LAN_IF="__LAN_INTERFACE__" | ||
|  | LAN_NET="__LAN_NETWORK__" | ||
|  | 
 | ||
|  | enable_killswitch() { | ||
|  |     echo "ENABLING PERMANENT KILLSWITCH - NO INTERNET WITHOUT VPN" | ||
|  |      | ||
|  |     # Reset all rules | ||
|  |     iptables -F | ||
|  |     iptables -X | ||
|  |     iptables -t nat -F | ||
|  |     iptables -t nat -X | ||
|  |     iptables -t mangle -F | ||
|  |     iptables -t mangle -X | ||
|  |      | ||
|  |     # DEFAULT: BLOCK EVERYTHING | ||
|  |     iptables -P INPUT DROP | ||
|  |     iptables -P FORWARD DROP | ||
|  |     iptables -P OUTPUT DROP | ||
|  |      | ||
|  |     # Allow loopback | ||
|  |     iptables -A INPUT -i lo -j ACCEPT | ||
|  |     iptables -A OUTPUT -o lo -j ACCEPT | ||
|  |      | ||
|  |     # Allow LAN communication | ||
|  |     iptables -A INPUT -i $LAN_IF -s $LAN_NET -j ACCEPT | ||
|  |     iptables -A OUTPUT -o $LAN_IF -d $LAN_NET -j ACCEPT | ||
|  |      | ||
|  |     # Allow DNS for initial connection (root only) | ||
|  |     iptables -A OUTPUT -p udp --dport 53 -m owner --uid-owner root -j ACCEPT | ||
|  |     iptables -A OUTPUT -p tcp --dport 53 -m owner --uid-owner root -j ACCEPT | ||
|  |      | ||
|  |     # Allow established connections | ||
|  |     iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | ||
|  |     iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | ||
|  |     iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | ||
|  |      | ||
|  |     # Allow forwarding from LAN to VPN (when active) | ||
|  |     iptables -A FORWARD -i $LAN_IF -s $LAN_NET -j ACCEPT | ||
|  |      | ||
|  |     # Block IPv6 completely | ||
|  |     ip6tables -P INPUT DROP | ||
|  |     ip6tables -P FORWARD DROP | ||
|  |     ip6tables -P OUTPUT DROP | ||
|  |     ip6tables -A INPUT -i lo -j ACCEPT | ||
|  |     ip6tables -A OUTPUT -o lo -j ACCEPT | ||
|  |      | ||
|  |     # Save rules | ||
|  |     iptables-save > /etc/iptables/rules.v4 | ||
|  |     ip6tables-save > /etc/iptables/rules.v6 | ||
|  |      | ||
|  |     echo "KILLSWITCH ACTIVE - System is now protected" | ||
|  | } | ||
|  | 
 | ||
|  | disable_killswitch() { | ||
|  |     echo "WARNING: Killswitch cannot be disabled for security!" | ||
|  |     enable_killswitch | ||
|  | } | ||
|  | 
 | ||
|  | case "$1" in | ||
|  |     enable|disable) | ||
|  |         enable_killswitch | ||
|  |         ;; | ||
|  |     *) | ||
|  |         echo "Usage: $0 {enable|disable}" | ||
|  |         exit 1 | ||
|  |         ;; | ||
|  | esac | ||
|  | EOFSCRIPT | ||
|  |      | ||
|  |     # Replace placeholders | ||
|  |     sed -i "s|__LAN_INTERFACE__|$LAN_INTERFACE|g" /usr/local/bin/vpn-killswitch.sh | ||
|  |     sed -i "s|__LAN_NETWORK__|$LAN_NETWORK|g" /usr/local/bin/vpn-killswitch.sh | ||
|  |      | ||
|  |     chmod +x /usr/local/bin/vpn-killswitch.sh | ||
|  |      | ||
|  |     # Create systemd service | ||
|  |     cat > /etc/systemd/system/vpn-killswitch.service << 'EOF' | ||
|  | [Unit] | ||
|  | Description=VPN Killswitch - Block ALL traffic except through VPN | ||
|  | DefaultDependencies=no | ||
|  | Before=network-pre.target | ||
|  | Wants=network-pre.target | ||
|  | 
 | ||
|  | [Service] | ||
|  | Type=oneshot | ||
|  | RemainAfterExit=yes | ||
|  | ExecStart=/usr/local/bin/vpn-killswitch.sh enable | ||
|  | 
 | ||
|  | [Install] | ||
|  | WantedBy=sysinit.target | ||
|  | EOF | ||
|  |      | ||
|  |     # Enable and start killswitch | ||
|  |     systemctl daemon-reload | ||
|  |     systemctl enable vpn-killswitch.service | ||
|  |     systemctl start vpn-killswitch.service | ||
|  |      | ||
|  |     log "Killswitch installed and activated" | ||
|  | } | ||
|  | 
 | ||
|  | # Install VPN provider specific components | ||
|  | install_vpn_provider() { | ||
|  |     log "Installing VPN provider components..." | ||
|  |      | ||
|  |     case "$VPN_PROVIDER" in | ||
|  |         mullvad) | ||
|  |             install_mullvad | ||
|  |             ;; | ||
|  |         custom) | ||
|  |             setup_custom_provider | ||
|  |             ;; | ||
|  |         import) | ||
|  |             setup_import_provider | ||
|  |             ;; | ||
|  |     esac | ||
|  | } | ||
|  | 
 | ||
|  | # Install Mullvad | ||
|  | install_mullvad() { | ||
|  |     log "Installing Mullvad client..." | ||
|  |      | ||
|  |     # Download Mullvad signing key | ||
|  |     curl -fsSL https://mullvad.net/media/mullvad-code-signing.asc | gpg --dearmor -o /usr/share/keyrings/mullvad-keyring.gpg | ||
|  |      | ||
|  |     # Add Mullvad repository | ||
|  |     echo "deb [signed-by=/usr/share/keyrings/mullvad-keyring.gpg arch=$( dpkg --print-architecture )] https://repository.mullvad.net/deb/stable $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/mullvad.list | ||
|  |      | ||
|  |     # Update and install | ||
|  |     apt-get update &>/dev/null | ||
|  |     apt-get install -y mullvad-vpn &>/dev/null || { | ||
|  |         warning "Could not install Mullvad client, using WireGuard directly" | ||
|  |     } | ||
|  |      | ||
|  |     # Login to Mullvad | ||
|  |     if command -v mullvad &>/dev/null; then | ||
|  |         mullvad account login "$MULLVAD_ACCOUNT" &>/dev/null || { | ||
|  |             warning "Could not login to Mullvad account" | ||
|  |         } | ||
|  |     fi | ||
|  |      | ||
|  |     # Generate WireGuard keys | ||
|  |     if [ ! -f /etc/wireguard/mullvad_private.key ]; then | ||
|  |         wg genkey | tee /etc/wireguard/mullvad_private.key | wg pubkey > /etc/wireguard/mullvad_public.key | ||
|  |         chmod 600 /etc/wireguard/mullvad_private.key | ||
|  |     fi | ||
|  |      | ||
|  |     # Save config | ||
|  |     echo "mullvad" > "$INSTALL_DIR/provider.conf" | ||
|  |     echo "$MULLVAD_ACCOUNT" > "$INSTALL_DIR/.mullvad_account" | ||
|  |     chmod 600 "$INSTALL_DIR/.mullvad_account" | ||
|  |      | ||
|  |     log "Mullvad configuration complete" | ||
|  | } | ||
|  | 
 | ||
|  | # Setup custom provider | ||
|  | setup_custom_provider() { | ||
|  |     log "Setting up custom WireGuard provider..." | ||
|  |      | ||
|  |     # Create config file for custom servers | ||
|  |     mkdir -p "$INSTALL_DIR/providers" | ||
|  |      | ||
|  |     # Save the custom server configuration | ||
|  |     cat > "$INSTALL_DIR/providers/custom_servers.json" << EOF | ||
|  | { | ||
|  |   "$WG_CUSTOM_LOCATION": { | ||
|  |     "Custom": [{ | ||
|  |       "hostname": "$WG_CUSTOM_NAME", | ||
|  |       "endpoint": "$WG_ENDPOINT", | ||
|  |       "public_key": "$WG_SERVER_PUBKEY", | ||
|  |       "private_key": "$WG_CLIENT_PRIVKEY", | ||
|  |       "address": "$WG_CLIENT_IP", | ||
|  |       "dns": "$WG_DNS", | ||
|  |       "allowed_ips": "$WG_ALLOWED_IPS", | ||
|  |       "type": "WireGuard", | ||
|  |       "provider": "Custom" | ||
|  |     }] | ||
|  |   } | ||
|  | } | ||
|  | EOF | ||
|  |      | ||
|  |     # Save provider config | ||
|  |     echo "custom" > "$INSTALL_DIR/provider.conf" | ||
|  |      | ||
|  |     # If backup servers were provided | ||
|  |     if [ ${#BACKUP_SERVERS[@]} -gt 0 ]; then | ||
|  |         log "Adding backup servers..." | ||
|  |         # Additional logic for backup servers | ||
|  |     fi | ||
|  |      | ||
|  |     log "Custom provider setup complete" | ||
|  | } | ||
|  | 
 | ||
|  | # Setup import provider | ||
|  | setup_import_provider() { | ||
|  |     log "Setting up import provider..." | ||
|  |      | ||
|  |     mkdir -p "$INSTALL_DIR/providers/imported" | ||
|  |      | ||
|  |     # Copy imported config | ||
|  |     if [ -n "$WG_IMPORTED_CONFIG" ]; then | ||
|  |         cp "$WG_IMPORTED_CONFIG" "$INSTALL_DIR/providers/imported/" | ||
|  |         log "Imported configuration saved" | ||
|  |     fi | ||
|  |      | ||
|  |     # Save provider config | ||
|  |     echo "import" > "$INSTALL_DIR/provider.conf" | ||
|  |      | ||
|  |     log "Import provider setup complete" | ||
|  | } | ||
|  | 
 | ||
|  | # Install VPN provider (unified function) | ||
|  | install_vpn_provider() { | ||
|  |     case "$VPN_PROVIDER" in | ||
|  |         "mullvad") | ||
|  |             install_mullvad | ||
|  |             ;; | ||
|  |         "custom") | ||
|  |             setup_custom_provider | ||
|  |             ;; | ||
|  |         "import") | ||
|  |             setup_import_provider | ||
|  |             ;; | ||
|  |         *) | ||
|  |             error "Unknown VPN provider: $VPN_PROVIDER" | ||
|  |             exit 1 | ||
|  |             ;; | ||
|  |     esac | ||
|  | } | ||
|  | 
 | ||
|  | # Install Python backend | ||
|  | install_backend() { | ||
|  |     log "Installing VPN Gateway backend..." | ||
|  |      | ||
|  |     # Ensure install directory exists | ||
|  |     mkdir -p "$INSTALL_DIR" | ||
|  |      | ||
|  |     # Create virtual environment | ||
|  |     python3 -m venv "$INSTALL_DIR/venv" | ||
|  |     source "$INSTALL_DIR/venv/bin/activate" | ||
|  |      | ||
|  |     # Install Python packages | ||
|  |     pip install --upgrade pip &>/dev/null | ||
|  |     pip install flask flask-cors requests gunicorn pyyaml &>/dev/null | ||
|  |      | ||
|  |     # Download or create the multi-provider backend | ||
|  |     # For production, this would be downloaded from GitHub | ||
|  |     # Here we create it inline for the complete solution | ||
|  |      | ||
|  |     log "Installing multi-provider backend..." | ||
|  |      | ||
|  |     # The backend app.py would be downloaded from GitHub in production: | ||
|  |     # wget -O "$INSTALL_DIR/app.py" "$GITHUB_REPO/app.py" | ||
|  |      | ||
|  |     # For now, we use a simplified version that supports all providers | ||
|  |     cat > "$INSTALL_DIR/app.py" << 'EOFAPP' | ||
|  | #!/usr/bin/env python3 | ||
|  | # Multi-Provider VPN Backend | ||
|  | # This is a placeholder - in production, use the full backend from the artifact | ||
|  | # Download from: https://github.com/yourusername/vpn-gateway/blob/main/backend/app.py | ||
|  | 
 | ||
|  | from flask import Flask, request, jsonify, send_from_directory | ||
|  | from flask_cors import CORS | ||
|  | import subprocess | ||
|  | import json | ||
|  | import os | ||
|  | import logging | ||
|  | 
 | ||
|  | app = Flask(__name__) | ||
|  | CORS(app) | ||
|  | 
 | ||
|  | # ... (Full backend code would be here) | ||
|  | # For production, download the complete multi-provider backend | ||
|  | 
 | ||
|  | @app.route('/') | ||
|  | def index(): | ||
|  |     return send_from_directory('__INSTALL_DIR__/static', 'index.html') | ||
|  | 
 | ||
|  | if __name__ == '__main__': | ||
|  |     app.run(host='0.0.0.0', port=5000) | ||
|  | EOFAPP | ||
|  |      | ||
|  |     # Replace placeholders | ||
|  |     sed -i "s|__INSTALL_DIR__|$INSTALL_DIR|g" "$INSTALL_DIR/app.py" | ||
|  |      | ||
|  |     log "Backend installed" | ||
|  | } | ||
|  | 
 | ||
|  | # Setup nginx (placeholder function) | ||
|  | setup_nginx() { | ||
|  |     log "Setting up nginx reverse proxy..." | ||
|  |     # This would contain nginx configuration | ||
|  |     # For now, we'll skip this as it's optional | ||
|  |     log "Nginx setup skipped (optional)" | ||
|  | } | ||
|  | 
 | ||
|  | # Finalize installation | ||
|  | finalize_installation() { | ||
|  |     log "Finalizing installation..." | ||
|  |      | ||
|  |     # Set proper permissions | ||
|  |     chown -R root:root "$INSTALL_DIR" | ||
|  |     chmod +x "$INSTALL_DIR"/scripts/*.sh 2>/dev/null || true | ||
|  |      | ||
|  |     # Enable and start services | ||
|  |     systemctl enable vpn-webui vpn-killswitch vpn-security-monitor | ||
|  |     systemctl start vpn-killswitch | ||
|  |     systemctl start vpn-security-monitor | ||
|  |     systemctl start vpn-webui | ||
|  |      | ||
|  |     log "Installation finalized" | ||
|  | } | ||
|  | 
 | ||
|  | # Show installation summary | ||
|  | show_summary() { | ||
|  |     echo "" | ||
|  |     echo -e "${GREEN}${BOLD}Installation Complete!${NC}" | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Services Status:${NC}" | ||
|  |     systemctl is-active vpn-webui && echo -e "  WebUI:           ${GREEN}Running${NC}" || echo -e "  WebUI:           ${RED}Stopped${NC}" | ||
|  |     systemctl is-active vpn-killswitch && echo -e "  Killswitch:      ${GREEN}Active${NC}" || echo -e "  Killswitch:      ${RED}Inactive${NC}" | ||
|  |     systemctl is-active vpn-security-monitor && echo -e "  Security Monitor: ${GREEN}Running${NC}" || echo -e "  Security Monitor: ${RED}Stopped${NC}" | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Access Information:${NC}" | ||
|  |     echo -e "  WebUI URL:       ${BOLD}http://$LAN_IP:5000${NC}" | ||
|  |     echo -e "  Install Dir:     ${BOLD}$INSTALL_DIR${NC}" | ||
|  |     echo -e "  Log File:        ${BOLD}$LOG_FILE${NC}" | ||
|  |     echo "" | ||
|  |     echo -e "${YELLOW}Important Notes:${NC}" | ||
|  |     echo -e "  • The killswitch is ${BOLD}PERMANENTLY ACTIVE${NC}" | ||
|  |     echo -e "  • No internet access without VPN connection" | ||
|  |     echo -e "  • Use the WebUI to connect to VPN servers" | ||
|  |     echo "" | ||
|  | } | ||
|  | 
 | ||
|  | # Install WebUI | ||
|  | install_webui() { | ||
|  |     log "Installing WebUI..." | ||
|  |      | ||
|  |     # Download WebUI from GitHub or create inline | ||
|  |     cat > "$INSTALL_DIR/static/index.html" << 'EOFHTML' | ||
|  | <!DOCTYPE html> | ||
|  | <html lang="en"> | ||
|  | <head> | ||
|  |     <meta charset="UTF-8"> | ||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
|  |     <title>VPN Gateway Control</title> | ||
|  |     <style> | ||
|  |         * { margin: 0; padding: 0; box-sizing: border-box; } | ||
|  |         body { | ||
|  |             font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | ||
|  |             background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||
|  |             min-height: 100vh; | ||
|  |             display: flex; | ||
|  |             justify-content: center; | ||
|  |             align-items: center; | ||
|  |             padding: 20px; | ||
|  |         } | ||
|  |         .container { | ||
|  |             background: rgba(255, 255, 255, 0.95); | ||
|  |             border-radius: 20px; | ||
|  |             box-shadow: 0 20px 60px rgba(0,0,0,0.3); | ||
|  |             max-width: 800px; | ||
|  |             width: 100%; | ||
|  |             padding: 40px; | ||
|  |         } | ||
|  |         h1 { text-align: center; margin-bottom: 30px; } | ||
|  |         .status-card { | ||
|  |             background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||
|  |             color: white; | ||
|  |             padding: 30px; | ||
|  |             border-radius: 15px; | ||
|  |             margin-bottom: 30px; | ||
|  |         } | ||
|  |         select, button { | ||
|  |             width: 100%; | ||
|  |             padding: 15px; | ||
|  |             margin: 10px 0; | ||
|  |             border-radius: 10px; | ||
|  |             font-size: 16px; | ||
|  |         } | ||
|  |         button { | ||
|  |             background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%); | ||
|  |             color: white; | ||
|  |             border: none; | ||
|  |             cursor: pointer; | ||
|  |         } | ||
|  |         button:hover { transform: translateY(-2px); } | ||
|  |         .btn-disconnect { | ||
|  |             background: linear-gradient(135deg, #f87171 0%, #ef4444 100%); | ||
|  |         } | ||
|  |         .security-notice { | ||
|  |             background: #4ade80; | ||
|  |             color: white; | ||
|  |             padding: 20px; | ||
|  |             border-radius: 10px; | ||
|  |             margin-bottom: 20px; | ||
|  |         } | ||
|  |     </style> | ||
|  | </head> | ||
|  | <body> | ||
|  |     <div class="container"> | ||
|  |         <h1>🔐 VPN Gateway Control</h1> | ||
|  |          | ||
|  |         <div class="security-notice"> | ||
|  |             <strong>🛡️ Security Status: PROTECTED</strong><br> | ||
|  |             ✓ Killswitch permanently active<br> | ||
|  |             ✓ No internet without VPN | ||
|  |         </div> | ||
|  |          | ||
|  |         <div class="status-card"> | ||
|  |             <h2>Status: <span id="status">Checking...</span></h2> | ||
|  |             <p>IP: <span id="ip">-</span></p> | ||
|  |             <p>Location: <span id="location">-</span></p> | ||
|  |         </div> | ||
|  |          | ||
|  |         <select id="countrySelect" onchange="updateCities()"> | ||
|  |             <option>Loading countries...</option> | ||
|  |         </select> | ||
|  |          | ||
|  |         <select id="citySelect" onchange="updateServers()"> | ||
|  |             <option>Select country first</option> | ||
|  |         </select> | ||
|  |          | ||
|  |         <select id="serverSelect"> | ||
|  |             <option>Select city first</option> | ||
|  |         </select> | ||
|  |          | ||
|  |         <button onclick="connectVPN()">Connect</button> | ||
|  |         <button class="btn-disconnect" onclick="disconnectVPN()">Disconnect</button> | ||
|  |     </div> | ||
|  |      | ||
|  |     <script> | ||
|  |         let servers = {}; | ||
|  |          | ||
|  |         async function loadServers() { | ||
|  |             const response = await fetch('/api/servers'); | ||
|  |             const data = await response.json(); | ||
|  |             servers = data.servers; | ||
|  |              | ||
|  |             const countrySelect = document.getElementById('countrySelect'); | ||
|  |             countrySelect.innerHTML = '<option value="">Select country</option>'; | ||
|  |             Object.keys(servers).forEach(country => { | ||
|  |                 countrySelect.innerHTML += '<option value="' + country + '">' + country + '</option>'; | ||
|  |             }); | ||
|  |         } | ||
|  |          | ||
|  |         function updateCities() { | ||
|  |             const country = document.getElementById('countrySelect').value; | ||
|  |             const citySelect = document.getElementById('citySelect'); | ||
|  |              | ||
|  |             if (country && servers[country]) { | ||
|  |                 citySelect.innerHTML = '<option value="">Select city</option>'; | ||
|  |                 Object.keys(servers[country]).forEach(city => { | ||
|  |                     citySelect.innerHTML += '<option value="' + city + '">' + city + '</option>'; | ||
|  |                 }); | ||
|  |             } | ||
|  |         } | ||
|  |          | ||
|  |         function updateServers() { | ||
|  |             const country = document.getElementById('countrySelect').value; | ||
|  |             const city = document.getElementById('citySelect').value; | ||
|  |             const serverSelect = document.getElementById('serverSelect'); | ||
|  |              | ||
|  |             if (country && city && servers[country][city]) { | ||
|  |                 serverSelect.innerHTML = '<option value="">Select server</option>'; | ||
|  |                 servers[country][city].forEach(server => { | ||
|  |                     serverSelect.innerHTML += '<option value="' + server.hostname + '">' + server.hostname + '</option>'; | ||
|  |                 }); | ||
|  |             } | ||
|  |         } | ||
|  |          | ||
|  |         async function updateStatus() { | ||
|  |             const response = await fetch('/api/status'); | ||
|  |             const data = await response.json(); | ||
|  |              | ||
|  |             document.getElementById('status').textContent = data.connected ? 'Connected' : 'Disconnected'; | ||
|  |             document.getElementById('ip').textContent = data.ip || '-'; | ||
|  |             document.getElementById('location').textContent = data.location || '-'; | ||
|  |         } | ||
|  |          | ||
|  |         async function connectVPN() { | ||
|  |             const server = document.getElementById('serverSelect').value; | ||
|  |             if (!server) { alert('Select a server'); return; } | ||
|  |              | ||
|  |             const response = await fetch('/api/connect', { | ||
|  |                 method: 'POST', | ||
|  |                 headers: {'Content-Type': 'application/json'}, | ||
|  |                 body: JSON.stringify({server: server}) | ||
|  |             }); | ||
|  |              | ||
|  |             const data = await response.json(); | ||
|  |             if (data.success) { | ||
|  |                 alert('Connected!'); | ||
|  |                 updateStatus(); | ||
|  |             } else { | ||
|  |                 alert('Connection failed: ' + data.error); | ||
|  |             } | ||
|  |         } | ||
|  |          | ||
|  |         async function disconnectVPN() { | ||
|  |             const response = await fetch('/api/disconnect', {method: 'POST'}); | ||
|  |             const data = await response.json(); | ||
|  |             if (data.success) { | ||
|  |                 alert('Disconnected - No internet (killswitch active)'); | ||
|  |                 updateStatus(); | ||
|  |             } | ||
|  |         } | ||
|  |          | ||
|  |         loadServers(); | ||
|  |         updateStatus(); | ||
|  |         setInterval(updateStatus, 10000); | ||
|  |     </script> | ||
|  | </body> | ||
|  | </html> | ||
|  | EOFHTML | ||
|  |      | ||
|  |     log "WebUI installed" | ||
|  | } | ||
|  | 
 | ||
|  | # Setup systemd services | ||
|  | setup_services() { | ||
|  |     log "Setting up systemd services..." | ||
|  |      | ||
|  |     # VPN WebUI service | ||
|  |     cat > /etc/systemd/system/vpn-webui.service << EOF | ||
|  | [Unit] | ||
|  | Description=VPN Gateway WebUI | ||
|  | After=network.target vpn-killswitch.service | ||
|  | 
 | ||
|  | [Service] | ||
|  | Type=simple | ||
|  | User=root | ||
|  | WorkingDirectory=$INSTALL_DIR | ||
|  | Environment="PATH=$INSTALL_DIR/venv/bin" | ||
|  | ExecStart=$INSTALL_DIR/venv/bin/gunicorn --bind 0.0.0.0:5000 app:app | ||
|  | Restart=always | ||
|  | 
 | ||
|  | [Install] | ||
|  | WantedBy=multi-user.target | ||
|  | EOF | ||
|  |      | ||
|  |     # Security monitor service | ||
|  |     cat > /etc/systemd/system/vpn-security-monitor.service << 'EOF' | ||
|  | [Unit] | ||
|  | Description=VPN Security Monitor | ||
|  | After=vpn-killswitch.service | ||
|  | 
 | ||
|  | [Service] | ||
|  | Type=simple | ||
|  | ExecStart=/usr/local/bin/vpn-security-monitor.sh | ||
|  | Restart=always | ||
|  | 
 | ||
|  | [Install] | ||
|  | WantedBy=multi-user.target | ||
|  | EOF | ||
|  |      | ||
|  |     # Create security monitor script | ||
|  |     cat > /usr/local/bin/vpn-security-monitor.sh << 'EOFMON' | ||
|  | #!/bin/bash | ||
|  | while true; do | ||
|  |     # Check if killswitch is active | ||
|  |     if ! iptables -L -n | grep -q "policy DROP"; then | ||
|  |         /usr/local/bin/vpn-killswitch.sh enable | ||
|  |     fi | ||
|  |     sleep 10 | ||
|  | done | ||
|  | EOFMON | ||
|  |      | ||
|  |     chmod +x /usr/local/bin/vpn-security-monitor.sh | ||
|  |      | ||
|  |     # Reload and start services | ||
|  |     systemctl daemon-reload | ||
|  |     systemctl enable vpn-killswitch vpn-webui vpn-security-monitor | ||
|  |     systemctl start vpn-killswitch vpn-webui vpn-security-monitor | ||
|  |      | ||
|  |     log "Services configured and started" | ||
|  | } | ||
|  | 
 | ||
|  | # Configure Nginx | ||
|  | setup_nginx() { | ||
|  |     log "Configuring Nginx..." | ||
|  |      | ||
|  |     cat > /etc/nginx/sites-available/vpn-gateway << EOF | ||
|  | server { | ||
|  |     listen 80; | ||
|  |     server_name _; | ||
|  |      | ||
|  |     location / { | ||
|  |         proxy_pass http://127.0.0.1:5000; | ||
|  |         proxy_set_header Host \$host; | ||
|  |         proxy_set_header X-Real-IP \$remote_addr; | ||
|  |     } | ||
|  | } | ||
|  | EOF | ||
|  |      | ||
|  |     ln -sf /etc/nginx/sites-available/vpn-gateway /etc/nginx/sites-enabled/ | ||
|  |     rm -f /etc/nginx/sites-enabled/default | ||
|  |      | ||
|  |     nginx -t && systemctl restart nginx | ||
|  |      | ||
|  |     log "Nginx configured" | ||
|  | } | ||
|  | 
 | ||
|  | # Final setup | ||
|  | finalize_installation() { | ||
|  |     log "Finalizing installation..." | ||
|  |      | ||
|  |     # Set IP forwarding | ||
|  |     echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf | ||
|  |     echo "net.ipv6.conf.all.disable_ipv6=1" >> /etc/sysctl.conf | ||
|  |     sysctl -p &>/dev/null | ||
|  |      | ||
|  |     # Save iptables rules | ||
|  |     netfilter-persistent save &>/dev/null | ||
|  |      | ||
|  |     log "Installation complete!" | ||
|  | } | ||
|  | 
 | ||
|  | # Show summary | ||
|  | show_summary() { | ||
|  |     echo "" | ||
|  |     echo -e "${GREEN}╔════════════════════════════════════════════════════════╗${NC}" | ||
|  |     echo -e "${GREEN}║        VPN Gateway Installation Complete!              ║${NC}" | ||
|  |     echo -e "${GREEN}╚════════════════════════════════════════════════════════╝${NC}" | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Access WebUI:${NC}" | ||
|  |     echo -e "  Local: ${BOLD}http://$LAN_IP${NC}" | ||
|  |     echo -e "  Port:  ${BOLD}5000${NC}" | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}VPN Provider:${NC}" | ||
|  |     echo -e "  Type: ${BOLD}$VPN_PROVIDER${NC}" | ||
|  |     if [ "$VPN_PROVIDER" = "mullvad" ]; then | ||
|  |         echo -e "  Account: ${BOLD}Configured${NC}" | ||
|  |     elif [ "$VPN_PROVIDER" = "custom" ]; then | ||
|  |         echo -e "  Server: ${BOLD}$WG_ENDPOINT${NC}" | ||
|  |     elif [ "$VPN_PROVIDER" = "import" ]; then | ||
|  |         echo -e "  Config: ${BOLD}Imported${NC}" | ||
|  |     fi | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Security Status:${NC}" | ||
|  |     echo -e "  Killswitch: ${GREEN}✓ Active${NC}" | ||
|  |     echo -e "  Firewall:   ${GREEN}✓ Configured${NC}" | ||
|  |     echo -e "  No Leaks:   ${GREEN}✓ Protected${NC}" | ||
|  |     echo "" | ||
|  |     echo -e "${YELLOW}Important:${NC}" | ||
|  |     echo -e "  • Killswitch is ${BOLD}permanently active${NC}" | ||
|  |     echo -e "  • ${BOLD}No internet${NC} without VPN connection" | ||
|  |     echo -e "  • Configure clients to use ${BOLD}$LAN_IP${NC} as gateway" | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Commands:${NC}" | ||
|  |     echo -e "  Status:  ${BOLD}systemctl status vpn-webui${NC}" | ||
|  |     echo -e "  Logs:    ${BOLD}journalctl -u vpn-webui -f${NC}" | ||
|  |     echo -e "  Test:    ${BOLD}curl http://localhost:5000/api/status${NC}" | ||
|  |     echo "" | ||
|  | } | ||
|  | 
 | ||
|  | # Cleanup on error | ||
|  | cleanup_on_error() { | ||
|  |     error "Installation failed! Cleaning up..." | ||
|  |     systemctl stop vpn-webui vpn-killswitch vpn-security-monitor 2>/dev/null | ||
|  |     rm -rf "$INSTALL_DIR" | ||
|  |     exit 1 | ||
|  | } | ||
|  | 
 | ||
|  | # Main installation flow | ||
|  | main() { | ||
|  |     trap cleanup_on_error ERR | ||
|  |      | ||
|  |     show_banner | ||
|  |     check_root | ||
|  |     detect_container | ||
|  |     check_requirements | ||
|  |     detect_network | ||
|  |     choose_vpn_provider | ||
|  |      | ||
|  |     echo "" | ||
|  |     echo -e "${CYAN}Ready to install with the following configuration:${NC}" | ||
|  |     echo -e "  Network Interface: ${BOLD}$LAN_INTERFACE${NC}" | ||
|  |     echo -e "  Network Subnet:    ${BOLD}$LAN_NETWORK${NC}" | ||
|  |     echo -e "  Container Type:    ${BOLD}$CONTAINER_TYPE${NC}" | ||
|  |     echo -e "  VPN Provider:      ${BOLD}$VPN_PROVIDER${NC}" | ||
|  |      | ||
|  |     if [ "$VPN_PROVIDER" = "mullvad" ]; then | ||
|  |         echo -e "  Mullvad Account:   ${BOLD}${MULLVAD_ACCOUNT:0:4}...${MULLVAD_ACCOUNT: -4}${NC}" | ||
|  |     elif [ "$VPN_PROVIDER" = "custom" ]; then | ||
|  |         echo -e "  Custom Server:     ${BOLD}$WG_ENDPOINT${NC}" | ||
|  |     elif [ "$VPN_PROVIDER" = "import" ]; then | ||
|  |         echo -e "  Config File:       ${BOLD}$WG_IMPORTED_CONFIG${NC}" | ||
|  |     fi | ||
|  |      | ||
|  |     echo "" | ||
|  |     read -p "Proceed with installation? (Y/n): " -n 1 -r | ||
|  |     echo "" | ||
|  |      | ||
|  |     if [[ ! $REPLY =~ ^[Yy]$ ]] && [ -n "$REPLY" ]; then | ||
|  |         echo "Installation cancelled" | ||
|  |         exit 0 | ||
|  |     fi | ||
|  |      | ||
|  |     install_dependencies | ||
|  |     create_directories | ||
|  |     install_vpn_provider  # Install VPN first (needs internet) | ||
|  |     install_killswitch    # Then activate killswitch (blocks internet) | ||
|  |     install_backend | ||
|  |     install_webui | ||
|  |     setup_services | ||
|  |     setup_nginx | ||
|  |     finalize_installation | ||
|  |      | ||
|  |     show_summary | ||
|  | } | ||
|  | 
 | ||
|  | # Run main function | ||
|  | main "$@" |