From be43dfa9dc531bcec480892eff9441d8c03fd878 Mon Sep 17 00:00:00 2001 From: Callan Bryant Date: Sun, 1 Jun 2025 22:30:24 +0100 Subject: [PATCH 1/2] maintainers: add naggie --- maintainers/maintainer-list.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 52c741b7c370..bb2563c7d9ac 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -16928,6 +16928,12 @@ githubId = 1131571; name = "naelstrof"; }; + naggie = { + name = "Cal Bryant"; + email = "callan.bryant@gmail.com"; + github = "naggie"; + githubId = 208440; + }; nagisa = { name = "Simonas Kazlauskas"; email = "nixpkgs@kazlauskas.me"; From eb01e02bd5c96acf9e95044012452b7a1b56be70 Mon Sep 17 00:00:00 2001 From: Callan Bryant Date: Sun, 18 May 2025 17:27:53 +0100 Subject: [PATCH 2/2] dsnet: init at 0.8.1 and init module --- nixos/modules/services/networking/dsnet.md | 31 ++++ nixos/modules/services/networking/dsnet.nix | 184 ++++++++++++++++++++ pkgs/by-name/ds/dsnet/package.nix | 47 +++++ 3 files changed, 262 insertions(+) create mode 100644 nixos/modules/services/networking/dsnet.md create mode 100644 nixos/modules/services/networking/dsnet.nix create mode 100644 pkgs/by-name/ds/dsnet/package.nix diff --git a/nixos/modules/services/networking/dsnet.md b/nixos/modules/services/networking/dsnet.md new file mode 100644 index 000000000000..1d6a91024540 --- /dev/null +++ b/nixos/modules/services/networking/dsnet.md @@ -0,0 +1,31 @@ +# dsnet {#module-services-dsnet} + +dsnet is a CLI tool to manage a centralised wireguard server. It allows easy +generation of client configuration, handling key generation, IP allocation etc. + +It keeps its own configuration at `/etc/dsnetconfig.json`, which is more of a +database. It contains key material too. + +The way this module works is to patch this database with whatever is configured +in the nix service instantiation. This happens automatically when required. + +This way it is possible to decide what to let dnset manage and what parts you +want to keep declaratively. + +``` +services.dsnet = { + enable = true; + settings = { + ExternalHostname = "vpn.example.com"; + Network = "10.171.90.0/24"; + Network6 = ""; + IP = "10.171.90.1"; + IP6 = ""; + DNS = "10.171.90.1"; + Networks = [ "0.0.0.0/0" ]; + }; + +``` + + +See for more information. diff --git a/nixos/modules/services/networking/dsnet.nix b/nixos/modules/services/networking/dsnet.nix new file mode 100644 index 000000000000..131f1b0adb40 --- /dev/null +++ b/nixos/modules/services/networking/dsnet.nix @@ -0,0 +1,184 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.dsnet; + settingsFormat = pkgs.formats.json { }; + patchFile = settingsFormat.generate "dsnet-patch.json" cfg.settings; +in +{ + options.services.dsnet = { + enable = lib.mkEnableOption "dsnet, a centralised Wireguard VPN manager"; + + package = lib.mkPackageOption pkgs "dsnet" { }; + + settings = lib.mkOption { + type = lib.types.submodule { + + freeformType = settingsFormat.type; + + options = { + ExternalHostname = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "vpn.example.com"; + description = '' + The hostname that clients should use to connect to this server. + This is used to generate the client configuration files. + + This is preferred over ExternalIP, as it allows for IPv4 and + IPv6, as well as enabling the ability tp change IP. + ''; + }; + + ExternalIP = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "192.0.2.1"; + description = '' + The external IP address of the server. This is used to generate + the client configuration files for when an ExternalHostname is not set. + + Leaving this empty will cause dsnet to use the IP address of + what looks like the WAN interface. + ''; + }; + + ExternalIP6 = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "2001:db8::1"; + description = '' + The external IPv6 address of the server. This is used to generate + the client configuration files for when an ExternalHostname is + not set. Used in preference to ExternalIP. + + Leaving this empty will cause dsnet to use the IP address of + what looks like the WAN interface. + ''; + }; + + Network = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "172.18.0.0/24"; + description = '' + The IPv4 network that the server will use to allocate IPs on the network. + Leave this empty to let dsnet choose a network. + ''; + }; + + Network6 = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "2001:db8::1/64"; + description = '' + The IPv6 network that the server will use to allocate IPs on the + network. + Leave this empty to let dsnet choose a network. + ''; + }; + + IP = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "172.18.0.1"; + description = '' + The IPv4 address that the server will use on the network. + Leave this empty to let dsnet choose an address. + ''; + }; + + IP6 = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "2001:db8::1"; + description = '' + The IPv6 address that the server will use on the network + Leave this empty to let dsnet choose an address. + ''; + }; + + Networks = lib.mkOption { + type = lib.types.nullOr (lib.types.listOf lib.types.str); + default = null; + example = [ + "0.0.0.0/0" + "192.168.0.0/24" + ]; + description = '' + The CIDR networks that should route through this server. Clients + will be configured to route traffic for these networks through + the server peer. + ''; + }; + }; + }; + + default = { }; + description = '' + The settings to use for dsnet. This will be converted to a JSON + object that will be passed to dsnet as a patch, using the patch + command when the service is started. See the dsnet documentation for + more information on the additional options. + + Note that the resulting /etc/dsnetconfg.json is more of a database + than it is a configuration file. It is therefore recommended that + system specific values are configured here, rather than the full + configuration including peers. + + Peers may be managed via the dsnet add/remove commands, negating the + need to manage key material and cumbersom configuration with nix. If + you want peer configuration in nix, you may as well use the regular + wireguard module. + ''; + example = { + ExternalHostname = "vpn.example.com"; + ExternalIP = "127.0.0.1"; + ExternalIP6 = ""; + ListenPort = 51820; + Network = "10.3.148.0/22"; + Network6 = ""; + IP = "10.3.148.1"; + IP6 = ""; + DNS = "8.8.8.8"; + Networks = [ "0.0.0.0/0" ]; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + systemd.services.dsnet = { + description = "dsnet VPN Management"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + test ! -f /etc/dsnetconfig.json && ${lib.getExe cfg.package} init + ${lib.getExe cfg.package} patch < ${patchFile} + ''; + serviceConfig = { + ExecStart = "${lib.getExe cfg.package} up"; + ExecStop = "${lib.getExe cfg.package} down"; + Type = "oneshot"; + # consider the service to be active after process exits, so it can be + # reloaded + RemainAfterExit = true; + }; + + reload = '' + ${lib.getExe cfg.package} patch < ${patchFile} + ${lib.getExe cfg.package} sync < ${patchFile} + ''; + + # reload _instead_ of restarting on change + reloadIfChanged = true; + }; + }; +} diff --git a/pkgs/by-name/ds/dsnet/package.nix b/pkgs/by-name/ds/dsnet/package.nix new file mode 100644 index 000000000000..07b9a7aaed98 --- /dev/null +++ b/pkgs/by-name/ds/dsnet/package.nix @@ -0,0 +1,47 @@ +{ + lib, + stdenv, + fetchFromGitHub, + buildGoModule, +}: + +buildGoModule (finalAttrs: { + pname = "dsnet"; + version = "0.8.1"; + + src = fetchFromGitHub { + owner = "naggie"; + repo = "dsnet"; + tag = "v${finalAttrs.version}"; + hash = "sha256-CKDtILZMWFeSU5nTSguM2fi0BCFdvR2LqELIZ6LYOMk="; + }; + vendorHash = "sha256-Q2Ipj9yZ+/GUBEmDvgwFLLww7EXnbvdvj/shGQnh1G8="; + + subPackages = [ "cmd" ]; + + postInstall = '' + mv $out/bin/cmd $out/bin/dsnet + ''; + + # The ldflags reduce the executable size by stripping some debug stuff. + # The other variables are set so that the output of dsnet version shows the + # git ref and the release version from github. + # Ref + ldflags = [ + "-w" + "-s" + "-X github.com/naggie/dsnet.VERSION=${finalAttrs.src.tag}" + "-X github.com/naggie/dsnet.GIT_COMMIT=${finalAttrs.src.tag}" + ]; + + meta = { + description = "Fast command to manage a centralised Wireguard VPN"; + homepage = "https://github.com/naggie/dsnet"; + changelog = "https://github.com/naggie/dsnet/releases/tag/${finalAttrs.src.tag}"; + license = lib.licenses.mit; + platforms = lib.platforms.linux; + maintainers = [ lib.maintainers.naggie ]; + mainProgram = "dsnet"; + }; + +})