From 01a77e65a6542c0e617f1c66a172c7a2f8fb5618 Mon Sep 17 00:00:00 2001 From: Konarak Date: Tue, 30 Sep 2025 21:10:33 +0530 Subject: [PATCH] update overall module structure, remove host-specific config files - update flake.nix to reflect new structure - update modules/default.nix imports - update modules/network.nix and wireguard.nix - remove config/configuration.nix and hardware-configuration.nix - remove modules/access.nix from this module's scope --- config/configuration.nix | 45 ------ config/hardware-configuration.nix | 41 ------ flake.nix | 23 +-- modules/access.nix | 44 ------ modules/default.nix | 2 +- modules/network.nix | 225 +++++++++++++++++++++++------- modules/wireguard.nix | 51 +++---- 7 files changed, 204 insertions(+), 227 deletions(-) delete mode 100644 config/configuration.nix delete mode 100644 config/hardware-configuration.nix delete mode 100644 modules/access.nix diff --git a/config/configuration.nix b/config/configuration.nix deleted file mode 100644 index 2ef02af..0000000 --- a/config/configuration.nix +++ /dev/null @@ -1,45 +0,0 @@ -{ config, lib, pkgs, ... }: - -{ - sops.defaultSopsFile = ../secrets/secrets.yaml; - - imports = [ - # Include the results of the hardware scan. - ./hardware-configuration.nix - - ../modules/sixnix - ]; - - # Use the GRUB 2 boot loader. - boot.loader.grub.enable = true; - - # Enable the Flakes feature and the accompanying new nix command-line tool - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - - environment.systemPackages = with pkgs; [ git curl btop emacs-nox ]; - - # Copy the NixOS configuration file and link it from the resulting system - # (/run/current-system/configuration.nix). This is useful in case you - # accidentally delete configuration.nix. - # system.copySystemConfiguration = true; - - # This option defines the first version of NixOS you have installed on this particular machine, - # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. - # - # Most users should NEVER change this value after the initial install, for any reason, - # even if you've upgraded your system to a new NixOS release. - # - # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, - # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how - # to actually do that. - # - # This value being lower than the current NixOS release does NOT mean your system is - # out of date, out of support, or vulnerable. - # - # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration, - # and migrated your data accordingly. - # - # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . - system.stateVersion = "25.05"; # Did you read the comment? - -} diff --git a/config/hardware-configuration.nix b/config/hardware-configuration.nix deleted file mode 100644 index a590721..0000000 --- a/config/hardware-configuration.nix +++ /dev/null @@ -1,41 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; - - boot.initrd.availableKernelModules = - [ "virtio_pci" "virtio_scsi" "ahci" "sd_mod" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "wireguard" ]; - boot.extraModulePackages = [ ]; - - fileSystems."/" = { - device = "/dev/sda"; - fsType = "ext4"; - }; - - swapDevices = [ ]; - - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - # networking.useDHCP = lib.mkDefault true; - # networking.interfaces.enp0s4.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - - boot.kernelParams = [ "console=ttyS0,19200n8" ]; - boot.loader.grub.extraConfig = '' - serial --speed=19200 --unit=0 --word=8 --parity=no --stop=1; - terminal_input serial; - terminal_output serial; - ''; - boot.loader.grub.forceInstall = true; - boot.loader.grub.device = "nodev"; - boot.loader.timeout = 10; - -} diff --git a/flake.nix b/flake.nix index 39c4bb3..1ed1cf3 100644 --- a/flake.nix +++ b/flake.nix @@ -1,25 +1,12 @@ { - description = "Server Configuration for WireGuard provider"; + description = "Simple WireGuard tunnelbroker, with optional failover using BGP"; inputs = { - nixpkgs.url = "nixpkgs/nixos-25.05"; - nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; - - sops-nix.url = "github:Mic92/sops-nix"; - sops-nix.inputs.nixpkgs.follows = "nixpkgs"; - + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; }; - outputs = { self, nixpkgs, nixpkgs-unstable, sops-nix, }@attrs: { - nixosConfigurations = { - "tunnels" = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - specialArgs = attrs; - modules = [ - (import ./config/maa1-ops/configuration.nix) - sops-nix.nixosModules.sops - ]; - }; - }; + outputs = { self, nixpkgs }: { + nixosModules.default = import ./modules; + nixosModules.sixnix = import ./modules; }; } diff --git a/modules/access.nix b/modules/access.nix deleted file mode 100644 index 00d9f1b..0000000 --- a/modules/access.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ config, pkgs, ... }: - -let - keys = [ - ]; - - root.password = config.sops.secrets.password-root.path; -in { - - sops.secrets = let def = { neededForUsers = true; }; - in { - "password-root" = def; - }; - - services.openssh = { - enable = true; - settings.PermitRootLogin = "no"; - }; - - # protect from ssh spammers - services.sshguard.enable = true; - - # disable kernel from logging REFUSED CONNECTIONS messages when we actually drop this traffic - networking.firewall.logRefusedConnections = false; - - # enable mosh and open firewall ports - programs.mosh.enable = true; - - security.sudo.wheelNeedsPassword = false; - - users.mutableUsers = false; - - users.users.root = { - hashedPasswordFile = root.password; - openssh.authorizedKeys.keys = keys; - }; - - # users.users. = { - # isNormalUser = true; - # extraGroups = [ "wheel" ]; - # hashedPasswordFile = ; - # openssh.authorizedKeys.keys = ; - # }; -} diff --git a/modules/default.nix b/modules/default.nix index deb780f..dd54ff5 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1 +1 @@ -{ config, pkgs, ... }: { imports = [ ./access.nix ./network.nix ]; } +{ config, pkgs, ... }: { imports = [ ./network.nix ./wireguard.nix ]; } diff --git a/modules/network.nix b/modules/network.nix index 10cc4d9..abfcc20 100644 --- a/modules/network.nix +++ b/modules/network.nix @@ -1,63 +1,188 @@ -{ config, pkgs, ... }: -let - hostname = "tunnels"; - domain = "example.com"; +{ config, pkgs, lib, ... }: - resolvers = [ "9.9.9.9" "2620:fe::fe" ]; +with lib; - egress = { - interface = "eth0"; - - ipv4.gateway = "198.51.100.10"; - ipv4.address = "198.51.100.1/24"; - - ipv6.gateway = "fe80::1"; - ipv6.address = "2001:DB8:10:cafe:dcaf::3210/64"; - }; - - tunnels = [{ - interface = "wg0"; - serverPort = 51820; - serverAddress = "2001:db8:de:ff0::1/128"; - clientAddress = "2001:db8:de:ff0::2/128"; - clientSubnet = "2001:db8:de:f00::/60"; - serverPrivateKeyFile = "wg0-server-private"; - clientPublicKeyFile = "wg0-client-public"; - }]; +let cfg = config.sixnix; in { + options.sixnix = { + enable = mkEnableOption "sixnix networking configuration"; - networking.hostName = hostname; - networking.domain = domain; - - # Enable systemd-networkd - systemd.network.enable = true; - networking.useNetworkd = true; - - # Tools for monitoring/key generation - environment.systemPackages = with pkgs; [ wireguard-tools tcpdump ]; - - # Configure Ethernet interface (eth0) - systemd.network.networks."10-egress" = { - matchConfig.Name = egress.interface; - networkConfig = { - DHCP = "no"; - IPv6AcceptRA = "no"; - IPv6PrivacyExtensions = "yes"; - IPv6Forwarding = "yes"; + hostname = mkOption { + type = types.str; + description = "Server hostname"; }; - address = [ egress.ipv4.address egress.ipv6.address ]; - gateway = [ egress.ipv4.gateway egress.ipv6.gateway ]; + domain = mkOption { + type = types.str; + default = "notwork.in"; + description = "Domain name"; + }; - dns = resolvers; + interface = mkOption { + type = types.str; + default = "enp0s3"; + description = "Network interface name"; + }; + + dns = mkOption { + type = types.listOf types.str; + default = [ "9.9.9.9" "2620:fe::fe" ]; + description = "DNS resolvers for systemd-networkd"; + }; + + ipv4 = { + address = mkOption { + type = types.nullOr types.str; + default = null; + description = "IPv4 address with CIDR (documentation only - using DHCP)"; + }; + + gateway = mkOption { + type = types.nullOr types.str; + default = null; + description = "IPv4 gateway (documentation only - using DHCP)"; + }; + }; + + ipv6 = { + address = mkOption { + type = types.nullOr types.str; + default = null; + description = "IPv6 address with CIDR (documentation only - using SLAAC)"; + }; + + gateway = mkOption { + type = types.str; + default = "fe80::1"; + description = "IPv6 gateway (documentation only - using SLAAC)"; + }; + }; + + bgp = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable BGP/FRR for shared IP failover"; + }; + + peers = mkOption { + type = types.listOf types.str; + default = []; + example = [ "2001:db8::1" "2001:db8::2" "2001:db8::3" "2001:db8::4" ]; + description = "List of BGP peer addresses (e.g., Linode route servers)"; + }; + + advertisedSubnets = mkOption { + type = types.listOf types.str; + default = []; + example = [ "2001:db8:1234::/56" ]; + description = "List of IPv6 subnets to advertise via BGP"; + }; + + routeMap = mkOption { + type = types.enum [ "primary" "secondary" ]; + description = "BGP route map type (primary/secondary for HA)"; + }; + }; + + wireguard = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable WireGuard configuration"; + }; + }; }; - boot.kernel.sysctl = { "net.ipv6.conf.all.forwarding" = 1; }; - imports = [ ./wireguard.nix ]; - wireguard.interfaces = tunnels; + config = mkIf cfg.enable { + networking.hostName = cfg.hostname; + networking.domain = cfg.domain; + networking.nftables.enable = true; - networking.firewall = { allowedUDPPorts = map (x: x.serverPort) tunnels; }; + # Enable systemd-networkd + systemd.network.enable = true; + networking.useNetworkd = true; -} + # Tools for monitoring/key generation + environment.systemPackages = with pkgs; [ wireguard-tools tcpdump ]; + + # Configure network interface - pure SLAAC/DHCP + systemd.network.networks."10-egress" = { + matchConfig.Name = cfg.interface; + networkConfig = { + DHCP = "yes"; # Enable both IPv4 and IPv6 DHCP + IPv6AcceptRA = "yes"; + IPv6PrivacyExtensions = "no"; + IPv6Forwarding = "yes"; + }; + + dns = cfg.dns; + }; + + # Add blackhole routes for advertised subnets on loopback + systemd.network.networks."10-loopback" = mkIf cfg.bgp.enable { + matchConfig.Name = "lo"; + routes = map (subnet: { + Destination = subnet; + Type = "blackhole"; + }) cfg.bgp.advertisedSubnets; + }; + + # Create dummy interface for shared WireGuard endpoint IP (HA setup) + systemd.network.netdevs."20-shared-dummy" = mkIf cfg.bgp.enable { + netdevConfig = { + Kind = "dummy"; + Name = "shared0"; + }; + }; + + systemd.network.networks."20-shared-dummy" = mkIf cfg.bgp.enable { + matchConfig.Name = "shared0"; + address = [ "2600:3c08:e002:6cff::1/128" ]; + }; + + boot.kernel.sysctl = { + "net.ipv6.conf.all.forwarding" = 1; + "net.ipv6.conf.${cfg.interface}.accept_ra" = 2; + # Accept prefix info + "net.ipv6.conf.${cfg.interface}.accept_ra_pinfo" = 1; + # Accept default router + "net.ipv6.conf.${cfg.interface}.accept_ra_defrtr" = 1; + }; + + # BGP configuration + # Reference: https://techdocs.akamai.com/cloud-computing/docs/configuring-ip-failover-over-bgp-using-frr-advanced + services.frr.bgpd.enable = cfg.bgp.enable; + services.frr.config = mkIf cfg.bgp.enable (let + peerConfig = concatMapStringsSep "\n" (peer: " neighbor ${peer} peer-group RS") cfg.bgp.peers; + networkConfig = concatMapStringsSep "\n" (subnet: " network ${subnet} route-map ${cfg.bgp.routeMap}") cfg.bgp.advertisedSubnets; + in '' + hostname ${cfg.hostname} + + router bgp 65001 + no bgp ebgp-requires-policy + coalesce-time 1000 + bgp bestpath as-path multipath-relax + neighbor RS peer-group + neighbor RS remote-as external + neighbor RS ebgp-multihop 10 + neighbor RS capability extended-nexthop + ${peerConfig} + + address-family ipv6 unicast + ${networkConfig} + redistribute static + neighbor RS activate + exit-address-family + + route-map primary permit 10 + set community 65000:1 + route-map secondary permit 10 + set community 65000:2 + + ipv6 nht resolve-via-default + ''); + }; +} \ No newline at end of file diff --git a/modules/wireguard.nix b/modules/wireguard.nix index a742d84..9d548dc 100644 --- a/modules/wireguard.nix +++ b/modules/wireguard.nix @@ -12,7 +12,7 @@ let serverAddress = mkOption { type = types.str; description = - "IPv6 address with CIDR prefix to bind WireGuard (e.g., 2001:db8::1/64)."; + "IPv6 address/prefix to bind WireGuard (e.g., 2001:db8::1/64)."; }; serverPort = mkOption { type = types.port; @@ -20,19 +20,19 @@ let }; clientAddress = mkOption { type = types.str; - description = "Peer IPv6 address with CIDR (e.g., 2001:db8::2/128)."; + description = "Peer IPv6 address/prefix (e.g., 2001:db8::2/128)."; }; clientSubnet = mkOption { type = types.str; - description = "Peer subnet IPv6 CIDR (e.g., 2001:db8::/64)."; + description = "Peer IPv6 subnet (e.g., 2001:db8::/64)."; }; serverPrivateKeyFile = mkOption { - type = types.str; - description = "Name of the sops secret containing server private key."; + type = types.path; + description = "Path to server private key file."; }; clientPublicKeyFile = mkOption { - type = types.str; - description = "Name of the sops secret containing client public key."; + type = types.path; + description = "Path to client public key file."; }; }; }); @@ -47,14 +47,14 @@ let "Duplicate values found for '${name}': ${builtins.toString values}"; }; - secretAssertions = lib.flatten (map (cfg: [ + fileAssertions = lib.flatten (map (cfg: [ { - assertion = builtins.hasAttr cfg.serverPrivateKeyFile config.sops.secrets; - message = "Missing sops secret: ${cfg.serverPrivateKeyFile}"; + assertion = cfg.serverPrivateKeyFile != null; + message = "serverPrivateKeyFile must be provided for interface ${cfg.interface}"; } { - assertion = builtins.hasAttr cfg.clientPublicKeyFile config.sops.secrets; - message = "Missing sops secret: ${cfg.clientPublicKeyFile}"; + assertion = cfg.clientPublicKeyFile != null; + message = "clientPublicKeyFile must be provided for interface ${cfg.interface}"; } ]) interfaces); @@ -69,34 +69,28 @@ let in { options.wireguard.interfaces = mkOption { type = wgConfigType; + default = []; description = "List of WireGuard interface configurations."; }; config = { - sops.secrets = let - def = { - owner = "systemd-network"; - group = "systemd-network"; - }; - in lib.mkMerge (map (cfg: { - "${cfg.serverPrivateKeyFile}" = def; - "${cfg.clientPublicKeyFile}" = def; - }) interfaces); + assertions = lib.mkAfter (fileAssertions ++ uniquenessAssertions); - assertions = lib.mkAfter (secretAssertions ++ uniquenessAssertions); + systemd.network.enable = true; systemd.network.netdevs = lib.mkMerge (map (cfg: { "${cfg.interface}" = { netdevConfig = { Kind = "wireguard"; + MTUBytes = "1412"; Name = cfg.interface; }; wireguardConfig = { - PrivateKeyFile = config.sops.secrets.${cfg.serverPrivateKeyFile}.path; + PrivateKeyFile = cfg.serverPrivateKeyFile; ListenPort = cfg.serverPort; }; wireguardPeers = [{ - PublicKeyFile = config.sops.secrets.${cfg.clientPublicKeyFile}.path; + PublicKeyFile = cfg.clientPublicKeyFile; AllowedIPs = [ cfg.clientAddress cfg.clientSubnet ]; }]; }; @@ -105,13 +99,11 @@ in { systemd.network.networks = lib.mkMerge (map (cfg: { "${cfg.interface}" = { matchConfig = { Name = cfg.interface; }; + networkConfig = { IPv6Forwarding = "yes"; }; + address = [ cfg.serverAddress ]; routes = [ - { - Destination = cfg.clientAddress; - Scope = "link"; - } { Destination = cfg.clientSubnet; Scope = "link"; @@ -119,5 +111,8 @@ in { ]; }; }) interfaces); + + # Automatically open firewall ports for WireGuard + networking.firewall.allowedUDPPorts = map (cfg: cfg.serverPort) interfaces; }; }