From e6ed60548397627bf10f561f9438201dbba0a36e Mon Sep 17 00:00:00 2001 From: Azat Bahawi Date: Sun, 21 Apr 2024 02:15:42 +0300 Subject: 2024-04-21 --- modules/wireguard.nix | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 modules/wireguard.nix (limited to 'modules/wireguard.nix') diff --git a/modules/wireguard.nix b/modules/wireguard.nix new file mode 100644 index 0000000..f645a90 --- /dev/null +++ b/modules/wireguard.nix @@ -0,0 +1,215 @@ +{ + 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; + }; + }) + ]; +} -- cgit v1.2.3