diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index dc5669f991aa..e015d75e93a4 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -21,6 +21,8 @@ - [Renovate](https://github.com/renovatebot/renovate), a dependency updating tool for various git forges and language ecosystems. Available as [services.renovate](#opt-services.renovate.enable). +- [wg-access-server](https://github.com/freifunkMUC/wg-access-server/), an all-in-one WireGuard VPN solution with a web ui for connecting devices. Available at [services.wg-access-server](#opt-services.wg-access-server.enable). + ## Backward Incompatibilities {#sec-release-24.11-incompatibilities} - `transmission` package has been aliased with a `trace` warning to `transmission_3`. Since [Transmission 4 has been released last year](https://github.com/transmission/transmission/releases/tag/4.0.0), and Transmission 3 will eventually go away, it was decided perform this warning alias to make people aware of the new version. The `services.transmission.package` defaults to `transmission_3` as well because the upgrade can cause data loss in certain specific usage patterns (examples: [#5153](https://github.com/transmission/transmission/issues/5153), [#6796](https://github.com/transmission/transmission/issues/6796)). Please make sure to back up to your data directory per your usage: diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index c7004a6e3b1e..8f5d8ecd1ce3 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1225,6 +1225,7 @@ ./services/networking/vsftpd.nix ./services/networking/wasabibackend.nix ./services/networking/websockify.nix + ./services/networking/wg-access-server.nix ./services/networking/wg-netmanager.nix ./services/networking/webhook.nix ./services/networking/wg-quick.nix diff --git a/nixos/modules/services/networking/wg-access-server.nix b/nixos/modules/services/networking/wg-access-server.nix new file mode 100644 index 000000000000..5876699924b2 --- /dev/null +++ b/nixos/modules/services/networking/wg-access-server.nix @@ -0,0 +1,124 @@ +{ config, pkgs, lib, ... }: +let + inherit (lib) mkEnableOption mkPackageOption mkOption types; + + cfg = config.services.wg-access-server; + + settingsFormat = pkgs.formats.yaml { }; + configFile = settingsFormat.generate "config.yaml" cfg.settings; +in +{ + + options.services.wg-access-server = { + enable = mkEnableOption "wg-access-server"; + + package = mkPackageOption pkgs "wg-access-server" { }; + + settings = mkOption { + type = lib.types.submodule { + freeformType = settingsFormat.type; + options = { + dns.enable = mkOption { + type = types.bool; + default = true; + description = '' + Enable/disable the embedded DNS proxy server. + This is enabled by default and allows VPN clients to avoid DNS leaks by sending all DNS requests to wg-access-server itself. + ''; + }; + storage = mkOption { + type = types.str; + default = "sqlite3://db.sqlite"; + description = "A storage backend connection string. See [storage docs](https://www.freie-netze.org/wg-access-server/3-storage/)"; + }; + }; + }; + description = "See https://www.freie-netze.org/wg-access-server/2-configuration/ for possible options"; + }; + + secretsFile = mkOption { + type = types.path; + description = '' + yaml file containing all secrets. this needs to be in the same structure as the configuration. + + This must to contain the admin password and wireguard private key. + As well as the secrets for your auth backend. + + Example: + ```yaml + adminPassword: + wireguard: + privateKey: + auth: + oidc: + clientSecret: + ``` + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = + map + (attrPath: + { + assertion = !lib.hasAttrByPath attrPath config.services.wg-access-server.settings; + message = '' + {option}`services.wg-access-server.settings.${lib.concatStringsSep "." attrPath}` must definded + in {option}`services.wg-access-server.secretsFile`. + ''; + }) + [ + [ "adminPassword" ] + [ "wireguard" "privateKey" ] + [ "auth" "sessionStore" ] + [ "auth" "oidc" "clientSecret" ] + [ "auth" "gitlab" "clientSecret" ] + ]; + + boot.kernel.sysctl = { + "net.ipv4.conf.all.forwarding" = "1"; + "net.ipv6.conf.all.forwarding" = "1"; + }; + + systemd.services.wg-access-server = { + description = "WG access server"; + wantedBy = [ "multi-user.target" ]; + requires = [ "network-online.target" ]; + after = [ "network-online.target" ]; + script = '' + # merge secrets into main config + yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" ${configFile} $CREDENTIALS_DIRECTORY/SECRETS_FILE \ + > "$STATE_DIRECTORY/config.yml" + + ${lib.getExe cfg.package} serve --config "$STATE_DIRECTORY/config.yml" + ''; + + path = with pkgs; [ + iptables + # needed by startup script + yq-go + ]; + + serviceConfig = + let + capabilities = [ + "CAP_NET_ADMIN" + ] ++ lib.optional cfg.settings.dns.enabled "CAP_NET_BIND_SERVICE"; + in + { + WorkingDirectory = "/var/lib/wg-access-server"; + StateDirectory = "wg-access-server"; + + LoadCredential = [ + "SECRETS_FILE:${cfg.secretsFile}" + ]; + + # Hardening + DynamicUser = true; + AmbientCapabilities = capabilities; + CapabilityBoundingSet = capabilities; + }; + }; + }; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index d9551c33d8f6..ad9025a917c3 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1042,6 +1042,7 @@ in { wiki-js = handleTest ./wiki-js.nix {}; wine = handleTest ./wine.nix {}; wireguard = handleTest ./wireguard {}; + wg-access-server = handleTest ./wg-access-server.nix {}; without-nix = handleTest ./without-nix.nix {}; wmderland = handleTest ./wmderland.nix {}; workout-tracker = handleTest ./workout-tracker.nix {}; diff --git a/nixos/tests/wg-access-server.nix b/nixos/tests/wg-access-server.nix new file mode 100644 index 000000000000..84fdf43e7943 --- /dev/null +++ b/nixos/tests/wg-access-server.nix @@ -0,0 +1,28 @@ +import ./make-test-python.nix ({ pkgs, lib, kernelPackages ? null, ... }: +{ + name = "wg-access-server"; + meta = with pkgs.lib.maintainers; { + maintainers = [ xanderio ]; + }; + + nodes = { + server = { + services.wg-access-server = { + enable = true; + settings = { + adminUsername = "admin"; + }; + secretsFile = (pkgs.writers.writeYAML "secrets.yaml" { + adminPassword = "hunter2"; + }); + }; + }; + }; + + testScript = '' + start_all() + + server.wait_for_unit("wg-access-server.service") + ''; +} +) diff --git a/pkgs/by-name/wg/wg-access-server/package.nix b/pkgs/by-name/wg/wg-access-server/package.nix new file mode 100644 index 000000000000..e10210f31da4 --- /dev/null +++ b/pkgs/by-name/wg/wg-access-server/package.nix @@ -0,0 +1,71 @@ +{ lib +, buildGoModule +, buildNpmPackage +, fetchFromGitHub +, makeWrapper +, iptables +, nixosTests +}: + +buildGoModule rec { + pname = "wg-access-server"; + version = "0.12.1"; + + src = fetchFromGitHub { + owner = "freifunkMUC"; + repo = "wg-access-server"; + rev = "v${version}"; + hash = "sha256-AhFqEmHrx9MCdjnB/YA3qU7KsaMyLO+vo53VWUrcL8I="; + }; + + proxyVendor = true; # darwin/linux hash mismatch + vendorHash = "sha256-YwFq0KxUctU3ElZBo/b68pyp4lJnFGL9ClKIwUzdngM="; + + CGO_ENABLED = 1; + + ldflags = [ "-s" "-w" ]; + + nativeBuildInputs = [ makeWrapper ]; + + checkFlags = [ "-skip=TestDNSProxy_ServeDNS" ]; + + ui = buildNpmPackage { + inherit version src; + pname = "wg-access-server-ui"; + + npmDepsHash = "sha256-04AkSDSKsr20Jbx5BJy36f8kWNlzzplu/xnoDTkU8OQ="; + + sourceRoot = "${src.name}/website"; + + installPhase = '' + mv build $out + ''; + }; + + postPatch = '' + substituteInPlace internal/services/website_router.go \ + --replace-fail 'website/build' "${ui}" + ''; + + preBuild = '' + VERSION=v${version} go generate buildinfo/buildinfo.go + ''; + + postInstall = '' + mkdir -p $out/ + wrapProgram $out/bin/wg-access-server \ + --prefix PATH : ${lib.makeBinPath [ iptables ]} + ''; + + passthru = { + tests = { inherit (nixosTests) wg-access-server; }; + }; + + meta = with lib; { + description = "An all-in-one WireGuard VPN solution with a web ui for connecting devices"; + homepage = "https://github.com/freifunkMUC/wg-access-server"; + license = licenses.mit; + maintainers = with maintainers; [ xanderio ]; + mainProgram = "wg-access-server"; + }; +}