122 lines
3.8 KiB
Nix
122 lines
3.8 KiB
Nix
{ config, pkgs, ... }:
|
|
let
|
|
localNetwork = "10.42.96.0/20";
|
|
vpnServer = "87-1-hu.cg-dialup.net";
|
|
in
|
|
{
|
|
# SOPS secrets for CyberGhost credentials
|
|
sops.secrets.cyberghost-auth = {
|
|
mode = "0400";
|
|
owner = "root";
|
|
};
|
|
sops.secrets.cyberghost-ca = {
|
|
mode = "0400";
|
|
owner = "root";
|
|
};
|
|
sops.secrets.cyberghost-cert = {
|
|
mode = "0400";
|
|
owner = "root";
|
|
};
|
|
sops.secrets.cyberghost-key = {
|
|
mode = "0400";
|
|
owner = "root";
|
|
};
|
|
|
|
environment.systemPackages = [ pkgs.openvpn ];
|
|
|
|
# Enable iproute2 for routing tables
|
|
networking.iproute2.enable = true;
|
|
networking.iproute2.rttablesExtraConfig = ''
|
|
100 vpn
|
|
'';
|
|
|
|
# OpenVPN client service - only establishes tunnel, no routing
|
|
services.openvpn.servers.cyberghost = {
|
|
autoStart = true;
|
|
updateResolvConf = false;
|
|
config = ''
|
|
client
|
|
dev tun
|
|
proto udp
|
|
remote 87-1-hu.cg-dialup.net 443
|
|
resolv-retry infinite
|
|
nobind
|
|
persist-key
|
|
persist-tun
|
|
|
|
# Authentication
|
|
auth-user-pass ${config.sops.secrets.cyberghost-auth.path}
|
|
ca ${config.sops.secrets.cyberghost-ca.path}
|
|
cert ${config.sops.secrets.cyberghost-cert.path}
|
|
key ${config.sops.secrets.cyberghost-key.path}
|
|
|
|
# Security
|
|
data-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC
|
|
data-ciphers-fallback AES-256-CBC
|
|
auth SHA256
|
|
remote-cert-tls server
|
|
script-security 2
|
|
|
|
# Connection
|
|
ping 5
|
|
explicit-exit-notify 2
|
|
|
|
# Don't pull any routes - we manage routing ourselves
|
|
route-nopull
|
|
|
|
verb 4
|
|
'';
|
|
};
|
|
|
|
# Systemd service to set up VPN routing after tunnel is up
|
|
systemd.services.cyberghost-routing = {
|
|
description = "CyberGhost VPN routing rules";
|
|
after = [ "openvpn-cyberghost.service" ];
|
|
requires = [ "openvpn-cyberghost.service" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
ExecStart = pkgs.writeShellScript "vpn-routing-up" ''
|
|
# Wait for tun0 to be available
|
|
for i in $(seq 1 30); do
|
|
if ${pkgs.iproute2}/bin/ip link show tun0 &>/dev/null; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# Add default route via VPN tunnel to table vpn
|
|
${pkgs.iproute2}/bin/ip route add default dev tun0 table vpn 2>/dev/null || true
|
|
|
|
# Policy rules (lower number = higher priority)
|
|
# VPN server traffic must use main table to avoid routing loop
|
|
# Resolve all IPs (hostname has multiple A records for load balancing)
|
|
for VPN_SERVER_IP in $(${pkgs.dig}/bin/dig +short ${vpnServer} A); do
|
|
${pkgs.iproute2}/bin/ip rule add to $VPN_SERVER_IP table main priority 50 2>/dev/null || true
|
|
done
|
|
|
|
# Local network always uses main routing table
|
|
${pkgs.iproute2}/bin/ip rule add to ${localNetwork} table main priority 100 2>/dev/null || true
|
|
${pkgs.iproute2}/bin/ip rule add from ${localNetwork} table main priority 100 2>/dev/null || true
|
|
|
|
# Everything else goes through VPN
|
|
${pkgs.iproute2}/bin/ip rule add table vpn priority 200 2>/dev/null || true
|
|
'';
|
|
ExecStop = pkgs.writeShellScript "vpn-routing-down" ''
|
|
${pkgs.iproute2}/bin/ip rule del table vpn priority 200 2>/dev/null || true
|
|
${pkgs.iproute2}/bin/ip rule del from ${localNetwork} table main priority 100 2>/dev/null || true
|
|
${pkgs.iproute2}/bin/ip rule del to ${localNetwork} table main priority 100 2>/dev/null || true
|
|
|
|
# Clean up all VPN server IP rules
|
|
for VPN_SERVER_IP in $(${pkgs.dig}/bin/dig +short ${vpnServer} A); do
|
|
${pkgs.iproute2}/bin/ip rule del to $VPN_SERVER_IP table main priority 50 2>/dev/null || true
|
|
done
|
|
|
|
${pkgs.iproute2}/bin/ip route del default table vpn 2>/dev/null || true
|
|
'';
|
|
};
|
|
};
|
|
}
|