diff options
Diffstat (limited to 'modules/nixos/wireguard.nix')
-rw-r--r-- | modules/nixos/wireguard.nix | 198 |
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; + }; + }) + ]; +} |