vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / netbird / management.nix
blobf4b5bbf643239a033c5969f404e356662fca4542
2   config,
3   lib,
4   pkgs,
5   utils,
6   ...
7 }:
9 let
10   inherit (lib)
11     any
12     concatMap
13     getExe'
14     literalExpression
15     mkEnableOption
16     mkIf
17     mkOption
18     mkPackageOption
19     optional
20     recursiveUpdate
21     ;
23   inherit (lib.types)
24     bool
25     enum
26     listOf
27     port
28     str
29     ;
31   inherit (utils) escapeSystemdExecArgs genJqSecretsReplacementSnippet;
33   stateDir = "/var/lib/netbird-mgmt";
35   settingsFormat = pkgs.formats.json { };
37   defaultSettings = {
38     Stuns = [
39       {
40         Proto = "udp";
41         URI = "stun:${cfg.turnDomain}:3478";
42         Username = "";
43         Password = null;
44       }
45     ];
47     TURNConfig = {
48       Turns = [
49         {
50           Proto = "udp";
51           URI = "turn:${cfg.turnDomain}:${builtins.toString cfg.turnPort}";
52           Username = "netbird";
53           Password = "netbird";
54         }
55       ];
57       CredentialsTTL = "12h";
58       Secret = "not-secure-secret";
59       TimeBasedCredentials = false;
60     };
62     Signal = {
63       Proto = "https";
64       URI = "${cfg.domain}:443";
65       Username = "";
66       Password = null;
67     };
69     ReverseProxy = {
70       TrustedHTTPProxies = [ ];
71       TrustedHTTPProxiesCount = 0;
72       TrustedPeers = [ "0.0.0.0/0" ];
73     };
75     Datadir = "${stateDir}/data";
76     DataStoreEncryptionKey = "very-insecure-key";
77     StoreConfig = {
78       Engine = "sqlite";
79     };
81     HttpConfig = {
82       Address = "127.0.0.1:${builtins.toString cfg.port}";
83       IdpSignKeyRefreshEnabled = true;
84       OIDCConfigEndpoint = cfg.oidcConfigEndpoint;
85     };
87     IdpManagerConfig = {
88       ManagerType = "none";
89       ClientConfig = {
90         Issuer = "";
91         TokenEndpoint = "";
92         ClientID = "netbird";
93         ClientSecret = "";
94         GrantType = "client_credentials";
95       };
97       ExtraConfig = { };
98       Auth0ClientCredentials = null;
99       AzureClientCredentials = null;
100       KeycloakClientCredentials = null;
101       ZitadelClientCredentials = null;
102     };
104     DeviceAuthorizationFlow = {
105       Provider = "none";
106       ProviderConfig = {
107         Audience = "netbird";
108         Domain = null;
109         ClientID = "netbird";
110         TokenEndpoint = null;
111         DeviceAuthEndpoint = "";
112         Scope = "openid profile email";
113         UseIDToken = false;
114       };
115     };
117     PKCEAuthorizationFlow = {
118       ProviderConfig = {
119         Audience = "netbird";
120         ClientID = "netbird";
121         ClientSecret = "";
122         AuthorizationEndpoint = "";
123         TokenEndpoint = "";
124         Scope = "openid profile email";
125         RedirectURLs = [ "http://localhost:53000" ];
126         UseIDToken = false;
127       };
128     };
129   };
131   managementConfig = recursiveUpdate defaultSettings cfg.settings;
133   managementFile = settingsFormat.generate "config.json" managementConfig;
135   cfg = config.services.netbird.server.management;
139   options.services.netbird.server.management = {
140     enable = mkEnableOption "Netbird Management Service";
142     package = mkPackageOption pkgs "netbird" { };
144     domain = mkOption {
145       type = str;
146       description = "The domain under which the management API runs.";
147     };
149     turnDomain = mkOption {
150       type = str;
151       description = "The domain of the TURN server to use.";
152     };
154     turnPort = mkOption {
155       type = port;
156       default = 3478;
157       description = ''
158         The port of the TURN server to use.
159       '';
160     };
162     dnsDomain = mkOption {
163       type = str;
164       default = "netbird.selfhosted";
165       description = "Domain used for peer resolution.";
166     };
168     singleAccountModeDomain = mkOption {
169       type = str;
170       default = "netbird.selfhosted";
171       description = ''
172         Enables single account mode.
173         This means that all the users will be under the same account grouped by the specified domain.
174         If the installation has more than one account, the property is ineffective.
175       '';
176     };
178     disableAnonymousMetrics = mkOption {
179       type = bool;
180       default = true;
181       description = "Disables push of anonymous usage metrics to NetBird.";
182     };
184     disableSingleAccountMode = mkOption {
185       type = bool;
186       default = false;
187       description = ''
188         If set to true, disables single account mode.
189         The `singleAccountModeDomain` property will be ignored and every new user will have a separate NetBird account.
190       '';
191     };
193     port = mkOption {
194       type = port;
195       default = 8011;
196       description = "Internal port of the management server.";
197     };
199     extraOptions = mkOption {
200       type = listOf str;
201       default = [ ];
202       description = ''
203         Additional options given to netbird-mgmt as commandline arguments.
204       '';
205     };
207     oidcConfigEndpoint = mkOption {
208       type = str;
209       description = "The oidc discovery endpoint.";
210       example = "https://example.eu.auth0.com/.well-known/openid-configuration";
211     };
213     settings = mkOption {
214       inherit (settingsFormat) type;
216       defaultText = literalExpression ''
217         defaultSettings = {
218           Stuns = [
219             {
220               Proto = "udp";
221               URI = "stun:''${cfg.turnDomain}:3478";
222               Username = "";
223               Password = null;
224             }
225           ];
227           TURNConfig = {
228             Turns = [
229               {
230                 Proto = "udp";
231                 URI = "turn:''${cfg.turnDomain}:3478";
232                 Username = "netbird";
233                 Password = "netbird";
234               }
235             ];
237             CredentialsTTL = "12h";
238             Secret = "not-secure-secret";
239             TimeBasedCredentials = false;
240           };
242           Signal = {
243             Proto = "https";
244             URI = "''${cfg.domain}:443";
245             Username = "";
246             Password = null;
247           };
249           ReverseProxy = {
250             TrustedHTTPProxies = [ ];
251             TrustedHTTPProxiesCount = 0;
252             TrustedPeers = [ "0.0.0.0/0" ];
253           };
255           Datadir = "''${stateDir}/data";
256           DataStoreEncryptionKey = "genEVP6j/Yp2EeVujm0zgqXrRos29dQkpvX0hHdEUlQ=";
257           StoreConfig = { Engine = "sqlite"; };
259           HttpConfig = {
260             Address = "127.0.0.1:''${builtins.toString cfg.port}";
261             IdpSignKeyRefreshEnabled = true;
262             OIDCConfigEndpoint = cfg.oidcConfigEndpoint;
263           };
265           IdpManagerConfig = {
266             ManagerType = "none";
267             ClientConfig = {
268               Issuer = "";
269               TokenEndpoint = "";
270               ClientID = "netbird";
271               ClientSecret = "";
272               GrantType = "client_credentials";
273             };
275             ExtraConfig = { };
276             Auth0ClientCredentials = null;
277             AzureClientCredentials = null;
278             KeycloakClientCredentials = null;
279             ZitadelClientCredentials = null;
280           };
282           DeviceAuthorizationFlow = {
283             Provider = "none";
284             ProviderConfig = {
285               Audience = "netbird";
286               Domain = null;
287               ClientID = "netbird";
288               TokenEndpoint = null;
289               DeviceAuthEndpoint = "";
290               Scope = "openid profile email offline_access api";
291               UseIDToken = false;
292             };
293           };
295           PKCEAuthorizationFlow = {
296             ProviderConfig = {
297               Audience = "netbird";
298               ClientID = "netbird";
299               ClientSecret = "";
300               AuthorizationEndpoint = "";
301               TokenEndpoint = "";
302               Scope = "openid profile email offline_access api";
303               RedirectURLs = "http://localhost:53000";
304               UseIDToken = false;
305             };
306           };
307         };
308       '';
310       default = { };
312       description = ''
313         Configuration of the netbird management server.
314         Options containing secret data should be set to an attribute set containing the attribute _secret
315         - a string pointing to a file containing the value the option should be set to.
316         See the example to get a better picture of this: in the resulting management.json file,
317         the `DataStoreEncryptionKey` key will be set to the contents of the /run/agenix/netbird_mgmt-data_store_encryption_key file.
318       '';
320       example = {
321         DataStoreEncryptionKey = {
322           _secret = "/run/agenix/netbird_mgmt-data_store_encryption_key";
323         };
324       };
325     };
327     logLevel = mkOption {
328       type = enum [
329         "ERROR"
330         "WARN"
331         "INFO"
332         "DEBUG"
333       ];
334       default = "INFO";
335       description = "Log level of the netbird services.";
336     };
338     enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird management service";
339   };
341   config = mkIf cfg.enable {
342     warnings =
343       concatMap
344         (
345           { check, name }:
346           optional check "${name} is world-readable in the Nix Store, you should provide it as a _secret."
347         )
348         [
349           {
350             check = builtins.isString managementConfig.TURNConfig.Secret;
351             name = "The TURNConfig.secret";
352           }
353           {
354             check = builtins.isString managementConfig.DataStoreEncryptionKey;
355             name = "The DataStoreEncryptionKey";
356           }
357           {
358             check = any (T: (T ? Password) && builtins.isString T.Password) managementConfig.TURNConfig.Turns;
359             name = "A Turn configuration's password";
360           }
361         ];
363     systemd.services.netbird-management = {
364       description = "The management server for Netbird, a wireguard VPN";
365       documentation = [ "https://netbird.io/docs/" ];
367       after = [ "network.target" ];
368       wantedBy = [ "multi-user.target" ];
369       restartTriggers = [ managementFile ];
371       preStart = genJqSecretsReplacementSnippet managementConfig "${stateDir}/management.json";
373       serviceConfig = {
374         ExecStart = escapeSystemdExecArgs (
375           [
376             (getExe' cfg.package "netbird-mgmt")
377             "management"
378             # Config file
379             "--config"
380             "${stateDir}/management.json"
381             # Data directory
382             "--datadir"
383             "${stateDir}/data"
384             # DNS domain
385             "--dns-domain"
386             cfg.dnsDomain
387             # Port to listen on
388             "--port"
389             cfg.port
390             # Log to stdout
391             "--log-file"
392             "console"
393             # Log level
394             "--log-level"
395             cfg.logLevel
396             #
397             "--idp-sign-key-refresh-enabled"
398             # Domain for internal resolution
399             "--single-account-mode-domain"
400             cfg.singleAccountModeDomain
401           ]
402           ++ (optional cfg.disableAnonymousMetrics "--disable-anonymous-metrics")
403           ++ (optional cfg.disableSingleAccountMode "--disable-single-account-mode")
404           ++ cfg.extraOptions
405         );
406         Restart = "always";
407         RuntimeDirectory = "netbird-mgmt";
408         StateDirectory = [
409           "netbird-mgmt"
410           "netbird-mgmt/data"
411         ];
412         WorkingDirectory = stateDir;
414         # hardening
415         LockPersonality = true;
416         MemoryDenyWriteExecute = true;
417         NoNewPrivileges = true;
418         PrivateMounts = true;
419         PrivateTmp = true;
420         ProtectClock = true;
421         ProtectControlGroups = true;
422         ProtectHome = true;
423         ProtectHostname = true;
424         ProtectKernelLogs = true;
425         ProtectKernelModules = true;
426         ProtectKernelTunables = true;
427         ProtectSystem = true;
428         RemoveIPC = true;
429         RestrictNamespaces = true;
430         RestrictRealtime = true;
431         RestrictSUIDSGID = true;
432       };
434       stopIfChanged = false;
435     };
437     services.nginx = mkIf cfg.enableNginx {
438       enable = true;
440       virtualHosts.${cfg.domain} = {
441         locations = {
442           "/api".proxyPass = "http://localhost:${builtins.toString cfg.port}";
444           "/management.ManagementService/".extraConfig = ''
445             # This is necessary so that grpc connections do not get closed early
446             # see https://stackoverflow.com/a/67805465
447             client_body_timeout 1d;
449             grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
451             grpc_pass grpc://localhost:${builtins.toString cfg.port};
452             grpc_read_timeout 1d;
453             grpc_send_timeout 1d;
454             grpc_socket_keepalive on;
455           '';
456         };
457       };
458     };
459   };