feat: netbird setup

This commit is contained in:
Patrick 2024-11-21 18:10:33 +01:00
parent 66fa39a4c1
commit 063a6d9f24
Signed by: patrick
GPG key ID: 451F95EFB8BECD0F
14 changed files with 904 additions and 340 deletions

View file

@ -1,7 +1,6 @@
{ {
lib, lib,
config, config,
pkgs,
... ...
}: }:
{ {
@ -34,11 +33,4 @@
''; '';
}; };
services.mullvad-vpn = {
enable = true;
package = pkgs.mullvad-vpn;
};
environment.persistence."/state".directories = [
"/etc/mullvad-vpn"
];
} }

View file

@ -38,6 +38,7 @@
firefly-iii = uidGid 226; firefly-iii = uidGid 226;
homebox = uidGid 227; homebox = uidGid 227;
signal = uidGid 228; signal = uidGid 228;
netbird-main = uidGid 229;
paperless = uidGid 315; paperless = uidGid 315;
systemd-oom = uidGid 300; systemd-oom = uidGid 300;
systemd-coredump = uidGid 301; systemd-coredump = uidGid 301;

View file

@ -38,6 +38,7 @@
"||adguardhome.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}" "||adguardhome.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}"
"||nc.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}" "||nc.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}"
"||immich.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}" "||immich.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth config.secrets.secrets.global.net.privateSubnetv4}"
"||smb.${config.secrets.secrets.global.domains.web}^$dnsrewrite=${lib.net.cidr.host config.secrets.secrets.global.net.ips.elisabeth-samba config.secrets.secrets.global.net.privateSubnetv4}"
"||fritz.box^$dnsrewrite=${lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4}" "||fritz.box^$dnsrewrite=${lib.net.cidr.host 1 config.secrets.secrets.global.net.privateSubnetv4}"
]; ];
dhcp.enabled = false; dhcp.enabled = false;

View file

@ -36,6 +36,14 @@
networking.firewall.allowedUDPPorts = [ 3478 ]; # STUN/TURN server networking.firewall.allowedUDPPorts = [ 3478 ]; # STUN/TURN server
services.netbird = { services.netbird = {
clients.main = {
port = 51820;
environment = {
NB_MANAGEMENT_URL = "https://netbird.${config.secrets.secrets.global.domains.web}";
NB_ADMIN_URL = "https://netbird.${config.secrets.secrets.global.domains.web}";
NB_HOSTNAME = "home";
};
};
server = { server = {
enable = true; enable = true;
domain = "netbird.${config.secrets.secrets.global.domains.web}"; domain = "netbird.${config.secrets.secrets.global.domains.web}";
@ -50,7 +58,10 @@
}; };
}; };
relay.authSecretFile = config.age.secrets.relaySecret.path; relay = {
authSecretFile = config.age.secrets.relaySecret.path;
settings.NB_EXPOSED_ADDRESS = "rels://netbird.${config.secrets.secrets.global.domains.web}:443";
};
coturn = { coturn = {
enable = true; enable = true;
@ -66,6 +77,7 @@
TURNConfig = { TURNConfig = {
Secret._secret = config.age.secrets.coturnSecret.path; Secret._secret = config.age.secrets.coturnSecret.path;
}; };
Signal.URI = "netbird.${config.secrets.secrets.global.domains.web}:443";
HttpConfig = { HttpConfig = {
# This is not possible # This is not possible
# failed validating JWT token sent from peer y1ParZkbzVMQGeU/KMycYl75v90i2O6EwgO1YQZnSFs= with error rpc error: code = Internal desc = unable to fetch account with claims, err: user ID is empty # failed validating JWT token sent from peer y1ParZkbzVMQGeU/KMycYl75v90i2O6EwgO1YQZnSFs= with error rpc error: code = Internal desc = unable to fetch account with claims, err: user ID is empty
@ -88,5 +100,10 @@
mode = "440"; mode = "440";
user = "netbird"; user = "netbird";
} }
{
directory = "/var/lib/netbird-main";
mode = "440";
user = "netbird-main";
}
]; ];
} }

View file

@ -8,19 +8,6 @@ in
openFirewall = true; openFirewall = true;
}; };
disabledModules = [ "services/networking/netbird.nix" ];
imports = [ ../../modules/netbird-client.nix ];
services.netbird.tunnels = {
netbird-samba = {
environment = {
NB_MANAGEMENT_URL = "https://netbird.${config.secrets.secrets.global.domains.web}";
NB_HOSTNAME = "samba";
};
port = 56789;
};
};
age.secrets.resticpasswd = { age.secrets.resticpasswd = {
generator.script = "alnum"; generator.script = "alnum";
}; };
@ -64,7 +51,6 @@ in
networking.nftables.firewall.zones.untrusted.interfaces = [ networking.nftables.firewall.zones.untrusted.interfaces = [
"samba-patrick" "samba-patrick"
"netbird-samba"
]; ];
services.samba = { services.samba = {
@ -366,16 +352,6 @@ in
]; ];
} }
)) ))
(lib.flip lib.mapAttrsToList config.services.netbird.tunnels (
_: v: {
"/state".directories = [
{
directory = "/var/lib/${v.stateDir}";
mode = "0770";
}
];
}
))
] ]
); );
} }

View file

@ -1,4 +1,8 @@
{ config, ... }: {
config,
pkgs,
...
}:
{ {
networking = { networking = {
inherit (config.secrets.secrets.local.networking) hostId; inherit (config.secrets.secrets.local.networking) hostId;
@ -23,4 +27,26 @@
}; };
networking.nftables.firewall.zones.untrusted.interfaces = [ "lan01" ]; networking.nftables.firewall.zones.untrusted.interfaces = [ "lan01" ];
wireguard.samba-patrick.client.via = "elisabeth-samba"; wireguard.samba-patrick.client.via = "elisabeth-samba";
services.mullvad-vpn = {
enable = true;
package = pkgs.mullvad-vpn;
};
environment.persistence."/state".directories = [
"/etc/mullvad-vpn"
{
directory = "/var/lib/netbird-main";
owner = "netbird-main";
}
];
services.netbird = {
clients.main = {
port = 51820;
environment = {
NB_MANAGEMENT_URL = "https://netbird.${config.secrets.secrets.global.domains.web}";
NB_ADMIN_URL = "https://netbird.${config.secrets.secrets.global.domains.web}";
NB_HOSTNAME = "desktopnix";
};
};
};
users.users."patrick".extraGroups = [ "netbird-main" ];
} }

View file

@ -27,4 +27,5 @@
}; };
}; };
nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.hostPlatform = "x86_64-linux";
documentation.enable = false;
} }

View file

