summaryrefslogtreecommitdiff
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;
+ };
+ })
+ ];
+}