diff --git a/flake.lock b/flake.lock index 71262f5..7d3e473 100644 --- a/flake.lock +++ b/flake.lock @@ -58,6 +58,24 @@ "type": "github" } }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -78,7 +96,7 @@ }, "flake-utils_2": { "inputs": { - "systems": "systems_2" + "systems": "systems_3" }, "locked": { "lastModified": 1726560853, @@ -141,6 +159,27 @@ "type": "github" } }, + "lynpkgs": { + "inputs": { + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_2", + "pkgs-by-name-for-flake-parts": "pkgs-by-name-for-flake-parts", + "systems": "systems_2" + }, + "locked": { + "lastModified": 1732934040, + "narHash": "sha256-3QuAM3OP8SbZyz1bXKajHBRQGW8sMXZ3vJDLV6MQCRg=", + "owner": "lynatic1337", + "repo": "lynpkgs", + "rev": "1bad540c69e36520fcab64b99cf7a1907e2c0f73", + "type": "github" + }, + "original": { + "owner": "lynatic1337", + "repo": "lynpkgs", + "type": "github" + } + }, "microvm": { "inputs": { "flake-utils": "flake-utils_2", @@ -150,11 +189,11 @@ "spectrum": "spectrum" }, "locked": { - "lastModified": 1730499294, - "narHash": "sha256-RxV89z3TwhQT0Wue42aSPh3O7hXGbAFYHHNSnW9h6P8=", + "lastModified": 1732633513, + "narHash": "sha256-6LmtOmeDpv9iHS8l0GNcppP11dKIJFMZLdFyxQ+qQBM=", "owner": "astro", "repo": "microvm.nix", - "rev": "93122446d6001f9789d05e565f73bebfa3f53b50", + "rev": "093ef734d3c37669860043a87dbf1c09fc6f5b38", "type": "github" }, "original": { @@ -179,6 +218,18 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1730504152, + "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + } + }, "nixpkgs-stable": { "locked": { "lastModified": 1710695816, @@ -195,29 +246,13 @@ "type": "github" } }, - "nixpkgs-stable_2": { - "locked": { - "lastModified": 1730602179, - "narHash": "sha256-efgLzQAWSzJuCLiCaQUCDu4NudNlHdg2NzGLX5GYaEY=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3c2f1c4ca372622cb2f9de8016c9a0b1cbd0f37c", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "release-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs-unstable": { "locked": { - "lastModified": 1730785428, - "narHash": "sha256-Zwl8YgTVJTEum+L+0zVAWvXAGbWAuXHax3KzuejaDyo=", + "lastModified": 1732521221, + "narHash": "sha256-2ThgXBUXAE1oFsVATK1ZX9IjPcS4nKFOAjhPNKuiMn0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4aa36568d413aca0ea84a1684d2d46f55dbabad7", + "rev": "4633a7c72337ea8fd23a4f2ba3972865e3ec685d", "type": "github" }, "original": { @@ -229,11 +264,27 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1730963269, - "narHash": "sha256-rz30HrFYCHiWEBCKHMffHbMdWJ35hEkcRVU0h7ms3x0=", + "lastModified": 1732521221, + "narHash": "sha256-2ThgXBUXAE1oFsVATK1ZX9IjPcS4nKFOAjhPNKuiMn0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "83fb6c028368e465cd19bb127b86f971a5e41ebc", + "rev": "4633a7c72337ea8fd23a4f2ba3972865e3ec685d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1732749044, + "narHash": "sha256-T38FQOg0BV5M8FN1712fovzNakSOENEYs+CSkg31C9Y=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0c5b4ecbed5b155b705336aa96d878e55acd8685", "type": "github" }, "original": { @@ -243,13 +294,13 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_4": { "locked": { - "lastModified": 1730272153, - "narHash": "sha256-B5WRZYsRlJgwVHIV6DvidFN7VX7Fg9uuwkRW9Ha8z+w=", + "lastModified": 1731763621, + "narHash": "sha256-ddcX4lQL0X05AYkrkV2LMFgGdRvgap7Ho8kgon3iWZk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53", + "rev": "c69a9bffbecde46b4b939465422ddc59493d3e4d", "type": "github" }, "original": { @@ -259,6 +310,21 @@ "type": "github" } }, + "pkgs-by-name-for-flake-parts": { + "locked": { + "lastModified": 1727519927, + "narHash": "sha256-3SNX6BuaisoX9PKYI+fh3geZ3jBgKKkAtHcWuHRU0+o=", + "owner": "drupol", + "repo": "pkgs-by-name-for-flake-parts", + "rev": "91debb07d81ff25b8e3b48914b6abd6f11dc26e2", + "type": "github" + }, + "original": { + "owner": "drupol", + "repo": "pkgs-by-name-for-flake-parts", + "type": "github" + } + }, "pre-commit-hooks-nix": { "inputs": { "flake-compat": [ @@ -289,8 +355,9 @@ "root": { "inputs": { "lanzaboote": "lanzaboote", + "lynpkgs": "lynpkgs", "microvm": "microvm", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_3", "nixpkgs-unstable": "nixpkgs-unstable", "sops-nix": "sops-nix" } @@ -322,15 +389,14 @@ }, "sops-nix": { "inputs": { - "nixpkgs": "nixpkgs_3", - "nixpkgs-stable": "nixpkgs-stable_2" + "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1731047660, - "narHash": "sha256-iyp51lPWEQz4c5VH9bVbAuBcFP4crETU2QJYh5V0NYA=", + "lastModified": 1732575825, + "narHash": "sha256-xtt95+c7OUMoqZf4OvA/7AemiH3aVuWHQbErYQoPwFk=", "owner": "Mic92", "repo": "sops-nix", - "rev": "60e1bce1999f126e3b16ef45f89f72f0c3f8d16f", + "rev": "3433ea14fbd9e6671d0ff0dd45ed15ee4c156ffa", "type": "github" }, "original": { @@ -384,6 +450,21 @@ "repo": "default", "type": "github" } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 67e7f0d..815801c 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,7 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; sops-nix.url = "github:Mic92/sops-nix"; nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; + lynpkgs.url = "github:lynatic1337/lynpkgs"; }; outputs = { self, @@ -15,6 +16,7 @@ sops-nix, lanzaboote, microvm, + lynpkgs, } @ inputs: let imports = { imports = [ @@ -41,6 +43,13 @@ }; }; inherit (nixpkgs) lib; + + pkgs = import nixpkgs { + overlays = [ + lynpkgs.overlays.default + ]; + }; + mkLocalMods = import ./meta/mkLocalMods.nix {inherit lib;}; mkLocalModsInput = mkLocalMods { prefix = ["lyn"]; diff --git a/hosts/supernova/default.nix b/hosts/supernova/default.nix index 3182ff1..b325566 100644 --- a/hosts/supernova/default.nix +++ b/hosts/supernova/default.nix @@ -26,7 +26,7 @@ with config.lyn.lib; { lyn.services.wgautomesh = { enable = true; - useIPv6 = false; + #useIPv6 = false; }; ##1##3##3##7## diff --git a/hosts/wg-gateway/default.nix b/hosts/wg-gateway/default.nix index 7a7a349..4555be0 100644 --- a/hosts/wg-gateway/default.nix +++ b/hosts/wg-gateway/default.nix @@ -20,7 +20,7 @@ lyn.services.wgautomesh = { enable = true; - useIPv6 = false; + #useIPv6 = false; }; networking.useDHCP = false; diff --git a/meta/wgautomesh.nix b/meta/wgautomesh.nix new file mode 100644 index 0000000..c9ca7b3 --- /dev/null +++ b/meta/wgautomesh.nix @@ -0,0 +1,168 @@ +{ + lib, + config, + pkgs, + ... +}: +with lib; let + cfg = config.services.wgautomesh; + settingsFormat = pkgs.formats.toml {}; + configFile = + # Have to remove nulls manually as TOML generator will not just skip key + # if value is null + settingsFormat.generate "wgautomesh-config.toml" + (filterAttrs (k: v: v != null) + (mapAttrs + (k: v: + if k == "peers" + then map (e: filterAttrs (k: v: v != null) e) v + else v) + cfg.settings)); + runtimeConfigFile = + if cfg.enableGossipEncryption + then "/run/wgautomesh/wgautomesh.toml" + else configFile; +in { + options.services.wgautomesh = { + enable = mkEnableOption "the wgautomesh daemon"; + logLevel = mkOption { + type = types.enum ["trace" "debug" "info" "warn" "error"]; + default = "info"; + description = "wgautomesh log level."; + }; + enableGossipEncryption = mkOption { + type = types.bool; + default = true; + description = "Enable encryption of gossip traffic."; + }; + gossipSecretFile = mkOption { + type = types.path; + description = '' + File containing the gossip secret, a shared secret key to use for gossip + encryption. Required if `enableGossipEncryption` is set. This file + may contain any arbitrary-length utf8 string. To generate a new gossip + secret, use a command such as `openssl rand -base64 32`. + ''; + }; + enablePersistence = mkOption { + type = types.bool; + default = true; + description = "Enable persistence of Wireguard peer info between restarts."; + }; + openFirewall = mkOption { + type = types.bool; + default = true; + description = "Automatically open gossip port in firewall (recommended)."; + }; + settings = mkOption { + type = types.submodule { + freeformType = settingsFormat.type; + options = { + interface = mkOption { + type = types.str; + description = '' + Wireguard interface to manage (it is NOT created by wgautomesh, you + should use another NixOS option to create it such as + `networking.wireguard.interfaces.wg0 = {...};`). + ''; + example = "wg0"; + }; + gossip_port = mkOption { + type = types.port; + description = '' + wgautomesh gossip port, this MUST be the same number on all nodes in + the wgautomesh network. + ''; + default = 1666; + }; + lan_discovery = mkOption { + type = types.bool; + default = true; + description = "Enable discovery of peers on the same LAN using UDP broadcast."; + }; + ipv6 = mkOption { + type = types.bool; + default = true; + description = "Whether to use IPv6 or IPv4."; + }; + upnp_forward_external_port = mkOption { + type = types.nullOr types.port; + default = null; + description = '' + Public port number to try to redirect to this machine's Wireguard + daemon using UPnP IGD. + ''; + }; + peers = mkOption { + type = types.listOf (types.submodule { + options = { + pubkey = mkOption { + type = types.str; + description = "Wireguard public key of this peer."; + }; + address = mkOption { + type = types.str; + description = '' + Wireguard address of this peer (a single IP address, multiple + addresses or address ranges are not supported). + ''; + example = "10.0.0.42"; + }; + endpoint = mkOption { + type = types.nullOr types.str; + description = '' + Bootstrap endpoint for connecting to this Wireguard peer if no + other address is known or none are working. + ''; + default = null; + example = "wgnode.mydomain.example:51820"; + }; + }; + }); + default = []; + description = "wgautomesh peer list."; + }; + }; + }; + default = {}; + description = "Configuration for wgautomesh."; + }; + }; + + config = mkIf cfg.enable { + services.wgautomesh.settings = { + gossip_secret_file = mkIf cfg.enableGossipEncryption "$CREDENTIALS_DIRECTORY/gossip_secret"; + persist_file = mkIf cfg.enablePersistence "/var/lib/wgautomesh/state"; + }; + + systemd.services.wgautomesh = { + path = [pkgs.wireguard-tools]; + environment = {RUST_LOG = "wgautomesh=${cfg.logLevel}";}; + description = "wgautomesh"; + serviceConfig = { + Type = "simple"; + + ExecStart = "${getExe pkgs.wgautomesh} ${runtimeConfigFile}"; + Restart = "always"; + RestartSec = "30"; + LoadCredential = mkIf cfg.enableGossipEncryption ["gossip_secret:${cfg.gossipSecretFile}"]; + + ExecStartPre = mkIf cfg.enableGossipEncryption [ + '' ${pkgs.envsubst}/bin/envsubst \ + -i ${configFile} \ + -o ${runtimeConfigFile}'' + ]; + + DynamicUser = true; + StateDirectory = "wgautomesh"; + StateDirectoryMode = "0700"; + RuntimeDirectory = "wgautomesh"; + AmbientCapabilities = "CAP_NET_ADMIN"; + CapabilityBoundingSet = "CAP_NET_ADMIN"; + }; + wantedBy = ["multi-user.target"]; + }; + networking.firewall.allowedUDPPorts = + mkIf cfg.openFirewall [cfg.settings.gossip_port]; + }; +} diff --git a/modules/services/wgautomesh.nix b/modules/services/wgautomesh.nix index 0551ccc..5ce5a85 100644 --- a/modules/services/wgautomesh.nix +++ b/modules/services/wgautomesh.nix @@ -5,6 +5,13 @@ cfg, ... }: let + buildInputs = [pkgs.wgautomesh]; + options.services.wgautomesh.settings.ipv6 = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to use IPv6 or IPv4."; + }; + prefix = "lyn"; # decrypt gossip secret @@ -20,11 +27,15 @@ in lib.mapAttrsToList (name: host: { pubkey = host.wg.pubkey; - #if there is no public IP, make endpoint null so wgautomesh knows it unknown + #if there is no public IP, make endpoint null so wgautomesh knows it unknown. Else format it to a SocketAddr endpoint = if host.${version}.public == "" then null - else "${host.${version}.public}:${toString host.wg.port}"; + else "${ + if version == "IPv6" + then "[${host.${version}.public}]" + else host.${version}.public + }:${toString host.wg.port}"; address = host.${version}.internal; }) filteredHosts; @@ -34,24 +45,30 @@ currentHost = meshnetwork.hosts.${config.networking.hostName}; wireguardPort = currentHost.wg.port; in { - opt.useIPv6 = lib.mkOption { - type = lib.types.bool; - description = "Whether to use IPv6. Defaults to true"; - default = true; + opt = { + useIPv6 = lib.mkOption { + type = lib.types.bool; + description = "Whether to use IPv6. Defaults to true"; + default = true; + }; + enable_upnp = lib.mkOption { + type = lib.types.bool; + description = "Whether to allow the wireguard port in the gateway using UPnP IGD. Necessary on some firewalls, might spam unnecessary debug messages on environments without IGD gateways."; + default = false; + }; }; config = { networking.firewall = { - allowedUDPPorts = [ - wireguardPort - ]; - extraCommands = '' - # Allow UDP packets comming from port 1900 from a local address, - # these are necessary for UPnP/IGD - iptables -A INPUT -s 192.168.0.0/16 -p udp --sport 1900 -j ACCEPT - ''; - extraStopCommands = '' - iptables -D INPUT -s 192.168.0.0/16 -p udp --sport 1900 -j ACCEPT - ''; + allowedUDPPorts = + [ + wireguardPort + ] + # UPnP broadcast responses + ++ ( + if cfg.enable_upnp + then [1900] + else [] + ); }; networking.wireguard.interfaces.wg0 = { @@ -61,7 +78,7 @@ in { else ["${currentHost.IPv4.internal}/24"]; listenPort = wireguardPort; privateKeyFile = "/var/lib/wireguard-keys/private"; - mtu = 1200; + mtu = 1280; }; services.wgautomesh = {