diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 7c5b83464242..bd8cc3688e41 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -9245,6 +9245,13 @@ githubId = 2057309; name = "Sergey Sofeychuk"; }; + lx = { + email = "alex@adnab.me"; + github = "Alexis211"; + githubId = 101484; + matrix = "@lx:deuxfleurs.fr"; + name = "Alex Auvolat"; + }; lxea = { email = "nix@amk.ie"; github = "lxea"; diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index 6b0147571847..10224db34288 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -107,6 +107,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [trurl](https://github.com/curl/trurl), a command line tool for URL parsing and manipulation. +- [wgautomesh](https://git.deuxfleurs.fr/Deuxfleurs/wgautomesh), a simple utility to help connect wireguard nodes together in a full mesh topology. Available as [services.wgautomesh](options.html#opt-services.wgautomesh.enable). + - [woodpecker-agents](https://woodpecker-ci.org/), a simple CI engine with great extensibility. Available as [services.woodpecker-agents](#opt-services.woodpecker-agents.agents._name_.enable). - [woodpecker-server](https://woodpecker-ci.org/), a simple CI engine with great extensibility. Available as [services.woodpecker-server](#opt-services.woodpecker-server.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b6ac23c10d06..f1c459f75570 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1044,6 +1044,7 @@ ./services/networking/wg-netmanager.nix ./services/networking/webhook.nix ./services/networking/wg-quick.nix + ./services/networking/wgautomesh.nix ./services/networking/wireguard.nix ./services/networking/wpa_supplicant.nix ./services/networking/wstunnel.nix diff --git a/nixos/modules/services/networking/wgautomesh.nix b/nixos/modules/services/networking/wgautomesh.nix new file mode 100644 index 000000000000..93227a9b625d --- /dev/null +++ b/nixos/modules/services/networking/wgautomesh.nix @@ -0,0 +1,161 @@ +{ 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 (mdDoc "the wgautomesh daemon"); + logLevel = mkOption { + type = types.enum [ "trace" "debug" "info" "warn" "error" ]; + default = "info"; + description = mdDoc "wgautomesh log level."; + }; + enableGossipEncryption = mkOption { + type = types.bool; + default = true; + description = mdDoc "Enable encryption of gossip traffic."; + }; + gossipSecretFile = mkOption { + type = types.path; + description = mdDoc '' + File containing the shared secret key to use for gossip encryption. + Required if `enableGossipEncryption` is set. + ''; + }; + enablePersistence = mkOption { + type = types.bool; + default = true; + description = mdDoc "Enable persistence of Wireguard peer info between restarts."; + }; + openFirewall = mkOption { + type = types.bool; + default = true; + description = mdDoc "Automatically open gossip port in firewall (recommended)."; + }; + settings = mkOption { + type = types.submodule { + freeformType = settingsFormat.type; + options = { + + interface = mkOption { + type = types.str; + description = mdDoc '' + 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 = mdDoc '' + 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 = mdDoc "Enable discovery of peers on the same LAN using UDP broadcast."; + }; + upnp_forward_external_port = mkOption { + type = types.nullOr types.port; + default = null; + description = mdDoc '' + 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 = mdDoc "Wireguard public key of this peer."; + }; + address = mkOption { + type = types.str; + description = mdDoc '' + Wireguard address of this peer (a single IP address, multliple + addresses or address ranges are not supported). + ''; + example = "10.0.0.42"; + }; + endpoint = mkOption { + type = types.nullOr types.str; + description = mdDoc '' + 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 = mdDoc "wgautomesh peer list."; + }; + }; + + }; + default = { }; + description = mdDoc "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/pkgs/tools/networking/wgautomesh/default.nix b/pkgs/tools/networking/wgautomesh/default.nix new file mode 100644 index 000000000000..823987888a31 --- /dev/null +++ b/pkgs/tools/networking/wgautomesh/default.nix @@ -0,0 +1,25 @@ +{ lib +, fetchFromGitea +, rustPlatform +}: +rustPlatform.buildRustPackage rec { + pname = "wgautomesh"; + version = "0.1.0"; + + src = fetchFromGitea { + domain = "git.deuxfleurs.fr"; + owner = "Deuxfleurs"; + repo = "wgautomesh"; + rev = "v${version}"; + sha256 = "FiFEpYLSJg52EtBXaZ685ICbaIyY9URrDt0bS0HPi0Q="; + }; + + cargoHash = "sha256-DGDVjQ4fr4/F1RE0qVc5CWcXrrCEswCF7rQQwlKzMPA="; + + meta = with lib; { + description = "A simple utility to help connect wireguard nodes together in a full mesh topology"; + homepage = "https://git.deuxfleurs.fr/Deuxfleurs/wgautomesh"; + license = licenses.agpl3Only; + maintainers = [ maintainers.lx ]; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 73594265adf1..2ced5b6813c6 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -13465,6 +13465,8 @@ with pkgs; inherit (darwin.apple_sdk.frameworks) Security; }; + wgautomesh = callPackage ../tools/networking/wgautomesh { }; + woff2 = callPackage ../development/web/woff2 { }; woodpecker-agent = callPackage ../development/tools/continuous-integration/woodpecker/agent.nix { };