about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyan Lahfa <masterancpp@gmail.com>2023-04-19 08:26:09 +0200
committerGitHub <noreply@github.com>2023-04-19 08:26:09 +0200
commit2fa5e844de089b7c2dd9257436147d17fd361e69 (patch)
tree2e0177b7369461f07af89589626fc78a2ff83e94
parenta30c6c8e94adce7809d88ebaf8ef61586c2fbac6 (diff)
parenta727a3d6769ea064d00b24883fc08535db195f1a (diff)
Merge pull request #223749 from Alexis211/add-wgautomesh
wgautomesh: init at 0.1.0
-rw-r--r--maintainers/maintainer-list.nix7
-rw-r--r--nixos/doc/manual/release-notes/rl-2305.section.md2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/networking/wgautomesh.nix161
-rw-r--r--pkgs/tools/networking/wgautomesh/default.nix25
-rw-r--r--pkgs/top-level/all-packages.nix2
6 files changed, 198 insertions, 0 deletions
diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
index 7c5b834642420..bd8cc3688e41e 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 6b01475718477..10224db34288c 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 b6ac23c10d06b..f1c459f755708 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 0000000000000..93227a9b625d0
--- /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 0000000000000..823987888a316
--- /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 73594265adf1e..2ced5b6813c60 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 { };