@ -24,7 +24,6 @@ let
apispotify = "apisptfy"; apispotify = "apisptfy";
kanidm = "auth"; kanidm = "auth";
oauth2-proxy = "oauth2"; oauth2-proxy = "oauth2";
netbird = "netbird";
actual = "actual"; actual = "actual";
firefly = "money"; firefly = "money";
homebox = "homebox"; homebox = "homebox";
@ -44,6 +43,19 @@ let
nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4; nodes."elisabeth-${hostName}".config.wireguard.elisabeth.ipv4;
in in
{ {
services.netbird.server.proxy =
let
cfg = nodes.elisabeth-netbird.config.services.netbird.server;
in
{
domain = "netbird.${config.secrets.secrets.global.domains.web}";
enable = true;
enableNginx = true;
signalAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:${toString cfg.signal.port}";
relayAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:${toString cfg.relay.port}";
managementAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:${toString cfg.management.port}";
dashboardAddress = "${nodes.elisabeth-netbird.config.wireguard.elisabeth.ipv4}:80";
};
services.nginx = services.nginx =
let let
blockOf = blockOf =
@ -129,51 +141,7 @@ in
{ {
enable = true; enable = true;
recommendedSetup = true; recommendedSetup = true;
upstreams.netbird = { virtualHosts."netbird.${config.secrets.secrets.global.domains.web}".useACMEHost = "web";
servers."${ipOf "netbird"}:80" = { };
extraConfig = ''
zone netbird 64k ;
keepalive 5 ;
'';
};
upstreams.netbird-mgmt = {
servers."${ipOf "netbird"}:3000" = { };
extraConfig = ''
zone netbird 64k ;
keepalive 5 ;
'';
};
virtualHosts.${domainOf "netbird"} = {
forceSSL = true;
useACMEHost = "web";
locations = {
"/" = {
proxyPass = "http://netbird";
proxyWebsockets = true;
X-Frame-Options = "SAMEORIGIN";
};
"/signalexchange.SignalExchange/".extraConfig = ''
grpc_pass grpc://${ipOf "netbird"}:8012;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
'';
"/api".proxyPass = "http://netbird-mgmt";
"/management.ManagementService/".extraConfig = ''
grpc_pass grpc://${ipOf "netbird"}:3000;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
'';
};
extraConfig = ''
client_max_body_size 500M ;
client_header_timeout 1d;
client_body_timeout 1d;
'';
};
} }
(blockOf "vaultwarden" { maxBodySize = "1G"; }) (blockOf "vaultwarden" { maxBodySize = "1G"; })
(blockOf "forgejo" { maxBodySize = "1G"; }) (blockOf "forgejo" { maxBodySize = "1G"; })

View file

@ -1,6 +1,9 @@
{ config, ... }: { config, pkgs, ... }:
{ {
environment.persistence."/state".directories = [ "/var/lib/iwd" ]; environment.persistence."/state".directories = [
"/var/lib/iwd"
"/etc/mullvad-vpn"
];
age.secrets.eduroam = { age.secrets.eduroam = {
rekeyFile = ./secrets/iwd/eduroam.8021x.age; rekeyFile = ./secrets/iwd/eduroam.8021x.age;
path = "/var/lib/iwd/eduroam.8021x"; path = "/var/lib/iwd/eduroam.8021x";
@ -69,4 +72,8 @@
dhcpV6Config.RouteMetric = 40; dhcpV6Config.RouteMetric = 40;
}; };
}; };
services.mullvad-vpn = {
enable = true;
package = pkgs.mullvad-vpn;
};
} }

View file

@ -1,219 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
attrNames
getExe
literalExpression
maintainers
mapAttrs'
mkDefault
mkEnableOption
mkIf
mkMerge
mkOption
mkPackageOption
nameValuePair
optional
versionOlder
;
inherit (lib.types)
attrsOf
port
str
submodule
bool
nullOr
path
;
kernel = config.boot.kernelPackages;
cfg = config.services.netbird;
in
{
meta.maintainers = with maintainers; [
misuzu
thubrecht
patrickdag
];
meta.doc = ./netbird.md;
options.services.netbird = {
enable = mkEnableOption (lib.mdDoc "Netbird daemon");
package = mkPackageOption pkgs "netbird" { };
tunnels = mkOption {
type = attrsOf (
submodule (
{ name, config, ... }:
{
options = {
port = mkOption {
type = port;
default = 51820;
description = ''
Port for the ${name} netbird interface.
'';
};
userAccess = mkOption {
type = bool;
description = "Allow unprivileged users access to the control socket";
default = false;
};
environmentFile = mkOption {
type = nullOr path;
default = null;
description = "An additional environment file for this service.";
};
environment = mkOption {
type = attrsOf str;
defaultText = literalExpression ''
{
NB_CONFIG = "/var/lib/''${stateDir}/config.json";
NB_LOG_FILE = "console";
NB_WIREGUARD_PORT = builtins.toString port;
NB_INTERFACE_NAME = name;
NB_DAMEON_ADDR = "/var/run/''${stateDir}"
}
'';
description = ''
Environment for the netbird service, used to pass configuration options.
'';
};
stateDir = mkOption {
type = str;
default = "netbird-${name}";
description = ''
Directory storing the netbird configuration.
'';
};
};
config.environment = builtins.mapAttrs (_: mkDefault) {
NB_CONFIG = "/var/lib/${config.stateDir}/config.json";
NB_LOG_FILE = "console";
NB_WIREGUARD_PORT = builtins.toString config.port;
NB_INTERFACE_NAME = name;
NB_DAEMON_ADDR = "unix:///var/run/${config.stateDir}/sock";
};
}
)
);
default = { };
description = ''
Attribute set of Netbird tunnels, each one will spawn a daemon listening on ...
'';
};
};
config = mkMerge [
(mkIf cfg.enable {
# For backwards compatibility
services.netbird.tunnels.wt0.stateDir = "netbird";
})
(mkIf (cfg.tunnels != { }) {
boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
environment.systemPackages = [ cfg.package ];
networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels;
systemd.network.networks = mkIf config.networking.useNetworkd (
mapAttrs' (
name: _:
nameValuePair "50-netbird-${name}" {
matchConfig = {
Name = name;
};
linkConfig = {
Unmanaged = true;
ActivationPolicy = "manual";
};
}
) cfg.tunnels
);
systemd.services = mapAttrs' (
name:
{
environment,
stateDir,
environmentFile,
userAccess,
...
}:
nameValuePair "netbird-${name}" {
description = "A WireGuard-based mesh network that connects your devices into a single private network";
documentation = [ "https://netbird.io/docs/" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ openresolv ];
inherit environment;
serviceConfig = {
EnvironmentFile = mkIf (environmentFile != null) environmentFile;
ExecStart = "${getExe cfg.package} service run";
Restart = "always";
RuntimeDirectory = stateDir;
StateDirectory = stateDir;
StateDirectoryMode = "0700";
WorkingDirectory = "/var/lib/${stateDir}";
RuntimeDirectoryMode = if userAccess then "0755" else "0750";
# hardening
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateMounts = true;
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = false; # needed to load wg module for kernel-mode WireGuard
ProtectKernelTunables = false;
ProtectSystem = true;
RemoveIPC = true;
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
# Hardening
#CapabilityBoundingSet = "";
#PrivateUsers = true;
#ProtectProc = "invisible";
#ProcSubset = "pid";
#RestrictAddressFamilies = [
# "AF_INET"
# "AF_INET6"
# "AF_NETLINK"
#];
#SystemCallArchitectures = "native";
#SystemCallFilter = [
# "@system-service"
# "@pkey"
#];
UMask = "0077";
};
stopIfChanged = false;
}
) cfg.tunnels;
})
];
}

