A comprehensive NixOS module for managing multiple VPN connections with support for WireGuard and OpenVPN.
- Multiple VPN Types: WireGuard and OpenVPN support
- Multiple Connections: Manage multiple VPNs simultaneously
- Auto-start: Optionally start VPNs on boot
- DNS Management: Automatic DNS configuration per VPN
- Firewall Integration: Per-VPN firewall rules
- Kill Switch: Prevent traffic leaks when VPN is down
- Flexible Routing: Route all traffic or specific subnets
- Declarative Configuration: Full NixOS-style declarative config
- Copy
vpn-module.nixto your NixOS configuration directory - Import the module in your
configuration.nix:
imports = [
./vpn-module.nix
];services.vpn-manager = {
enable = true;
connections.my-vpn = {
enable = true;
type = "wireguard";
autoStart = true;
wireguard = {
privateKeyFile = "/root/wg-private";
address = [ "10.0.0.2/24" ];
dns = [ "1.1.1.1" ];
peers = [{
publicKey = "SERVER_PUBLIC_KEY";
allowedIPs = [ "0.0.0.0/0" ];
endpoint = "vpn.example.com:51820";
persistentKeepalive = 25;
}];
};
};
};services.vpn-manager = {
enable = true;
connections.work = {
enable = true;
type = "openvpn";
autoStart = false;
openvpn = {
config = ''
client
dev tun
proto udp
remote vpn.company.com 1194
ca /path/to/ca.crt
cert /path/to/client.crt
key /path/to/client.key
'';
updateResolvConf = true;
};
};
};services.vpn-manager.enable: Enable the VPN managerservices.vpn-manager.connections.<name>: Define VPN connections
enable: Enable this VPN connection (default:true)type: VPN type -"wireguard"or"openvpn"autoStart: Start VPN automatically on boot (default:false)
wireguard.privateKeyFile: Path to private key filewireguard.address: List of IP addresses for the interfacewireguard.listenPort: Port to listen on (optional)wireguard.dns: DNS servers to usewireguard.mtu: MTU for the interface (optional)wireguard.peers: List of peer configurationspublicKey: Peer's public keyallowedIPs: IP ranges to route through this peerendpoint: Peer's address and portpersistentKeepalive: Keepalive interval in secondspresharedKeyFile: Path to preshared key (optional)
wireguard.postSetup: Commands to run after setupwireguard.postShutdown: Commands to run after shutdown
openvpn.config: OpenVPN configuration contentopenvpn.authUserPass: Path to auth file (optional)openvpn.updateResolvConf: Update DNS resolvers (default:true)openvpn.extraConfig: Additional configuration lines
firewall.enable: Enable firewall rules for this VPNfirewall.allowedTCPPorts: TCP ports to allowfirewall.allowedUDPPorts: UDP ports to allowfirewall.killSwitch: Block non-VPN traffic (default:false)
routing.table: Custom routing table numberrouting.routeAll: Route all traffic through VPN (default:false)routing.routes: Additional routes to add
# Generate keys
wg genkey | sudo tee /root/wg-private | wg pubkey | sudo tee /root/wg-public
# Secure the private key
sudo chmod 600 /root/wg-private# Start a WireGuard VPN
sudo systemctl start wireguard-my-vpn
# Stop a WireGuard VPN
sudo systemctl stop wireguard-my-vpn
# Check status
sudo systemctl status wireguard-my-vpn
# Start an OpenVPN connection
sudo systemctl start openvpn-work
# View logs
journalctl -u wireguard-my-vpn -f
journalctl -u openvpn-work -f# WireGuard
sudo wg show
# Check routing
ip route show
# Check DNS
cat /etc/resolv.confRoute only specific subnets through VPN:
connections.split-tunnel = {
enable = true;
type = "wireguard";
wireguard = {
privateKeyFile = "/root/wg-private";
address = [ "10.0.0.2/24" ];
peers = [{
publicKey = "SERVER_PUBLIC_KEY";
# Only route these subnets through VPN
allowedIPs = [ "192.168.1.0/24" "10.0.0.0/8" ];
endpoint = "vpn.example.com:51820";
}];
};
routing = {
routeAll = false;
routes = [ "192.168.1.0/24" ];
};
};Prevent traffic leaks if VPN disconnects:
connections.secure = {
enable = true;
type = "wireguard";
wireguard = {
# ... config ...
};
firewall = {
enable = true;
killSwitch = true;
# Only allow essential services
allowedTCPPorts = [ 22 ];
};
};services.vpn-manager = {
enable = true;
connections = {
work = {
enable = true;
type = "wireguard";
# Work VPN config
};
personal = {
enable = true;
type = "openvpn";
# Personal VPN config
};
site-to-site = {
enable = true;
type = "wireguard";
# Site-to-site config
};
};
};wireguard = {
# ... other config ...
postSetup = ''
# Add custom routes
ip route add 192.168.50.0/24 via 10.0.0.1
# Start monitoring
echo "VPN connected at $(date)" >> /var/log/vpn-events.log
# Send notification
${pkgs.libnotify}/bin/notify-send "VPN Connected"
'';
postShutdown = ''
echo "VPN disconnected at $(date)" >> /var/log/vpn-events.log
${pkgs.libnotify}/bin/notify-send "VPN Disconnected"
'';
};-
Private Keys: Store private keys outside of the Nix store
sudo mkdir -p /root/wireguard-keys sudo chmod 700 /root/wireguard-keys
-
File Permissions: Ensure key files have proper permissions
sudo chmod 600 /root/wireguard-keys/private
-
Kill Switch: Enable kill switch for sensitive connections to prevent leaks
-
DNS Leaks: Configure DNS servers in the VPN config to prevent DNS leaks
# Check system logs
journalctl -xe
# Check specific service
journalctl -u wireguard-my-vpn -n 50
# Verify configuration
sudo wg show my-vpn# Check routing table
ip route show
# Check DNS
cat /etc/resolv.conf
# Test with specific interface
ping -I my-vpn 1.1.1.1# Check DNS configuration
resolvectl status
# Manually test DNS
nslookup google.com
# Restart DNS resolution
sudo systemctl restart systemd-resolvedThe module works seamlessly with systemd-networkd for advanced routing scenarios.
The module integrates with NixOS's firewall configuration. Additional rules can be added:
networking.firewall.extraCommands = ''
# Custom iptables rules
iptables -A INPUT -i my-vpn -j ACCEPT
'';Feel free to submit issues or pull requests for improvements.
MIT
Built for NixOS with love for declarative infrastructure.