diff --git a/hosts/nixos/nfss/system.nix b/hosts/nixos/nfss/system.nix index 06c3fc6..ee077d3 100644 --- a/hosts/nixos/nfss/system.nix +++ b/hosts/nixos/nfss/system.nix @@ -6,12 +6,12 @@ ./containers.nix ./proxy.nix ../system-default.nix - ../../../modules/vpn/tailscale.nix ../../../modules/podman.nix ../../../modules/traefik.nix ../../../modules/borg/client.nix ../../../modules/media/server.nix ../../../modules/file-server/samba.nix + ../../../modules/vpn/client.nix ]; # GRUB bootloader with ZFS support @@ -132,9 +132,11 @@ }; }; - services.tailscale-custom = { - exitNode = true; - subnetRoutes = [ "10.1.1.0/24" ]; + services.wireguard-client = { + enable = true; + address = "10.2.2.10/24"; + serverPublicKey = "46QHjSzAas5g9Hll1SCEu9tbR5owCxXAy6wGOUoPwUM="; + serverEndpoint = "91.98.84.215:51820"; }; # Media server services diff --git a/hosts/nixos/vps/proxy.nix b/hosts/nixos/vps/proxy.nix index 1743577..1245a2b 100644 --- a/hosts/nixos/vps/proxy.nix +++ b/hosts/nixos/vps/proxy.nix @@ -57,7 +57,7 @@ loadBalancer = { serversTransport = "longTimeout"; servers = [{ - url = "http://10.1.1.152:8080"; + url = "http://10.2.2.10:8080"; }]; }; }; @@ -65,7 +65,7 @@ music = { loadBalancer = { servers = [{ - url = "http://10.1.1.152:4533"; + url = "http://10.2.2.10:4533"; }]; }; }; diff --git a/hosts/nixos/vps/system.nix b/hosts/nixos/vps/system.nix index 0094da4..a906d41 100644 --- a/hosts/nixos/vps/system.nix +++ b/hosts/nixos/vps/system.nix @@ -6,11 +6,11 @@ ./containers.nix ./proxy.nix ../system-default.nix - ../../../modules/vpn/tailscale.nix ../../../modules/podman.nix ../../../modules/traefik.nix ../../../modules/borg/client.nix ../../../modules/git/server.nix + ../../../modules/vpn/server.nix ]; # GRUB bootloader with UEFI support @@ -44,7 +44,6 @@ firewall = { enable = true; allowedTCPPorts = [ 22 80 443 27017 ]; - trustedInterfaces = [ "tailscale0" ]; }; }; @@ -71,7 +70,14 @@ ]; }; - services.tailscale-custom.exitNode = true; + services.wireguard-server = { + enable = true; + address = "10.2.2.1/24"; + peers = [{ + publicKey = "MCuSF/aFZy7Jq3nI6VpU7jbfZOuEGuMjgpxRWazxtmY="; + allowedIPs = [ "10.2.2.10/32" ]; + }]; + }; services.git-server-custom = { enable = true; diff --git a/modules/vpn/client.nix b/modules/vpn/client.nix new file mode 100644 index 0000000..14cd30f --- /dev/null +++ b/modules/vpn/client.nix @@ -0,0 +1,75 @@ +# NOTE: Private key file at: `/etc/wireguard/private.key` with mode 600 +# Auto-generated on first boot; get public key with: `sudo sh -c 'wg pubkey < /etc/wireguard/private.key'` + +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.wireguard-client; +in + +{ + options.services.wireguard-client = { + enable = mkEnableOption "WireGuard VPN client"; + + address = mkOption { + type = types.str; + example = "10.2.2.2/24"; + }; + + serverPublicKey = mkOption { + type = types.str; + }; + + serverEndpoint = mkOption { + type = types.str; + example = "vpn.example.com:51820"; + }; + + allowedIPs = mkOption { + type = types.listOf types.str; + default = [ "10.2.2.0/24" ]; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.wireguard-tools ]; + + systemd.tmpfiles.rules = [ + "d /etc/wireguard 0700 root root - -" + "f /etc/wireguard/private.key 0600 root root - -" + ]; + + systemd.services.wireguard-keygen = { + description = "Generate WireGuard private key"; + before = [ "wg-quick-wg0.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + if [ ! -s /etc/wireguard/private.key ]; then + ${pkgs.wireguard-tools}/bin/wg genkey > /etc/wireguard/private.key + chmod 600 /etc/wireguard/private.key + echo "Public key: $(${pkgs.wireguard-tools}/bin/wg pubkey < /etc/wireguard/private.key)" + fi + ''; + }; + + networking.wg-quick.interfaces.wg0 = { + privateKeyFile = "/etc/wireguard/private.key"; + address = [ cfg.address ]; + + peers = [{ + publicKey = cfg.serverPublicKey; + allowedIPs = cfg.allowedIPs; + endpoint = cfg.serverEndpoint; + persistentKeepalive = 25; + }]; + }; + + networking.firewall.trustedInterfaces = [ "wg0" ]; + }; +} diff --git a/modules/vpn/server.nix b/modules/vpn/server.nix new file mode 100644 index 0000000..f077dd0 --- /dev/null +++ b/modules/vpn/server.nix @@ -0,0 +1,89 @@ +# NOTE: Private key file at: `/etc/wireguard/private.key` with mode 600 +# Auto-generated on first boot; get public key with: `sudo sh -c 'wg pubkey < /etc/wireguard/private.key'` + +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.wireguard-server; +in + +{ + options.services.wireguard-server = { + enable = mkEnableOption "WireGuard VPN server"; + + address = mkOption { + type = types.str; + example = "10.2.2.1/24"; + }; + + peers = mkOption { + type = types.listOf (types.submodule { + options = { + publicKey = mkOption { type = types.str; }; + allowedIPs = mkOption { type = types.listOf types.str; }; + }; + }); + default = []; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.wireguard-tools ]; + + systemd.tmpfiles.rules = [ + "d /etc/wireguard 0700 root root - -" + "f /etc/wireguard/private.key 0600 root root - -" + ]; + + systemd.services.wireguard-keygen = { + description = "Generate WireGuard private key"; + before = [ "wg-quick-wg0.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + if [ ! -s /etc/wireguard/private.key ]; then + ${pkgs.wireguard-tools}/bin/wg genkey > /etc/wireguard/private.key + chmod 600 /etc/wireguard/private.key + echo "Public key: $(${pkgs.wireguard-tools}/bin/wg pubkey < /etc/wireguard/private.key)" + fi + ''; + }; + + networking.wg-quick.interfaces.wg0 = { + privateKeyFile = "/etc/wireguard/private.key"; + address = [ cfg.address ]; + listenPort = 51820; + + preUp = '' + ${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -A FORWARD -o wg0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.2.2.0/24 -o eth0 -j MASQUERADE + ''; + + postDown = '' + ${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -D FORWARD -o wg0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.2.2.0/24 -o eth0 -j MASQUERADE + ''; + + peers = map (peer: { + inherit (peer) publicKey allowedIPs; + }) cfg.peers; + }; + + networking.firewall = { + allowedUDPPorts = [ 51820 ]; + trustedInterfaces = [ "wg0" ]; + }; + + boot.kernel.sysctl = { + "net.ipv4.ip_forward" = 1; + "net.ipv6.conf.all.forwarding" = 1; + }; + }; +} diff --git a/modules/vpn/wireguard.nix b/modules/vpn/wireguard.nix deleted file mode 100644 index 0951726..0000000 --- a/modules/vpn/wireguard.nix +++ /dev/null @@ -1,203 +0,0 @@ -# NOTE: Private key file at: `/etc/wireguard/private.key` with mode 600 -# Generate with: `wg genkey > /etc/wireguard/private.key` - -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.wireguard-custom; -in - -{ - options.services.wireguard-custom = { - enable = mkEnableOption "WireGuard VPN"; - - mode = mkOption { - type = types.enum [ "server" "client" ]; - description = "Whether to run as server (hub) or client (spoke)"; - }; - - interface = mkOption { - type = types.str; - default = "wg0"; - description = "WireGuard interface name"; - }; - - listenPort = mkOption { - type = types.port; - default = 51820; - description = "UDP port to listen on (server mode only)"; - }; - - privateKeyFile = mkOption { - type = types.str; - default = "/etc/wireguard/private.key"; - description = "Path to private key file"; - }; - - serverConfig = mkOption { - type = types.submodule { - options = { - address = mkOption { - type = types.str; - example = "10.2.2.1/24"; - description = "Server IP address with CIDR"; - }; - - peers = mkOption { - type = types.listOf (types.submodule { - options = { - name = mkOption { - type = types.str; - description = "Peer name for identification"; - }; - - publicKey = mkOption { - type = types.str; - description = "Peer's public key"; - }; - - allowedIPs = mkOption { - type = types.listOf types.str; - description = "IP addresses this peer is allowed to use"; - }; - }; - }); - default = []; - description = "List of client peers"; - }; - }; - }; - description = "Server-specific configuration"; - }; - - clientConfig = mkOption { - type = types.submodule { - options = { - address = mkOption { - type = types.str; - example = "10.2.2.20/24"; - description = "Client IP address with CIDR"; - }; - - serverPublicKey = mkOption { - type = types.str; - description = "Server's public key"; - }; - - serverEndpoint = mkOption { - type = types.str; - example = "vpn.example.com:51820"; - description = "Server endpoint (host:port)"; - }; - - allowedIPs = mkOption { - type = types.listOf types.str; - default = [ "10.2.2.0/24" ]; - description = "IP ranges to route through the tunnel"; - }; - }; - }; - description = "Client-specific configuration"; - }; - }; - - config = mkIf cfg.enable { - # Install WireGuard tools - environment.systemPackages = with pkgs; [ wireguard-tools ]; - - # Create private key file if it doesn't exist - systemd.tmpfiles.rules = [ - "d /etc/wireguard 0700 root root - -" - "f ${cfg.privateKeyFile} 0600 root root - -" - ]; - - # Generate private key on first run - systemd.services.wireguard-keygen = { - description = "Generate WireGuard private key"; - before = [ "wg-quick-${cfg.interface}.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - script = '' - if [ ! -s ${cfg.privateKeyFile} ]; then - echo "Generating WireGuard private key..." - ${pkgs.wireguard-tools}/bin/wg genkey > ${cfg.privateKeyFile} - chmod 600 ${cfg.privateKeyFile} - echo "Private key generated. Public key:" - ${pkgs.wireguard-tools}/bin/wg pubkey < ${cfg.privateKeyFile} - echo "Please add this public key to your peer configurations." - fi - ''; - }; - - # WireGuard interface configuration (combined server and client) - networking.wg-quick.interfaces = { - ${cfg.interface} = mkMerge [ - # Common configuration - { - privateKeyFile = cfg.privateKeyFile; - } - - # Server-specific configuration - (mkIf (cfg.mode == "server") { - address = [ cfg.serverConfig.address ]; - listenPort = cfg.listenPort; - - # Enable IP forwarding and NAT for server - preUp = '' - ${pkgs.iptables}/bin/iptables -A FORWARD -i ${cfg.interface} -j ACCEPT - ${pkgs.iptables}/bin/iptables -A FORWARD -o ${cfg.interface} -j ACCEPT - ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.2.2.0/24 -o eth0 -j MASQUERADE - ''; - - postDown = '' - ${pkgs.iptables}/bin/iptables -D FORWARD -i ${cfg.interface} -j ACCEPT - ${pkgs.iptables}/bin/iptables -D FORWARD -o ${cfg.interface} -j ACCEPT - ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.2.2.0/24 -o eth0 -j MASQUERADE - ''; - - peers = map (peer: { - publicKey = peer.publicKey; - allowedIPs = peer.allowedIPs; - }) cfg.serverConfig.peers; - }) - - # Client-specific configuration - (mkIf (cfg.mode == "client") { - address = [ cfg.clientConfig.address ]; - - peers = [{ - publicKey = cfg.clientConfig.serverPublicKey; - allowedIPs = cfg.clientConfig.allowedIPs; - endpoint = cfg.clientConfig.serverEndpoint; - persistentKeepalive = 25; - }]; - }) - ]; - }; - - # Firewall configuration - networking.firewall = mkMerge [ - # Server firewall rules - (mkIf (cfg.mode == "server") { - allowedUDPPorts = [ cfg.listenPort ]; - trustedInterfaces = [ cfg.interface ]; - }) - - # Client firewall rules - (mkIf (cfg.mode == "client") { - trustedInterfaces = [ cfg.interface ]; - }) - ]; - - # Enable IP forwarding for server - boot.kernel.sysctl = mkIf (cfg.mode == "server") { - "net.ipv4.ip_forward" = 1; - "net.ipv6.conf.all.forwarding" = 1; - }; - }; -}