#cloud-config # Secure VPN Server Cloud-Init Configuration # # BEFORE DEPLOYMENT: # 1. Replace the example SSH keys below with your actual public keys # 2. Generate your SSH key: ssh-keygen -t ed25519 -C "your_email@example.com" # 3. Copy your public key: cat ~/.ssh/id_ed25519.pub # 4. Paste it in the ssh_authorized_keys sections below # # AFTER DEPLOYMENT: # - Connect via SSH: ssh -p 2222 vpnuser@YOUR_SERVER_IP # - Download VPN configs: scp -P 2222 -r vpnuser@YOUR_SERVER_IP:/var/lib/vpn/clients/ ./ # SSH Keys Configuration - Add your SSH public keys here # Keys are defined in the users section below for the vpnuser account package_update: true package_upgrade: true # User configuration users: - name: vpnuser shell: /bin/bash sudo: ['ALL=(ALL) NOPASSWD:ALL'] ssh_authorized_keys: # Replace these example keys with your actual SSH public keys: - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEXAMPLE1234567890abcdefghijklmnopqrstuvwxyz user@example.com" - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQEXAMPLE1234567890abcdefghijklmnopqrstuvwxyz user2@example.com" packages: - wireguard - openvpn - easy-rsa - stunnel4 - iptables-persistent - curl - fail2ban - unattended-upgrades write_files: # Enable IP forwarding - path: /etc/sysctl.d/99-ip-forward.conf content: | net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 # Security hardening net.ipv4.conf.all.send_redirects=0 net.ipv4.conf.default.send_redirects=0 net.ipv4.conf.all.accept_redirects=0 net.ipv4.conf.default.accept_redirects=0 net.ipv4.conf.all.secure_redirects=0 net.ipv4.conf.default.secure_redirects=0 permissions: '0644' # Secure firewall rules with DROP default policy - path: /etc/iptables/rules.v4 content: | *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] # Allow loopback -A INPUT -i lo -j ACCEPT -A OUTPUT -o lo -j ACCEPT # Allow established and related connections -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # Rate limiting for SSH (hardened port) -A INPUT -p tcp --dport 2222 -m conntrack --ctstate NEW -m recent --set --name ssh -A INPUT -p tcp --dport 2222 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 --name ssh -j DROP -A INPUT -p tcp --dport 2222 -j ACCEPT # Rate limiting for VPN ports -A INPUT -p udp --dport 51820 -m conntrack --ctstate NEW -m limit --limit 10/min --limit-burst 20 -j ACCEPT -A INPUT -p tcp --dport 1194 -m conntrack --ctstate NEW -m limit --limit 10/min --limit-burst 20 -j ACCEPT -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -m limit --limit 20/min --limit-burst 50 -j ACCEPT # VPN traffic forwarding with security -A FORWARD -i wg0 -o eth0 -j ACCEPT -A FORWARD -i tun0 -o eth0 -j ACCEPT -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A FORWARD -i eth0 -o tun0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # Drop invalid packets -A INPUT -m conntrack --ctstate INVALID -j DROP -A FORWARD -m conntrack --ctstate INVALID -j DROP COMMIT *nat :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] # NAT rules for VPN traffic -A POSTROUTING -s 10.66.66.0/24 ! -d 10.66.66.0/24 -o eth0 -j MASQUERADE -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -o eth0 -j MASQUERADE COMMIT permissions: '0644' # Fail2ban configuration for VPN protection - path: /etc/fail2ban/jail.d/vpn.conf content: | [sshd] enabled = true port = 2222 filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 3600 [openvpn] enabled = true port = 1194,443 filter = openvpn logpath = /var/log/openvpn/server.log maxretry = 5 bantime = 1800 permissions: '0644' # Secure stunnel config template - path: /etc/stunnel/stunnel.conf.template content: | pid = /var/run/stunnel4/stunnel.pid setuid = stunnel4 setgid = stunnel4 foreground = no debug = 3 output = /var/log/stunnel4/stunnel.log # Security options options = NO_SSLv2 options = NO_SSLv3 options = CIPHER_SERVER_PREFERENCE ciphersuites = TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 [openvpn-ssl] accept = 443 connect = 127.0.0.1:1194 cert = /etc/stunnel/stunnel.pem # Require client certificates for mutual TLS verify = 2 CAfile = /etc/stunnel/ca.pem permissions: '0644' # Automatic security updates - path: /etc/apt/apt.conf.d/20auto-upgrades content: | APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1"; permissions: '0644' # SSH hardening configuration - path: /etc/ssh/sshd_config.d/99-hardening.conf content: | # Change default port (update firewall rules accordingly) Port 2222 # Protocol and encryption Protocol 2 # Authentication PermitRootLogin no PasswordAuthentication no ChallengeResponseAuthentication no PubkeyAuthentication yes AuthenticationMethods publickey # Login restrictions MaxAuthTries 3 MaxSessions 2 LoginGraceTime 30 # Disable unused authentication methods KerberosAuthentication no GSSAPIAuthentication no UsePAM yes # Network settings ClientAliveInterval 300 ClientAliveCountMax 2 TCPKeepAlive no # Disable dangerous features AllowAgentForwarding no AllowTcpForwarding no X11Forwarding no PermitTunnel no GatewayPorts no PermitUserEnvironment no # Logging LogLevel VERBOSE SyslogFacility AUTH # Restrict users (adjust as needed) AllowUsers vpnuser DenyUsers root # Crypto settings Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512 KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512 # Host key algorithms HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,rsa-sha2-256,rsa-sha2-512 permissions: '0644' runcmd: # Apply sysctl settings - sysctl -p /etc/sysctl.d/99-ip-forward.conf # Auto-detect primary network interface - | PRIMARY_IFACE=$(ip route | awk '/default/ { print $5 ; exit }') sed -i "s/eth0/$PRIMARY_IFACE/g" /etc/iptables/rules.v4 # Load iptables rules - iptables-restore < /etc/iptables/rules.v4 - systemctl enable netfilter-persistent # Create VPN directories (user is created automatically by cloud-init) - mkdir -p /var/lib/vpn/clients /var/lib/vpn/server - chmod 700 /var/lib/vpn /var/lib/vpn/clients /var/lib/vpn/server - chown -R vpnuser:vpnuser /var/lib/vpn # === WireGuard setup === - mkdir -p /etc/wireguard - chmod 700 /etc/wireguard # Generate WireGuard keys with better entropy - | umask 077 wg genkey > /etc/wireguard/server.key wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub wg genkey > /var/lib/vpn/clients/wg-client.key wg pubkey < /var/lib/vpn/clients/wg-client.key > /var/lib/vpn/clients/wg-client.pub chown vpnuser:vpnuser /var/lib/vpn/clients/wg-client.* # Create WireGuard server config - | SERVER_KEY=$(cat /etc/wireguard/server.key) CLIENT_PUB=$(cat /var/lib/vpn/clients/wg-client.pub) cat > /etc/wireguard/wg0.conf <> vars < /var/lib/vpn/client_name.txt - openvpn --genkey secret /etc/openvpn/ta.key - chmod 600 /etc/openvpn/ta.key /etc/openvpn/easy-rsa/pki/private/* # Secure OpenVPN server config - | mkdir -p /var/log/openvpn cat > /etc/openvpn/server.conf <<'EOF' port 1194 proto tcp dev tun ca /etc/openvpn/easy-rsa/pki/ca.crt cert /etc/openvpn/easy-rsa/pki/issued/server.crt key /etc/openvpn/easy-rsa/pki/private/server.key dh /etc/openvpn/easy-rsa/pki/dh.pem # Strong crypto auth SHA512 cipher AES-256-GCM ncp-ciphers AES-256-GCM:AES-128-GCM tls-version-min 1.2 tls-auth /etc/openvpn/ta.key 0 topology subnet server 10.8.0.0 255.255.255.0 # Security options remote-cert-tls client tls-server # Networking push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 9.9.9.9" push "dhcp-option DNS 149.112.112.112" keepalive 10 120 persist-key persist-tun # Privileges user nobody group nogroup # Logging status /var/log/openvpn/status.log log-append /var/log/openvpn/server.log verb 3 mute 20 explicit-exit-notify 1 EOF - systemctl enable openvpn@server - systemctl start openvpn@server # === Secure stunnel setup === - | # Get public IP with fallback PUBLIC_IP=$(curl -s --max-time 10 ifconfig.me || curl -s --max-time 10 icanhazip.com || hostname -I | awk '{print $1}') # Generate stunnel server certificate using OpenVPN CA (not self-signed) cd /etc/openvpn/easy-rsa STUNNEL_SERVER="stunnel-server-$(openssl rand -hex 4)" ./easyrsa gen-req $STUNNEL_SERVER nopass ./easyrsa sign-req server $STUNNEL_SERVER # Create stunnel certificate bundle (cert + key) cat /etc/openvpn/easy-rsa/pki/issued/$STUNNEL_SERVER.crt \ /etc/openvpn/easy-rsa/pki/private/$STUNNEL_SERVER.key > /etc/stunnel/stunnel.pem chmod 600 /etc/stunnel/stunnel.pem # Copy CA for client verification (same CA as OpenVPN) cp /etc/openvpn/easy-rsa/pki/ca.crt /etc/stunnel/ca.pem # Generate client certificate for stunnel mutual TLS STUNNEL_CLIENT="stunnel-client-$(openssl rand -hex 4)" ./easyrsa gen-req $STUNNEL_CLIENT nopass ./easyrsa sign-req client $STUNNEL_CLIENT # Create client certificate bundle for stunnel cat /etc/openvpn/easy-rsa/pki/issued/$STUNNEL_CLIENT.crt \ /etc/openvpn/easy-rsa/pki/private/$STUNNEL_CLIENT.key > /var/lib/vpn/clients/client.pem chmod 600 /var/lib/vpn/clients/client.pem # Use secure stunnel config cp /etc/stunnel/stunnel.conf.template /etc/stunnel/stunnel.conf # Start services with proper ordering - systemctl enable stunnel4 fail2ban - sleep 5 # Allow OpenVPN to fully start - systemctl start stunnel4 - systemctl start fail2ban # Generate client configurations securely - | PUBLIC_IP=$(curl -s --max-time 10 ifconfig.me || curl -s --max-time 10 icanhazip.com || hostname -I | awk '{print $1}') CLIENT_NAME=$(cat /var/lib/vpn/client_name.txt) # WireGuard client config CLIENT_KEY=$(cat /var/lib/vpn/clients/wg-client.key) SERVER_PUB=$(cat /etc/wireguard/server.pub) cat > /var/lib/vpn/clients/wg-client.conf < /var/lib/vpn/clients/client-direct.ovpn < $(cat /etc/openvpn/easy-rsa/pki/ca.crt) $(sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' /etc/openvpn/easy-rsa/pki/issued/$CLIENT_NAME.crt) $(cat /etc/openvpn/easy-rsa/pki/private/$CLIENT_NAME.key) $(cat /etc/openvpn/ta.key) EOF # Secure stunnel client config cat > /var/lib/vpn/clients/stunnel-client.conf < /var/lib/vpn/clients/SECURITY-README.md </dev/null || true chmod 640 /var/log/stunnel4/* 2>/dev/null || true # Remove unnecessary packages apt autoremove -y # Update package database apt update # Status check - sleep 15 - systemctl status wg-quick@wg0 openvpn@server stunnel4 fail2ban --no-pager