about summary refs log tree commit diff
path: root/modules/nixos/wireguard.nix
diff options
context:
space:
mode:
Diffstat (limited to 'modules/nixos/wireguard.nix')
-rw-r--r--modules/nixos/wireguard.nix198
1 files changed, 198 insertions, 0 deletions
diff --git a/modules/nixos/wireguard.nix b/modules/nixos/wireguard.nix
new file mode 100644
index 0000000..d05c6ae
--- /dev/null
+++ b/modules/nixos/wireguard.nix
@@ -0,0 +1,198 @@
+{
+  config,
+  inputs,
+  lib,
+  pkgs,
+  this,
+  ...
+}:
+with lib; let
+  cfg = config.nixfiles.modules.wireguard;
+in {
+  options.nixfiles.modules.wireguard = {
+    client = {
+      enable = mkEnableOption "WireGuard client";
+
+      enableTrafficRouting = mkOption {
+        description = "Whether to enable traffic routing through the sever.";
+        type = with types; bool;
+        default = !this.isHeadless;
+      };
+    };
+
+    server = {
+      enable = mkEnableOption "WireGuard server";
+
+      ipv4.address = mkOption {
+        description = "IPv4 address to bind to.";
+        type = with types; str;
+        default = my.configurations.manwe.wireguard.ipv4.address;
+      };
+
+      ipv6.address = mkOption {
+        description = "IPv4 address to bind to.";
+        type = with types; str;
+        default = my.configurations.manwe.wireguard.ipv6.address;
+      };
+
+      address = mkOption {
+        description = "Endpoint address to use";
+        type = with types; str;
+        default = my.configurations.manwe.ipv4.address;
+      };
+
+      port = mkOption {
+        description = "Endpoint port to use.";
+        type = with types; int;
+        default = 6969;
+      };
+
+      publicKey = mkOption {
+        description = "Server's public key.";
+        type = with types; str;
+        default = my.configurations.manwe.wireguard.publicKey;
+      };
+
+      peers = mkOption {
+        description = "List of peers.";
+        type = with types; listOf attrs;
+        default = mapAttrsToList (_: attr:
+          with attr; {
+            inherit (wireguard) publicKey;
+            allowedIPs = with wireguard; [
+              "${ipv4.address}/32"
+              "${ipv6.address}/128"
+            ];
+          }) (filterAttrs (_: attr:
+          attr.hostname != this.hostname && hasAttr "wireguard" attr)
+        my.configurations);
+      };
+    };
+
+    interface = mkOption {
+      description = "Name of the interface to use WireGuard with.";
+      type = with types; str;
+      default = "wg69";
+    };
+
+    ipv4.subnet = mkOption {
+      description = "CIDR notation for the IPv4 subnet to use over WireGuard.";
+      type = with types; str;
+      default = "10.69.0.0/16";
+    };
+
+    ipv6.subnet = mkOption {
+      description = "CIDR notation for the IPv6 subnet to use over WireGuard.";
+      type = with types; str;
+      default = "fd69::/16";
+    };
+  };
+
+  config =
+    {
+      assertions = [
+        {
+          assertion = config.security.sudo.enable;
+          message = "Sudo is not enabled.";
+        }
+        {
+          assertion = any (x: x == "wheel") config.my.extraGroups;
+          message = ''User is not in the "wheel" group.'';
+        }
+      ];
+    }
+    // mkMerge [
+      (mkIf (cfg.client.enable || cfg.server.enable) {
+        secrets."wireguard-private-key-${this.hostname}".file = "${inputs.self}/secrets/wireguard-private-key-${this.hostname}";
+
+        networking.firewall.trustedInterfaces = [cfg.interface];
+      })
+      (mkIf cfg.client.enable {
+        networking.wg-quick.interfaces.${cfg.interface} = mkMerge [
+          (with this.wireguard; {
+            privateKeyFile = config.secrets."wireguard-private-key-${this.hostname}".path;
+            address = ["${ipv4.address}/16" "${ipv6.address}/16"];
+          })
+          (with cfg.server; {
+            peers = [
+              {
+                inherit publicKey;
+                endpoint = "${address}:${toString port}";
+                allowedIPs =
+                  if cfg.client.enableTrafficRouting
+                  then [
+                    "0.0.0.0/0"
+                    "::/0"
+                  ]
+                  else [
+                    cfg.ipv4.subnet
+                    cfg.ipv6.subnet
+                  ];
+                persistentKeepalive = 25;
+              }
+            ];
+            dns = [
+              ipv4.address
+              ipv6.address
+            ]; # This assumes that the host has Unbound running.
+          })
+        ];
+
+        environment.systemPackages = with pkgs; [
+          (writeShellApplication {
+            name = "wg-toggle";
+            runtimeInputs = [iproute2 jq wireguard-tools];
+            text = ''
+              ip46() {
+                sudo ip -4 "$@"
+                sudo ip -6 "$@"
+              }
+
+              fwmark=$(sudo wg show ${cfg.interface} fwmark) || exit
+              if ip -j rule list lookup "$fwmark" | jq -e 'length > 0' >/dev/null; then
+                  ip46 rule del lookup main suppress_prefixlength 0
+                  ip46 rule del lookup "$fwmark"
+              else
+                  ip46 rule add not fwmark "$fwmark" lookup "$fwmark"
+                  ip46 rule add lookup main suppress_prefixlength 0
+              fi
+            '';
+          })
+        ];
+      })
+      (mkIf cfg.server.enable {
+        networking = {
+          wireguard = {
+            enable = true;
+            interfaces.${cfg.interface} = with cfg.server; {
+              privateKeyFile = config.secrets."wireguard-private-key-${this.hostname}".path;
+              ips = ["${ipv4.address}/16" "${ipv6.address}/16"];
+              listenPort = port;
+              inherit peers;
+              allowedIPsAsRoutes = false;
+            };
+          };
+
+          nat = {
+            enable = true;
+            enableIPv6 = true;
+
+            externalInterface = mkDefault "eth0";
+
+            internalInterfaces = [cfg.interface];
+            internalIPs = [cfg.ipv4.subnet];
+            internalIPv6s = [cfg.ipv6.subnet];
+          };
+
+          firewall.allowedUDPPorts = [cfg.server.port];
+        };
+
+        services.prometheus.exporters.wireguard = {
+          enable = true;
+          listenAddress = mkDefault this.wireguard.ipv4.address;
+          withRemoteIp = true;
+          port = 9586;
+        };
+      })
+    ];
+}

Consider giving Nix/NixOS a try! <3