789
patches/287236.diff Normal file
View file

@ -0,0 +1,789 @@
diff --git a/nixos/modules/services/networking/netbird.md b/nixos/modules/services/networking/netbird.md
index e1f6753cbd30c..876c27cb0d22e 100644
--- a/nixos/modules/services/networking/netbird.md
+++ b/nixos/modules/services/networking/netbird.md
@@ -2,7 +2,7 @@
## Quickstart {#module-services-netbird-quickstart}
-The absolute minimal configuration for the netbird daemon looks like this:
+The absolute minimal configuration for the Netbird client daemon looks like this:
```nix
{
@@ -13,52 +13,76 @@ The absolute minimal configuration for the netbird daemon looks like this:
This will set up a netbird service listening on the port `51820` associated to the
`wt0` interface.
-It is strictly equivalent to setting:
+Which is equivalent to:
```nix
{
- services.netbird.tunnels.wt0.stateDir = "netbird";
+ services.netbird.clients.wt0 = {
+ port = 51820;
+ name = "netbird";
+ interface = "wt0";
+ hardened = false;
+ };
}
```
-The `enable` option is mainly kept for backward compatibility, as defining netbird
-tunnels through the `tunnels` option is more expressive.
+This will set up a `netbird.service` listening on the port `51820` associated to the
+`wt0` interface. There will also be `netbird-wt0` binary installed in addition to `netbird`.
+
+see [clients](#opt-services.netbird.clients) option documentation for more details.
## Multiple connections setup {#module-services-netbird-multiple-connections}
-Using the `services.netbird.tunnels` option, it is also possible to define more than
+Using the `services.netbird.clients` option, it is possible to define more than
one netbird service running at the same time.
-The following configuration will start a netbird daemon using the interface `wt1` and
-the port 51830. Its configuration file will then be located at `/var/lib/netbird-wt1/config.json`.
+You must at least define a `port` for the service to listen on, the rest is optional:
```nix
{
- services.netbird.tunnels = {
- wt1 = {
- port = 51830;
- };
- };
+ services.netbird.clients.wt1.port = 51830;
+ services.netbird.clients.wt2.port = 51831;
}
```
-To interact with it, you will need to specify the correct daemon address:
-
-```bash
-netbird --daemon-addr unix:///var/run/netbird-wt1/sock ...
-```
+see [clients](#opt-services.netbird.clients) option documentation for more details.
-The address will by default be `unix:///var/run/netbird-<name>`.
+## Exposing services internally on the Netbird network {#module-services-netbird-firewall}
-It is also possible to overwrite default options passed to the service, for
-example:
+You can easily expose services exclusively to Netbird network by combining
+[`networking.firewall.interfaces`](#opt-networking.firewall.interfaces) rules
+with [`interface`](#opt-services.netbird.clients._name_.interface) names:
```nix
{
- services.netbird.tunnels.wt1.environment = {
- NB_DAEMON_ADDR = "unix:///var/run/toto.sock";
+ services.netbird.clients.priv.port = 51819;
+ services.netbird.clients.work.port = 51818;
+ networking.firewall.interfaces = {
+ "${config.services.netbird.clients.priv.interface}" = {
+ allowedUDPPorts = [ 1234 ];
+ };
+ "${config.services.netbird.clients.work.interface}" = {
+ allowedTCPPorts = [ 8080 ];
+ };
};
}
```
-This will set the socket to interact with the netbird service to `/var/run/toto.sock`.
+### Additional customizations {#module-services-netbird-customization}
+
+Each Netbird client service by default:
+
+- runs in a [hardened](#opt-services.netbird.clients._name_.hardened) mode,
+- starts with the system,
+- [opens up a firewall](#opt-services.netbird.clients._name_.openFirewall) for direct (without TURN servers)
+ peer-to-peer communication,
+- can be additionally configured with environment variables,
+- automatically determines whether `netbird-ui-<name>` should be available,
+
+[autoStart](#opt-services.netbird.clients._name_.autoStart) allows you to start the client (an actual systemd service)
+on demand, for example to connect to work-related or otherwise conflicting network only when required.
+See the option description for more information.
+
+[environment](#opt-services.netbird.clients._name_.environment) allows you to pass additional configurations
+through environment variables, but special care needs to be taken for overriding config location and
+daemon address due [hardened](#opt-services.netbird.clients._name_.hardened) option.
diff --git a/nixos/modules/services/networking/netbird.nix b/nixos/modules/services/networking/netbird.nix
index d39c373dbc94c..c9a2251437c6a 100644
--- a/nixos/modules/services/networking/netbird.nix
+++ b/nixos/modules/services/networking/netbird.nix
@@ -7,64 +7,179 @@
let
inherit (lib)
- attrNames
+ attrValues
+ concatLists
+ concatStringsSep
+ escapeShellArgs
+ filterAttrs
getExe
literalExpression
maintainers
+ makeBinPath
mapAttrs'
+ mapAttrsToList
mkDefault
- mkEnableOption
mkIf
mkMerge
mkOption
+ mkOptionDefault
mkPackageOption
+ mkRemovedOptionModule
nameValuePair
optional
+ optionalAttrs
+ optionalString
+ toShellVars
+ versionAtLeast
versionOlder
;
inherit (lib.types)
attrsOf
+ bool
+ enum
+ nullOr
+ package
port
str
submodule
;
- kernel = config.boot.kernelPackages;
+ inherit (config.boot) kernelPackages;
+ inherit (config.boot.kernelPackages) kernel;
cfg = config.services.netbird;
+
+ toClientList = fn: map fn (attrValues cfg.clients);
+ toClientAttrs = fn: mapAttrs' (_: fn) cfg.clients;
+
+ hardenedClients = filterAttrs (_: client: client.hardened) cfg.clients;
+ toHardenedClientList = fn: map fn (attrValues hardenedClients);
+ toHardenedClientAttrs = fn: mapAttrs' (_: fn) hardenedClients;
+
+ nixosConfig = config;
in
{
- meta.maintainers = with maintainers; [ ];
+ meta.maintainers = with maintainers; [
+ nazarewk
+ ];
meta.doc = ./netbird.md;
+ imports = [
+ (mkRemovedOptionModule [
+ "services"
+ "netbird"
+ "tunnels"
+ ] "The option `services.netbird.tunnels` has been renamed to `services.netbird.clients`")
+ ];
+
options.services.netbird = {
- enable = mkEnableOption "Netbird daemon";
+ enable = mkOption {
+ type = bool;
+ default = false;
+ description = ''
+ Enables backwards compatible Netbird client service.
+
+ This is strictly equivalent to:
+
+ ```nix
+ services.netbird.clients.wt0 = {
+ port = 51820;
+ name = "netbird";
+ interface = "wt0";
+ hardened = false;
+ };
+ ```
+ '';
+ };
package = mkPackageOption pkgs "netbird" { };
- tunnels = mkOption {
+ ui.enable = mkOption {
+ type = bool;
+ default = config.services.displayManager.sessionPackages != [ ] || config.services.xserver.enable;
+ defaultText = literalExpression ''
+ config.services.displayManager.sessionPackages != [ ] || config.services.xserver.enable
+ '';
+ description = ''
+ Controls presence `netbird-ui` wrappers, defaults to presence of graphical sessions.
+ '';
+ };
+ ui.package = mkPackageOption pkgs "netbird-ui" { };
+
+ clients = mkOption {
type = attrsOf (
submodule (
{ name, config, ... }:
+ let
+ client = config;
+ in
{
options = {
port = mkOption {
type = port;
- default = 51820;
+ example = literalExpression "51820";
+ description = ''
+ Port the Netbird client listens on.
+ '';
+ };
+
+ name = mkOption {
+ type = str;
+ default = name;
+ description = ''
+ Primary name for use (as a suffix) in:
+ - systemd service name,
+ - hardened user name and group,
+ - [systemd `*Directory=`](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RuntimeDirectory=) names,
+ - desktop application identification,
+ '';
+ };
+
+ dns-resolver.address = mkOption {
+ type = nullOr str;
+ default = null;
+ example = "127.0.0.123";
description = ''
- Port for the ${name} netbird interface.
+ An explicit address that Netbird will serve `*.netbird.cloud.` (usually) entries on.
+
+ Netbird serves DNS on it's own (dynamic) client address by default.
'';
};
+ dns-resolver.port = mkOption {
+ type = port;
+ default = 53;
+ description = ''
+ A port to serve DNS entries on when `dns-resolver.address` is enabled.
+ '';
+ };
+
+ interface = mkOption {
+ type = str;
+ default = "nb-${client.name}";
+ description = ''
+ Name of the network interface managed by this client.
+ '';
+ apply =
+ iface:
+ lib.throwIfNot (
+ builtins.stringLength iface <= 15
+ ) "Network interface name must be 15 characters or less" iface;
+ };
+
environment = mkOption {
type = attrsOf str;
defaultText = literalExpression ''
{
- NB_CONFIG = "/var/lib/''${stateDir}/config.json";
- NB_LOG_FILE = "console";
- NB_WIREGUARD_PORT = builtins.toString port;
- NB_INTERFACE_NAME = name;
- NB_DAMEON_ADDR = "/var/run/''${stateDir}"
+ NB_CONFIG = "/var/lib/netbird-''${client.name}/config.json";
+ NB_DAEMON_ADDR = "unix:///var/run/netbird-''${client.name}/sock";
+ NB_INTERFACE_NAME = client.interface;
+ NB_LOG_FILE = mkOptionDefault "console";
+ NB_LOG_LEVEL = client.logLevel;
+ NB_SERVICE = "netbird-''${client.name}";
+ NB_WIREGUARD_PORT = toString client.port;
+ } // optionalAttrs (client.dns-resolver.address != null) {
+ NB_DNS_RESOLVER_ADDRESS = "''${client.dns-resolver.address}:''${builtins.toString client.dns-resolver.port}";
}
'';
description = ''
@@ -72,97 +187,416 @@ in
'';
};
- stateDir = mkOption {
- type = str;
- default = "netbird-${name}";
+ autoStart = mkOption {
+ type = bool;
+ default = true;
description = ''
- Directory storing the netbird configuration.
+ Start the service with the system.
+
+ As of 2024-02-13 it is not possible to start a Netbird client daemon without immediately
+ connecting to the network, but it is [planned for a near future](https://github.com/netbirdio/netbird/projects/2#card-91718018).
'';
};
- };
- config.environment = builtins.mapAttrs (_: mkDefault) {
- NB_CONFIG = "/var/lib/${config.stateDir}/config.json";
- NB_LOG_FILE = "console";
- NB_WIREGUARD_PORT = builtins.toString config.port;
- NB_INTERFACE_NAME = name;
- NB_DAEMON_ADDR = "unix:///var/run/${config.stateDir}/sock";
+ openFirewall = mkOption {
+ type = bool;
+ default = true;
+ description = ''
+ Opens up firewall `port` for communication between Netbird peers directly over LAN or public IP,
+ without using (internet-hosted) TURN servers as intermediaries.
+ '';
+ };
+
+ hardened = mkOption {
+ type = bool;
+ default = true;
+ description = ''
+ Hardened service:
+ - runs as a dedicated user with minimal set of permissions (see caveats),
+ - restricts daemon configuration socket access to dedicated user group
+ (you can grant access to it with `users.users."<user>".extraGroups = [ "netbird-${client.name}" ]`),
+
+ Even though the local system resources access is restricted:
+ - `CAP_NET_RAW`, `CAP_NET_ADMIN` and `CAP_BPF` still give unlimited network manipulation possibilites,
+ - older kernels don't have `CAP_BPF` and use `CAP_SYS_ADMIN` instead,
+
+ Known security features that are not (yet) integrated into the module:
+ - 2024-02-14: `rosenpass` is an experimental feature configurable solely
+ through `--enable-rosenpass` flag on the `netbird up` command,
+ see [the docs](https://docs.netbird.io/how-to/enable-post-quantum-cryptography)
+ '';
+ };
+
+ logLevel = mkOption {
+ type = enum [
+ # logrus loglevels
+ "panic"
+ "fatal"
+ "error"
+ "warn"
+ "warning"
+ "info"
+ "debug"
+ "trace"
+ ];
+ default = "info";
+ description = "Log level of the Netbird daemon.";
+ };
+
+ ui.enable = mkOption {
+ type = bool;
+ default = nixosConfig.services.netbird.ui.enable;
+ defaultText = literalExpression ''client.ui.enable'';
+ description = ''
+ Controls presence of `netbird-ui` wrapper for this Netbird client.
+ '';
+ };
+
+ wrapper = mkOption {
+ type = package;
+ internal = true;
+ default =
+ let
+ makeWrapperArgs = concatLists (
+ mapAttrsToList (key: value: [
+ "--set-default"
+ key
+ value
+ ]) client.environment
+ );
+ in
+ pkgs.stdenv.mkDerivation {
+ name = "${cfg.package.name}-wrapper-${client.name}";
+ meta.mainProgram = "netbird-${client.name}";
+ nativeBuildInputs = with pkgs; [ makeWrapper ];
+ phases = [ "installPhase" ];
+ installPhase = concatStringsSep "\n" [
+ ''
+ mkdir -p "$out/bin"
+ makeWrapper ${lib.getExe cfg.package} "$out/bin/netbird-${client.name}" \
+ ${escapeShellArgs makeWrapperArgs}
+ ''
+ (optionalString cfg.ui.enable ''
+ # netbird-ui doesn't support envvars
+ makeWrapper ${lib.getExe cfg.ui.package} "$out/bin/netbird-ui-${client.name}" \
+ --add-flags '--daemon-addr=${client.environment.NB_DAEMON_ADDR}'
+
+ mkdir -p "$out/share/applications"
+ substitute ${cfg.ui.package}/share/applications/netbird.desktop \
+ "$out/share/applications/netbird-${client.name}.desktop" \
+ --replace-fail 'Name=Netbird' "Name=Netbird @ netbird-${client.name}" \
+ --replace-fail '${lib.getExe cfg.ui.package}' "$out/bin/netbird-ui-${client.name}"
+ '')
+ ];
+ };
+ };
+
+ # see https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82
+ config = mkOption {
+ type = (pkgs.formats.json { }).type;
+ defaultText = literalExpression ''
+ {
+ DisableAutoConnect = !client.autoStart;
+ WgIface = client.interface;
+ WgPort = client.port;
+ } // optionalAttrs (client.dns-resolver.address != null) {
+ CustomDNSAddress = "''${client.dns-resolver.address}:''${builtins.toString client.dns-resolver.port}";
+ }
+ '';
+ description = ''
+ Additional configuration that exists before the first start and
+ later overrides the existing values in `config.json`.
+
+ It is mostly helpful to manage configuration ignored/not yet implemented
+ outside of `netbird up` invocation.
+
+ WARNING: this is not an upstream feature, it could break in the future
+ (by having lower priority) after upstream implements an equivalent.
+
+ It is implemented as a `preStart` script which overrides `config.json`
+ with content of `/etc/netbird-${client.name}/config.d/*.json` files.
+ This option manages specifically `50-nixos.json` file.
+
+ Consult [the source code](https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82)
+ or inspect existing file for a complete list of available configurations.
+ '';
+ };
};
+
+ config.environment =
+ {
+ NB_CONFIG = "/var/lib/netbird-${client.name}/config.json";
+ NB_DAEMON_ADDR = "unix:///var/run/netbird-${client.name}/sock";
+ NB_INTERFACE_NAME = client.interface;
+ NB_LOG_FILE = mkOptionDefault "console";
+ NB_LOG_LEVEL = client.logLevel;
+ NB_SERVICE = "netbird-${client.name}";
+ NB_WIREGUARD_PORT = toString client.port;
+ }
+ // optionalAttrs (client.dns-resolver.address != null) {
+ NB_DNS_RESOLVER_ADDRESS = "${client.dns-resolver.address}:${builtins.toString client.dns-resolver.port}";
+ };
+
+ config.config =
+ {
+ DisableAutoConnect = !client.autoStart;
+ WgIface = client.interface;
+ WgPort = client.port;
+ }
+ // optionalAttrs (client.dns-resolver.address != null) {
+ CustomDNSAddress = "${client.dns-resolver.address}:${builtins.toString client.dns-resolver.port}";
+ };
}
)
);
default = { };
description = ''
- Attribute set of Netbird tunnels, each one will spawn a daemon listening on ...
+ Attribute set of Netbird client daemons, by default each one will:
+
+ 1. be manageable using dedicated tooling:
+ - `netbird-<name>` script,
+ - `Netbird - netbird-<name>` graphical interface when appropriate (see `ui.enable`),
+ 2. run as a `netbird-<name>.service`,
+ 3. listen for incoming remote connections on the port `51820` (`openFirewall` by default),
+ 4. manage the `netbird-<name>` wireguard interface,
+ 5. use the `/var/lib/netbird-<name>/config.json` configuration file,
+ 6. override `/var/lib/netbird-<name>/config.json` with values from `/etc/netbird-<name>/config.d/*.json`,
+ 7. (`hardened`) be locally manageable by `netbird-<name>` system group,
+
+ With following caveats:
+
+ - multiple daemons will interfere with each other's DNS resolution of `netbird.cloud`, but
+ should remain fully operational otherwise.
+ Setting up custom (non-conflicting) DNS zone is currently possible only when self-hosting.
+ '';
+ example = lib.literalExpression ''
+ {
+ services.netbird.clients.wt0.port = 51820;
+ services.netbird.clients.personal.port = 51821;
+ services.netbird.clients.work1.port = 51822;
+ }
'';
};
};
config = mkMerge [
- (mkIf cfg.enable {
- # For backwards compatibility
- services.netbird.tunnels.wt0.stateDir = "netbird";
- })
+ (mkIf cfg.enable (
+ let
+ name = "wt0";
+ client = cfg.clients."${name}";
+ in
+ {
+ services.netbird.clients."${name}" = {
+ port = mkDefault 51820;
+ name = mkDefault "netbird";
+ interface = mkDefault "wt0";
+ hardened = mkDefault false;
+ };
+
+ environment.systemPackages = [
+ (lib.hiPrio (
+ pkgs.runCommand "${client.name}-as-default" { } ''
+ mkdir -p "$out/bin"
+ for binary in netbird ${optionalString cfg.ui.enable "netbird-ui"} ; do
+ ln -s "${client.wrapper}/bin/$binary-${client.name}" "$out/bin/$binary"
+ done
+ ''
+ ))
+ ];
+ }
+ ))
+ {
+ boot.extraModulePackages = optional (
+ cfg.clients != { } && (versionOlder kernel.version "5.6")
+ ) kernelPackages.wireguard;
- (mkIf (cfg.tunnels != { }) {
- boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard;
+ environment.systemPackages = toClientList (client: client.wrapper)
+ # omitted due to https://github.com/netbirdio/netbird/issues/1562
+ #++ optional (cfg.clients != { }) cfg.package
+ # omitted due to https://github.com/netbirdio/netbird/issues/1581
+ #++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package
+ ;
- environment.systemPackages = [ cfg.package ];
+ networking.dhcpcd.denyInterfaces = toClientList (client: client.interface);
+ networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}");
- networking.dhcpcd.denyInterfaces = attrNames cfg.tunnels;
+ networking.firewall.allowedUDPPorts = concatLists (
+ toClientList (client: optional client.openFirewall client.port)
+ );
systemd.network.networks = mkIf config.networking.useNetworkd (
- mapAttrs'
- (
- name: _:
- nameValuePair "50-netbird-${name}" {
- matchConfig = {
- Name = name;
- };
- linkConfig = {
- Unmanaged = true;
- ActivationPolicy = "manual";
- };
- }
- )
- cfg.tunnels
+ toClientAttrs (
+ client:
+ nameValuePair "50-netbird-${client.interface}" {
+ matchConfig = {
+ Name = client.interface;
+ };
+ linkConfig = {
+ Unmanaged = true;
+ ActivationPolicy = "manual";
+ };
+ }
+ )
);
- systemd.services =
- mapAttrs'
- (
- name:
- { environment, stateDir, ... }:
- nameValuePair "netbird-${name}" {
- description = "A WireGuard-based mesh network that connects your devices into a single private network";
+ environment.etc = toClientAttrs (
+ client:
+ nameValuePair "netbird-${client.name}/config.d/50-nixos.json" {
+ text = builtins.toJSON client.config;
+ mode = "0444";
+ }
+ );
- documentation = [ "https://netbird.io/docs/" ];
+ systemd.services = toClientAttrs (
+ client:
+ nameValuePair "netbird-${client.name}" {
+ description = "A WireGuard-based mesh network that connects your devices into a single private network";
- after = [ "network.target" ];
- wantedBy = [ "multi-user.target" ];
+ documentation = [ "https://netbird.io/docs/" ];
- path = with pkgs; [ openresolv ];
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
- inherit environment;
+ path = optional (!config.services.resolved.enable) pkgs.openresolv;
- serviceConfig = {
- ExecStart = "${getExe cfg.package} service run";
- Restart = "always";
- RuntimeDirectory = stateDir;
- StateDirectory = stateDir;
- StateDirectoryMode = "0700";
- WorkingDirectory = "/var/lib/${stateDir}";
- };
+ serviceConfig = {
+ ExecStart = "${getExe client.wrapper} service run";
+ Restart = "always";
- unitConfig = {
- StartLimitInterval = 5;
- StartLimitBurst = 10;
- };
+ RuntimeDirectory = "netbird-${client.name}";
+ RuntimeDirectoryMode = mkDefault "0755";
+ ConfigurationDirectory = "netbird-${client.name}";
+ StateDirectory = "netbird-${client.name}";
+ StateDirectoryMode = "0700";
- stopIfChanged = false;
- }
- )
- cfg.tunnels;
+ WorkingDirectory = "/var/lib/netbird-${client.name}";
+ };
+
+ unitConfig = {
+ StartLimitInterval = 5;
+ StartLimitBurst = 10;
+ };
+
+ stopIfChanged = false;
+ }
+ );
+ }
+ # Hardening section
+ (mkIf (hardenedClients != { }) {
+ users.groups = toHardenedClientAttrs (client: nameValuePair "netbird-${client.name}" { });
+ users.users = toHardenedClientAttrs (
+ client:
+ nameValuePair "netbird-${client.name}" {
+ isSystemUser = true;
+ home = "/var/lib/netbird-${client.name}";
+ group = "netbird-${client.name}";
+ }
+ );
+
+ systemd.services = toHardenedClientAttrs (
+ client:
+ nameValuePair "netbird-${client.name}" (
+ mkIf client.hardened {
+ serviceConfig = {
+ RuntimeDirectoryMode = "0750";
+
+ User = "netbird-${client.name}";
+ Group = "netbird-${client.name}";
+
+ # settings implied by DynamicUser=true, without actully using it,
+ # see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=
+ RemoveIPC = true;
+ PrivateTmp = true;
+ ProtectSystem = "strict";
+ ProtectHome = "yes";
+
+ AmbientCapabilities =
+ [
+ # see https://man7.org/linux/man-pages/man7/capabilities.7.html
+ # see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker
+ #
+ # seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE
+ # CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required,
+ # see https://github.com/netbirdio/netbird/pull/1513
+
+ # failed creating tunnel interface wt-priv: [operation not permitted
+ "CAP_NET_ADMIN"
+ # failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted
+ "CAP_NET_RAW"
+ ]
+ # required for eBPF filter, used to be subset of CAP_SYS_ADMIN
+ ++ optional (versionAtLeast kernel.version "5.8") "CAP_BPF"
+ ++ optional (versionOlder kernel.version "5.8") "CAP_SYS_ADMIN"
+ ++ optional (
+ client.dns-resolver.address != null && client.dns-resolver.port < 1024
+ ) "CAP_NET_BIND_SERVICE";
+ };
+ }
+ )
+ );
+
+ # see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43
+ # see all actions used at https://github.com/netbirdio/netbird/blob/13e7198046a0d73a9cd91bf8e063fafb3d41885c/client/internal/dns/systemd_linux.go#L29-L32
+ security.polkit.extraConfig = mkIf config.services.resolved.enable ''
+ // systemd-resolved access for Netbird clients
+ polkit.addRule(function(action, subject) {
+ var actions = [
+ "org.freedesktop.resolve1.revert",
+ "org.freedesktop.resolve1.set-default-route",
+ "org.freedesktop.resolve1.set-dns-servers",
+ "org.freedesktop.resolve1.set-domains",
+ ];
+ var users = ${builtins.toJSON (toHardenedClientList (client: "netbird-${client.name}"))};
+
+ if (actions.indexOf(action.id) >= 0 && users.indexOf(subject.user) >= 0 ) {
+ return polkit.Result.YES;
+ }
+ });
+ '';
})
+ # migration & temporary fixups section
+ {
+ systemd.services = toClientAttrs (
+ client:
+ nameValuePair "netbird-${client.name}" {
+ preStart = ''
+ set -eEuo pipefail
+ ${optionalString (client.logLevel == "trace" || client.logLevel == "debug") "set -x"}
+
+ PATH="${
+ makeBinPath (
+ with pkgs;
+ [
+ coreutils
+ jq
+ diffutils
+ ]
+ )
+ }:$PATH"
+ export ${toShellVars client.environment}
+
+ # merge /etc/netbird-${client.name}/config.d' into "$NB_CONFIG"
+ {
+ test -e "$NB_CONFIG" || echo -n '{}' > "$NB_CONFIG"
+
+ # merge config.d with "$NB_CONFIG" into "$NB_CONFIG.new"
+ jq -sS 'reduce .[] as $i ({}; . * $i)' \
+ "$NB_CONFIG" \
+ /etc/netbird-${client.name}/config.d/*.json \
+ > "$NB_CONFIG.new"
+
+ echo "Comparing $NB_CONFIG with $NB_CONFIG.new ..."
+ if ! diff <(jq -S <"$NB_CONFIG") "$NB_CONFIG.new" ; then
+ echo "Updating $NB_CONFIG ..."
+ mv "$NB_CONFIG.new" "$NB_CONFIG"
+ else
+ echo "Files are the same, not doing anything."
+ rm "$NB_CONFIG.new"
+ fi
+ }
+ '';
+ }
+ );
+ }
];
}

View file

@ -12,7 +12,7 @@ index 788b724231be3..c8f6836f0af93 100644
domain = mkOption { domain = mkOption {
type = str; type = str;
diff --git a/nixos/modules/services/networking/netbird/management.nix b/nixos/modules/services/networking/netbird/management.nix diff --git a/nixos/modules/services/networking/netbird/management.nix b/nixos/modules/services/networking/netbird/management.nix
index f4b5bbf643239..da5b851c3fb3e 100644 index f4b5bbf643239..bf95ea65e5fa3 100644
--- a/nixos/modules/services/networking/netbird/management.nix --- a/nixos/modules/services/networking/netbird/management.nix
+++ b/nixos/modules/services/networking/netbird/management.nix +++ b/nixos/modules/services/networking/netbird/management.nix
@@ -38,7 +38,7 @@ let @@ -38,7 +38,7 @@ let
@ -33,7 +33,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
Username = "netbird"; Username = "netbird";
Password = "netbird"; Password = "netbird";
} }
@@ -58,10 +58,15 @@ let @@ -58,10 +58,14 @@ let
Secret = "not-secure-secret"; Secret = "not-secure-secret";
TimeBasedCredentials = false; TimeBasedCredentials = false;
}; };
@ -46,11 +46,10 @@ index f4b5bbf643239..da5b851c3fb3e 100644
Signal = { Signal = {
Proto = "https"; Proto = "https";
- URI = "${cfg.domain}:443"; - URI = "${cfg.domain}:443";
+ URI = "localhost:${builtins.toString cfg.signal.port}";
Username = ""; Username = "";
Password = null; Password = null;
}; };
@@ -79,9 +84,9 @@ let @@ -79,9 +83,9 @@ let
}; };
HttpConfig = { HttpConfig = {
@ -62,7 +61,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
}; };
IdpManagerConfig = { IdpManagerConfig = {
@@ -128,18 +133,18 @@ let @@ -128,18 +132,18 @@ let
}; };
}; };
@ -84,7 +83,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
domain = mkOption { domain = mkOption {
type = str; type = str;
@@ -196,6 +201,12 @@ in @@ -196,6 +200,12 @@ in
description = "Internal port of the management server."; description = "Internal port of the management server.";
}; };
@ -97,7 +96,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
extraOptions = mkOption { extraOptions = mkOption {
type = listOf str; type = listOf str;
default = [ ]; default = [ ];
@@ -218,7 +229,7 @@ in @@ -218,7 +228,7 @@ in
Stuns = [ Stuns = [
{ {
Proto = "udp"; Proto = "udp";
@ -106,7 +105,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
Username = ""; Username = "";
Password = null; Password = null;
} }
@@ -228,7 +239,7 @@ in @@ -228,7 +238,7 @@ in
Turns = [ Turns = [
{ {
Proto = "udp"; Proto = "udp";
@ -115,7 +114,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
Username = "netbird"; Username = "netbird";
Password = "netbird"; Password = "netbird";
} }
@@ -241,7 +252,7 @@ in @@ -241,7 +251,7 @@ in
Signal = { Signal = {
Proto = "https"; Proto = "https";
@ -124,7 +123,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
Username = ""; Username = "";
Password = null; Password = null;
}; };
@@ -257,9 +268,9 @@ in @@ -257,9 +267,9 @@ in
StoreConfig = { Engine = "sqlite"; }; StoreConfig = { Engine = "sqlite"; };
HttpConfig = { HttpConfig = {
@ -136,7 +135,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
}; };
IdpManagerConfig = { IdpManagerConfig = {
@@ -334,11 +345,9 @@ in @@ -334,11 +344,9 @@ in
default = "INFO"; default = "INFO";
description = "Log level of the netbird services."; description = "Log level of the netbird services.";
}; };
@ -149,7 +148,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
warnings = warnings =
concatMap concatMap
( (
@@ -373,7 +382,7 @@ in @@ -373,7 +381,7 @@ in
serviceConfig = { serviceConfig = {
ExecStart = escapeSystemdExecArgs ( ExecStart = escapeSystemdExecArgs (
[ [
@ -158,7 +157,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
"management" "management"
# Config file # Config file
"--config" "--config"
@@ -383,25 +392,28 @@ in @@ -383,25 +391,28 @@ in
"${stateDir}/data" "${stateDir}/data"
# DNS domain # DNS domain
"--dns-domain" "--dns-domain"
@ -194,7 +193,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
); );
Restart = "always"; Restart = "always";
RuntimeDirectory = "netbird-mgmt"; RuntimeDirectory = "netbird-mgmt";
@@ -434,27 +446,5 @@ in @@ -434,27 +445,5 @@ in
stopIfChanged = false; stopIfChanged = false;
}; };
@ -224,7 +223,7 @@ index f4b5bbf643239..da5b851c3fb3e 100644
} }
diff --git a/nixos/modules/services/networking/netbird/proxy.nix b/nixos/modules/services/networking/netbird/proxy.nix diff --git a/nixos/modules/services/networking/netbird/proxy.nix b/nixos/modules/services/networking/netbird/proxy.nix
new file mode 100644 new file mode 100644
index 0000000000000..998ddbad212cf index 0000000000000..440f3c1d710a3
--- /dev/null --- /dev/null
+++ b/nixos/modules/services/networking/netbird/proxy.nix +++ b/nixos/modules/services/networking/netbird/proxy.nix
@@ -0,0 +1,104 @@ @@ -0,0 +1,104 @@
@ -234,6 +233,7 @@ index 0000000000000..998ddbad212cf
+ mkEnableOption + mkEnableOption
+ mkIf + mkIf
+ mkOption + mkOption
+ mkDefault
+ ; + ;
+ inherit (lib.types) str; + inherit (lib.types) str;
+ cfg = config.services.netbird.server.proxy; + cfg = config.services.netbird.server.proxy;
@ -274,10 +274,19 @@ index 0000000000000..998ddbad212cf
+ enable = true; + enable = true;
+ +
+ virtualHosts.${cfg.domain} = { + virtualHosts.${cfg.domain} = {
+ forceSSL = mkDefault true;
+ extraConfig = ''
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header X-Forwarded-Proto https;
+ proxy_set_header X-Forwarded-Host $host;
+ grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ '';
+ locations = { + locations = {
+ "/" = { + "/" = {
+ proxyPass = "http://${cfg.dashboardAddress}"; + proxyPass = "http://${cfg.dashboardAddress}";
+ proxyWebSockets = true; + proxyWebsockets = true;
+ }; + };
+ "/api".proxyPass = "http://${cfg.managementAddress}"; + "/api".proxyPass = "http://${cfg.managementAddress}";
+ +
@ -286,8 +295,6 @@ index 0000000000000..998ddbad212cf
+ # see https://stackoverflow.com/a/67805465 + # see https://stackoverflow.com/a/67805465
+ client_body_timeout 1d; + client_body_timeout 1d;
+ +
+ grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ grpc_pass grpc://${cfg.managementAddress}; + grpc_pass grpc://${cfg.managementAddress};
+ grpc_read_timeout 1d; + grpc_read_timeout 1d;
+ grpc_send_timeout 1d; + grpc_send_timeout 1d;
@ -299,8 +306,6 @@ index 0000000000000..998ddbad212cf
+ # see https://stackoverflow.com/a/67805465 + # see https://stackoverflow.com/a/67805465
+ client_body_timeout 1d; + client_body_timeout 1d;
+ +
+ grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ grpc_pass grpc://${cfg.signalAddress}; + grpc_pass grpc://${cfg.signalAddress};
+ grpc_read_timeout 1d; + grpc_read_timeout 1d;
+ grpc_send_timeout 1d; + grpc_send_timeout 1d;
@ -314,12 +319,6 @@ index 0000000000000..998ddbad212cf
+ proxy_set_header Upgrade $http_upgrade; + proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade"; + proxy_set_header Connection "Upgrade";
+ +
+ # Forward headers
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # Timeout settings + # Timeout settings
+ proxy_read_timeout 3600s; + proxy_read_timeout 3600s;
+ proxy_send_timeout 3600s; + proxy_send_timeout 3600s;
@ -334,10 +333,10 @@ index 0000000000000..998ddbad212cf
+} +}
diff --git a/nixos/modules/services/networking/netbird/relay.nix b/nixos/modules/services/networking/netbird/relay.nix diff --git a/nixos/modules/services/networking/netbird/relay.nix b/nixos/modules/services/networking/netbird/relay.nix
new file mode 100644 new file mode 100644
index 0000000000000..38af7fb1b2865 index 0000000000000..01c4186505b0c
--- /dev/null --- /dev/null
+++ b/nixos/modules/services/networking/netbird/relay.nix +++ b/nixos/modules/services/networking/netbird/relay.nix
@@ -0,0 +1,124 @@ @@ -0,0 +1,123 @@
+{ +{
+ config, + config,
+ lib, + lib,
@ -419,7 +418,6 @@ index 0000000000000..38af7fb1b2865
+ config = mkIf cfg.enable { + config = mkIf cfg.enable {
+ services.netbird.server.relay.settings = { + services.netbird.server.relay.settings = {
+ NB_LISTEN_ADDRESS = mkDefault ":${builtins.toString cfg.port}"; + NB_LISTEN_ADDRESS = mkDefault ":${builtins.toString cfg.port}";
+ NB_EXPOSED_ADDRESS = mkDefault "rel://${cfg.domain}:${builtins.toString cfg.port}";
+ NB_METRICS_PORT = mkDefault "9092"; # Upstream default is 9090 but this would clash for nixos where all services run on the same host + NB_METRICS_PORT = mkDefault "9092"; # Upstream default is 9090 but this would clash for nixos where all services run on the same host
+ }; + };
+ systemd.services.netbird-relay = { + systemd.services.netbird-relay = {
@ -428,7 +426,7 @@ index 0000000000000..38af7fb1b2865
+ environment = cfg.settings; + environment = cfg.settings;
+ +
+ script = '' + script = ''
+ export NB_AUTH_SECRET="(<${cfg.authSecretFile})" + export NB_AUTH_SECRET="$(<${cfg.authSecretFile})"
+ ${getExe' cfg.package "netbird-relay"} + ${getExe' cfg.package "netbird-relay"}
+ ''; + '';
+ serviceConfig = { + serviceConfig = {
@ -463,7 +461,7 @@ index 0000000000000..38af7fb1b2865
+ }; + };
+} +}
diff --git a/nixos/modules/services/networking/netbird/server.md b/nixos/modules/services/networking/netbird/server.md diff --git a/nixos/modules/services/networking/netbird/server.md b/nixos/modules/services/networking/netbird/server.md
index 3649e97b379e5..d5c137f4fff98 100644 index 3649e97b379e5..ffda72c0626a4 100644
--- a/nixos/modules/services/networking/netbird/server.md --- a/nixos/modules/services/networking/netbird/server.md
+++ b/nixos/modules/services/networking/netbird/server.md +++ b/nixos/modules/services/networking/netbird/server.md
@@ -4,9 +4,10 @@ NetBird is a VPN built on top of WireGuard® making it easy to create secure pri @@ -4,9 +4,10 @@ NetBird is a VPN built on top of WireGuard® making it easy to create secure pri
@ -491,7 +489,7 @@ index 3649e97b379e5..d5c137f4fff98 100644
coturn = { coturn = {
enable = true; enable = true;
@@ -24,19 +28,16 @@ services.netbird.server = { @@ -24,19 +28,17 @@ services.netbird.server = {
management = { management = {
oidcConfigEndpoint = "https://sso.example.selfhosted/oauth2/openid/netbird/.well-known/openid-configuration"; oidcConfigEndpoint = "https://sso.example.selfhosted/oauth2/openid/netbird/.well-known/openid-configuration";
@ -508,6 +506,7 @@ index 3649e97b379e5..d5c137f4fff98 100644
- ]; - ];
- }; - };
- }; - };
+ settings.Signal.URI = "publicly reachable signal endpoint";
}; };
}; };
``` ```
@ -522,7 +521,7 @@ index 3649e97b379e5..d5c137f4fff98 100644
+ +
+``` +```
diff --git a/nixos/modules/services/networking/netbird/server.nix b/nixos/modules/services/networking/netbird/server.nix diff --git a/nixos/modules/services/networking/netbird/server.nix b/nixos/modules/services/networking/netbird/server.nix
index 1725374d03c6b..5d02a4017328b 100644 index 1725374d03c6b..b481770071b82 100644
--- a/nixos/modules/services/networking/netbird/server.nix --- a/nixos/modules/services/networking/netbird/server.nix
+++ b/nixos/modules/services/networking/netbird/server.nix +++ b/nixos/modules/services/networking/netbird/server.nix
@@ -16,7 +16,7 @@ in @@ -16,7 +16,7 @@ in
@ -558,7 +557,7 @@ index 1725374d03c6b..5d02a4017328b 100644
managementServer = "https://${cfg.domain}"; managementServer = "https://${cfg.domain}";
}; };
@@ -53,7 +52,6 @@ in @@ -53,31 +52,35 @@ in
{ {
domain = mkDefault cfg.domain; domain = mkDefault cfg.domain;
enable = mkDefault cfg.enable; enable = mkDefault cfg.enable;
@ -566,7 +565,13 @@ index 1725374d03c6b..5d02a4017328b 100644
} }
// (optionalAttrs cfg.coturn.enable rec { // (optionalAttrs cfg.coturn.enable rec {
turnDomain = cfg.domain; turnDomain = cfg.domain;
@@ -66,18 +64,22 @@ in turnPort = config.services.coturn.tls-listening-port;
# We cannot merge a list of attrsets so we have to redefine the whole list
settings = {
+ Signal.URI = mkDefault "${cfg.domain}:${builtins.toString cfg.signal.port}";
TURNConfig.Turns = mkDefault [
{
Proto = "udp";
URI = "turn:${turnDomain}:${builtins.toString turnPort}"; URI = "turn:${turnDomain}:${builtins.toString turnPort}";
Username = "netbird"; Username = "netbird";
Password = Password =
@ -589,7 +594,7 @@ index 1725374d03c6b..5d02a4017328b 100644
+ }; + };
+ +
+ relay = { + relay = {
+ settings.NB_EXPOSED_ADDRESS = "rel://${cfg.domain}/${builtins.toString cfg.relay.port}"; + settings.NB_EXPOSED_ADDRESS = mkDefault "rel://${cfg.domain}/${builtins.toString cfg.relay.port}";
+ enable = mkDefault cfg.enable; + enable = mkDefault cfg.enable;
}; };

View file

@ -1,5 +1,5 @@
diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix
index a368b6eee2a6e..114400de434a3 100644 index a368b6eee2a6e..3f90ad99b7700 100644
--- a/nixos/modules/services/security/kanidm.nix --- a/nixos/modules/services/security/kanidm.nix
+++ b/nixos/modules/services/security/kanidm.nix +++ b/nixos/modules/services/security/kanidm.nix
@@ -502,13 +502,13 @@ in @@ -502,13 +502,13 @@ in
@ -7,7 +7,7 @@ index a368b6eee2a6e..114400de434a3 100644
originUrl = mkOption { originUrl = mkOption {
- description = "The origin URL of the service. OAuth2 redirects will only be allowed to sites under this origin. Must end with a slash."; - description = "The origin URL of the service. OAuth2 redirects will only be allowed to sites under this origin. Must end with a slash.";
+ description = "The redirect URL of the service. By default this needs to exactly match the OAuth2 redirect target. When disabling strict-redirect-uri kanidm will only match by origin."; + description = "The redirect URL of the service. These need to exactly match the OAuth2 redirect target";
type = type =
let let
- originStrType = types.strMatching ".*://.*/$"; - originStrType = types.strMatching ".*://.*/$";

View file

@ -52,7 +52,7 @@
# Autostart compositor if on tty1 (once, don't restart after logout) # Autostart compositor if on tty1 (once, don't restart after logout)
hm.programs.zsh.initExtra = lib.mkOrder 9999 '' hm.programs.zsh.initExtra = lib.mkOrder 9999 ''
if uwsm check may-start ; then if uwsm check may-start ; then
exec systemd-cat -t uwsm_start uwsm start -S -F Hyperland exec systemd-cat -t uwsm_start uwsm start -S -F Hyprland
fi fi
''; '';
} }