diff --git a/config/services/netbird.nix b/config/services/netbird.nix index a5513ed..d2a6d4b 100644 --- a/config/services/netbird.nix +++ b/config/services/netbird.nix @@ -3,8 +3,6 @@ lib, ... }: { - disabledModules = ["services/networking/netbird/server.nix"]; - imports = [../../modules/netbird/server.nix]; wireguard.elisabeth = { client.via = "elisabeth"; firewallRuleForNode.elisabeth.allowedTCPPorts = [80 3000 3001]; diff --git a/modules/netbird/coturn.nix b/modules/netbird/coturn.nix deleted file mode 100644 index 39098fa..0000000 --- a/modules/netbird/coturn.nix +++ /dev/null @@ -1,162 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - inherit - (lib) - getExe - literalExpression - mkAfter - mkEnableOption - mkIf - mkMerge - mkOption - optionalAttrs - optionalString - ; - - inherit - (lib.types) - bool - listOf - nullOr - path - port - str - ; - - cfg = config.services.netbird.server.coturn; -in { - options.services.netbird.server.coturn = { - enable = mkEnableOption "a Coturn server for Netbird, will also open the firewall on the configured range"; - - useAcmeCertificates = mkOption { - type = bool; - default = false; - description = '' - Whether to use ACME certificates corresponding to the given domain for the server. - ''; - }; - - domain = mkOption { - type = str; - description = "The domain under which the coturn server runs."; - }; - - user = mkOption { - type = str; - default = "netbird"; - description = '' - The username used by netbird to connect to the coturn server. - ''; - }; - - password = mkOption { - type = nullOr str; - default = null; - description = '' - The password of the user used by netbird to connect to the coturn server. - Be advised this will be world readable in the nix store. - ''; - }; - - passwordFile = mkOption { - type = nullOr path; - default = null; - description = '' - The path to a file containing the password of the user used by netbird to connect to the coturn server. - ''; - }; - - openPorts = mkOption { - type = listOf port; - default = with config.services.coturn; [ - listening-port - alt-listening-port - tls-listening-port - alt-tls-listening-port - ]; - defaultText = literalExpression '' - with config.services.coturn; [ - listening-port - alt-listening-port - tls-listening-port - alt-tls-listening-port - ]; - ''; - - description = '' - The list of ports used by coturn for listening to open in the firewall. - ''; - }; - }; - - config = mkIf cfg.enable (mkMerge [ - { - assertions = [ - { - assertion = (cfg.password == null) != (cfg.passwordFile == null); - message = "Exactly one of `password` or `passwordFile` must be given for the coturn setup."; - } - ]; - - services.coturn = - { - enable = true; - - realm = cfg.domain; - lt-cred-mech = true; - no-cli = true; - - extraConfig = '' - fingerprint - user=${cfg.user}:${ - if cfg.password != null - then cfg.password - else "@password@" - } - no-software-attribute - ''; - } - // (optionalAttrs cfg.useAcmeCertificates { - cert = "@cert@"; - pkey = "@pkey@"; - }); - - systemd.services.coturn = let - dir = config.security.acme.certs.${cfg.domain}.directory; - preStart' = - (optionalString (cfg.passwordFile != null) '' - ${getExe pkgs.replace-secret} @password@ ${cfg.passwordFile} /run/coturn/turnserver.cfg - '') - + (optionalString cfg.useAcmeCertificates '' - ${getExe pkgs.replace-secret} @cert@ "$CREDENTIALS_DIRECTORY/cert.pem" /run/coturn/turnserver.cfg - ${getExe pkgs.replace-secret} @pkey@ "$CREDENTIALS_DIRECTORY/pkey.pem" /run/coturn/turnserver.cfg - ''); - in - (optionalAttrs (preStart' != "") {preStart = mkAfter preStart';}) - // (optionalAttrs cfg.useAcmeCertificates { - serviceConfig.LoadCredential = [ - "cert.pem:${dir}/fullchain.pem" - "pkey.pem:${dir}/key.pem" - ]; - }); - - security.acme.certs = mkIf cfg.useAcmeCertificates {${cfg.domain}.postRun = "systemctl restart coturn.service";}; - - networking.firewall = { - allowedUDPPorts = cfg.openPorts; - allowedTCPPorts = cfg.openPorts; - - allowedUDPPortRanges = with config.services.coturn; [ - { - from = min-port; - to = max-port; - } - ]; - }; - } - ]); -} diff --git a/modules/netbird/dashboard.nix b/modules/netbird/dashboard.nix deleted file mode 100644 index c275697..0000000 --- a/modules/netbird/dashboard.nix +++ /dev/null @@ -1,189 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - inherit - (lib) - boolToString - concatStringsSep - hasAttr - isBool - mapAttrs - mkDefault - mkEnableOption - mkIf - mkOption - mkPackageOption - ; - - inherit - (lib.types) - attrsOf - bool - either - package - str - submodule - ; - - toStringEnv = value: - if isBool value - then boolToString value - else toString value; - - cfg = config.services.netbird.server.dashboard; -in { - options.services.netbird.server.dashboard = { - enable = mkEnableOption "the static netbird dashboard frontend"; - - package = mkPackageOption pkgs "netbird-dashboard" {}; - - enableNginx = mkEnableOption "Nginx reverse-proxy to serve the dashboard."; - - domain = mkOption { - type = str; - default = "localhost"; - description = "The domain under which the dashboard runs."; - }; - - managementServer = mkOption { - type = str; - description = "The address of the management server, used for the API endpoints."; - }; - - settings = mkOption { - type = submodule {freeformType = attrsOf (either str bool);}; - - defaultText = '' - { - AUTH_AUDIENCE = "netbird"; - AUTH_CLIENT_ID = "netbird"; - AUTH_SUPPORTED_SCOPES = "openid profile email"; - NETBIRD_TOKEN_SOURCE = "idToken"; - USE_AUTH0 = false; - } - ''; - - description = '' - An attribute set that will be used to substitute variables when building the dashboard. - Any values set here will be templated into the frontend and be public for anyone that can reach your website. - The exact values sadly aren't documented anywhere. - A starting point when searching for valid values is this [script](https://github.com/netbirdio/dashboard/blob/main/docker/init_react_envs.sh) - The only mandatory value is 'AUTH_AUTHORITY' as we cannot set a default value here. - ''; - }; - - finalDrv = mkOption { - readOnly = true; - type = package; - description = '' - The derivation containing the final templated dashboard. - ''; - }; - }; - - config = mkIf cfg.enable { - assertions = [ - { - assertion = hasAttr "AUTH_AUTHORITY" cfg.settings; - message = "The setting AUTH_AUTHORITY is required for the dasboard to function."; - } - ]; - - services.netbird.server.dashboard = { - settings = - { - # Due to how the backend and frontend work this secret will be templated into the backend - # and then served statically from your website - # This enables you to login without the normally needed indirection through the backend - # but this also means anyone that can reach your website can - # fetch this secret, which is why there is no real need to put it into - # special options as its public anyway - # As far as I know leaking this secret is just - # an information leak as one can fetch some basic app - # informations from the IDP - # To actually do something one still needs to have login - # data and this secret so this being public will not - # suffice for anything just decreasing security - AUTH_CLIENT_SECRET = ""; - - NETBIRD_MGMT_API_ENDPOINT = cfg.managementServer; - NETBIRD_MGMT_GRPC_API_ENDPOINT = cfg.managementServer; - } - // (mapAttrs (_: mkDefault) { - # Those values have to be easily overridable - AUTH_AUDIENCE = "netbird"; # must be set for your devices to be able to log in - AUTH_CLIENT_ID = "netbird"; - AUTH_SUPPORTED_SCOPES = "openid profile email"; - NETBIRD_TOKEN_SOURCE = "idToken"; - USE_AUTH0 = false; - }); - - # The derivation containing the templated dashboard - finalDrv = - pkgs.runCommand "netbird-dashboard" - { - nativeBuildInputs = [pkgs.gettext]; - env = - { - ENV_STR = concatStringsSep " " [ - "$AUTH_AUDIENCE" - "$AUTH_AUTHORITY" - "$AUTH_CLIENT_ID" - "$AUTH_CLIENT_SECRET" - "$AUTH_REDIRECT_URI" - "$AUTH_SILENT_REDIRECT_URI" - "$AUTH_SUPPORTED_SCOPES" - "$NETBIRD_DRAG_QUERY_PARAMS" - "$NETBIRD_GOOGLE_ANALYTICS_ID" - "$NETBIRD_HOTJAR_TRACK_ID" - "$NETBIRD_MGMT_API_ENDPOINT" - "$NETBIRD_MGMT_GRPC_API_ENDPOINT" - "$NETBIRD_TOKEN_SOURCE" - "$USE_AUTH0" - ]; - } - // (mapAttrs (_: toStringEnv) cfg.settings); - } - '' - cp -R ${cfg.package} build - - find build -type d -exec chmod 755 {} \; - OIDC_TRUSTED_DOMAINS="build/OidcTrustedDomains.js" - - envsubst "$ENV_STR" < "$OIDC_TRUSTED_DOMAINS.tmpl" > "$OIDC_TRUSTED_DOMAINS" - - for f in $(grep -R -l AUTH_SUPPORTED_SCOPES build/); do - mv "$f" "$f.copy" - envsubst "$ENV_STR" < "$f.copy" > "$f" - rm "$f.copy" - done - - cp -R build $out - ''; - }; - - services.nginx = mkIf cfg.enableNginx { - enable = true; - - virtualHosts.${cfg.domain} = { - locations = { - "/" = { - root = cfg.finalDrv; - tryFiles = "$uri $uri.html $uri/ =404"; - }; - - "/404.html".extraConfig = '' - internal; - ''; - }; - - extraConfig = '' - error_page 404 /404.html; - ''; - }; - }; - }; -} diff --git a/modules/netbird/management.nix b/modules/netbird/management.nix deleted file mode 100644 index 7306a85..0000000 --- a/modules/netbird/management.nix +++ /dev/null @@ -1,461 +0,0 @@ -{ - config, - lib, - pkgs, - utils, - ... -}: let - inherit - (lib) - any - concatMap - getExe' - literalExpression - mkEnableOption - mkIf - mkOption - mkPackageOption - optional - recursiveUpdate - ; - - inherit - (lib.types) - bool - enum - listOf - port - str - ; - - inherit (utils) escapeSystemdExecArgs genJqSecretsReplacementSnippet; - - stateDir = "/var/lib/netbird-mgmt"; - - settingsFormat = pkgs.formats.json {}; - - defaultSettings = { - Stuns = [ - { - Proto = "udp"; - URI = "stun:${cfg.turnDomain}:3478"; - Username = ""; - Password = null; - } - ]; - - TURNConfig = { - Turns = [ - { - Proto = "udp"; - URI = "turn:${cfg.turnDomain}:${builtins.toString cfg.turnPort}"; - Username = "netbird"; - Password = "netbird"; - } - ]; - - CredentialsTTL = "12h"; - Secret = "not-secure-secret"; - TimeBasedCredentials = false; - }; - - Signal = { - Proto = "https"; - URI = "${cfg.domain}:443"; - Username = ""; - Password = null; - }; - - ReverseProxy = { - TrustedHTTPProxies = []; - TrustedHTTPProxiesCount = 0; - TrustedPeers = ["0.0.0.0/0"]; - }; - - Datadir = "${stateDir}/data"; - DataStoreEncryptionKey = "very-insecure-key"; - StoreConfig = { - Engine = "sqlite"; - }; - - HttpConfig = { - Address = "127.0.0.1:${builtins.toString cfg.port}"; - IdpSignKeyRefreshEnabled = true; - OIDCConfigEndpoint = cfg.oidcConfigEndpoint; - }; - - IdpManagerConfig = { - ManagerType = "none"; - ClientConfig = { - Issuer = ""; - TokenEndpoint = ""; - ClientID = "netbird"; - ClientSecret = ""; - GrantType = "client_credentials"; - }; - - ExtraConfig = {}; - Auth0ClientCredentials = null; - AzureClientCredentials = null; - KeycloakClientCredentials = null; - ZitadelClientCredentials = null; - }; - - DeviceAuthorizationFlow = { - Provider = "none"; - ProviderConfig = { - Audience = "netbird"; - Domain = null; - ClientID = "netbird"; - TokenEndpoint = null; - DeviceAuthEndpoint = ""; - Scope = "openid profile email"; - UseIDToken = false; - }; - }; - - PKCEAuthorizationFlow = { - ProviderConfig = { - Audience = "netbird"; - ClientID = "netbird"; - ClientSecret = ""; - AuthorizationEndpoint = ""; - TokenEndpoint = ""; - Scope = "openid profile email"; - RedirectURLs = ["http://localhost:53000"]; - UseIDToken = false; - }; - }; - }; - - managementConfig = recursiveUpdate defaultSettings cfg.settings; - - managementFile = settingsFormat.generate "config.json" managementConfig; - - cfg = config.services.netbird.server.management; -in { - options.services.netbird.server.management = { - enable = mkEnableOption "Netbird Management Service."; - - package = mkPackageOption pkgs "netbird" {}; - - domain = mkOption { - type = str; - description = "The domain under which the management API runs."; - }; - - turnDomain = mkOption { - type = str; - description = "The domain of the TURN server to use."; - }; - - turnPort = mkOption { - type = port; - default = 3478; - description = '' - The port of the TURN server to use. - ''; - }; - - dnsDomain = mkOption { - type = str; - default = "netbird.selfhosted"; - description = "Domain used for peer resolution."; - }; - - singleAccountModeDomain = mkOption { - type = str; - default = "netbird.selfhosted"; - description = '' - Enables single account mode. - This means that all the users will be under the same account grouped by the specified domain. - If the installation has more than one account, the property is ineffective. - ''; - }; - - disableAnonymousMetrics = mkOption { - type = bool; - default = true; - description = "Disables push of anonymous usage metrics to NetBird."; - }; - - disableSingleAccountMode = mkOption { - type = bool; - default = false; - description = '' - If set to true, disables single account mode. - The `singleAccountModeDomain` property will be ignored and every new user will have a separate NetBird account. - ''; - }; - - port = mkOption { - type = port; - default = 8011; - description = "Internal port of the management server."; - }; - - extraOptions = mkOption { - type = listOf str; - default = []; - description = '' - Additional options given to netbird-mgmt as commandline arguments. - ''; - }; - - oidcConfigEndpoint = mkOption { - type = str; - description = "The oidc discovery endpoint."; - example = "https://example.eu.auth0.com/.well-known/openid-configuration"; - }; - - settings = mkOption { - inherit (settingsFormat) type; - - defaultText = literalExpression '' - defaultSettings = { - Stuns = [ - { - Proto = "udp"; - URI = "stun:''${cfg.turnDomain}:3478"; - Username = ""; - Password = null; - } - ]; - - TURNConfig = { - Turns = [ - { - Proto = "udp"; - URI = "turn:''${cfg.turnDomain}:3478"; - Username = "netbird"; - Password = "netbird"; - } - ]; - - CredentialsTTL = "12h"; - Secret = "not-secure-secret"; - TimeBasedCredentials = false; - }; - - Signal = { - Proto = "https"; - URI = "''${cfg.domain}:443"; - Username = ""; - Password = null; - }; - - ReverseProxy = { - TrustedHTTPProxies = [ ]; - TrustedHTTPProxiesCount = 0; - TrustedPeers = [ "0.0.0.0/0" ]; - }; - - Datadir = "''${stateDir}/data"; - DataStoreEncryptionKey = "genEVP6j/Yp2EeVujm0zgqXrRos29dQkpvX0hHdEUlQ="; - StoreConfig = { Engine = "sqlite"; }; - - HttpConfig = { - Address = "127.0.0.1:''${builtins.toString cfg.port}"; - IdpSignKeyRefreshEnabled = true; - OIDCConfigEndpoint = cfg.oidcConfigEndpoint; - }; - - IdpManagerConfig = { - ManagerType = "none"; - ClientConfig = { - Issuer = ""; - TokenEndpoint = ""; - ClientID = "netbird"; - ClientSecret = ""; - GrantType = "client_credentials"; - }; - - ExtraConfig = { }; - Auth0ClientCredentials = null; - AzureClientCredentials = null; - KeycloakClientCredentials = null; - ZitadelClientCredentials = null; - }; - - DeviceAuthorizationFlow = { - Provider = "none"; - ProviderConfig = { - Audience = "netbird"; - Domain = null; - ClientID = "netbird"; - TokenEndpoint = null; - DeviceAuthEndpoint = ""; - Scope = "openid profile email offline_access api"; - UseIDToken = false; - }; - }; - - PKCEAuthorizationFlow = { - ProviderConfig = { - Audience = "netbird"; - ClientID = "netbird"; - ClientSecret = ""; - AuthorizationEndpoint = ""; - TokenEndpoint = ""; - Scope = "openid profile email offline_access api"; - RedirectURLs = "http://localhost:53000"; - UseIDToken = false; - }; - }; - }; - ''; - - default = {}; - - description = '' - Configuration of the netbird management server. - Options containing secret data should be set to an attribute set containing the attribute _secret - - a string pointing to a file containing the value the option should be set to. - See the example to get a better picture of this: in the resulting management.json file, - the `DataStoreEncryptionKey` key will be set to the contents of the /run/agenix/netbird_mgmt-data_store_encryption_key file. - ''; - - example = { - DataStoreEncryptionKey = { - _secret = "/run/agenix/netbird_mgmt-data_store_encryption_key"; - }; - }; - }; - - logLevel = mkOption { - type = enum [ - "ERROR" - "WARN" - "INFO" - "DEBUG" - ]; - default = "INFO"; - description = "Log level of the netbird services."; - }; - - enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird management service."; - }; - - config = mkIf cfg.enable { - warnings = - concatMap - ( - { - check, - name, - }: - optional check "${name} is world-readable in the Nix Store, you should provide it as a _secret." - ) - [ - { - check = builtins.isString managementConfig.TURNConfig.Secret; - name = "The TURNConfig.secret"; - } - { - check = builtins.isString managementConfig.DataStoreEncryptionKey; - name = "The DataStoreEncryptionKey"; - } - { - check = any (T: (T ? Password) && builtins.isString T.Password) managementConfig.TURNConfig.Turns; - name = "A Turn configuration's password"; - } - ]; - - systemd.services.netbird-management = { - description = "The management server for Netbird, a wireguard VPN"; - documentation = ["https://netbird.io/docs/"]; - - after = ["network.target"]; - wantedBy = ["multi-user.target"]; - restartTriggers = [managementFile]; - - preStart = genJqSecretsReplacementSnippet managementConfig "${stateDir}/management.json"; - - serviceConfig = { - ExecStart = escapeSystemdExecArgs ( - [ - (getExe' cfg.package "netbird-mgmt") - "management" - # Config file - "--config" - "${stateDir}/management.json" - # Data directory - "--datadir" - "${stateDir}/data" - # DNS domain - "--dns-domain" - cfg.dnsDomain - # Port to listen on - "--port" - cfg.port - # Log to stdout - "--log-file" - "console" - # Log level - "--log-level" - cfg.logLevel - # - "--idp-sign-key-refresh-enabled" - # Domain for internal resolution - "--single-account-mode-domain" - cfg.singleAccountModeDomain - ] - ++ (optional cfg.disableAnonymousMetrics "--disable-anonymous-metrics") - ++ (optional cfg.disableSingleAccountMode "--disable-single-account-mode") - ++ cfg.extraOptions - ); - Restart = "always"; - RuntimeDirectory = "netbird-mgmt"; - StateDirectory = [ - "netbird-mgmt" - "netbird-mgmt/data" - ]; - WorkingDirectory = stateDir; - - # hardening - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateMounts = true; - PrivateTmp = true; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectSystem = true; - RemoveIPC = true; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - }; - - stopIfChanged = false; - }; - - services.nginx = mkIf cfg.enableNginx { - enable = true; - - virtualHosts.${cfg.domain} = { - locations = { - "/api".proxyPass = "http://localhost:${builtins.toString cfg.port}"; - - "/management.ManagementService/".extraConfig = '' - # This is necessary so that grpc connections do not get closed early - # see https://stackoverflow.com/a/67805465 - client_body_timeout 1d; - - grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - grpc_pass grpc://localhost:${builtins.toString cfg.port}; - grpc_read_timeout 1d; - grpc_send_timeout 1d; - grpc_socket_keepalive on; - ''; - }; - }; - }; - }; -} diff --git a/modules/netbird/server.md b/modules/netbird/server.md deleted file mode 100644 index 3649e97..0000000 --- a/modules/netbird/server.md +++ /dev/null @@ -1,42 +0,0 @@ -# Netbird server {#module-services-netbird-server} - -NetBird is a VPN built on top of WireGuard® making it easy to create secure private networks for your organization or home. - -## Quickstart {#module-services-netbird-server-quickstart} - -To fully setup Netbird as a self-hosted server, we need both a Coturn server and an identity provider, the list of supported SSOs and their setup are available [on Netbird's documentation](https://docs.netbird.io/selfhosted/selfhosted-guide#step-3-configure-identity-provider-idp). - -There are quite a few settings that need to be passed to Netbird for it to function, and a minimal config looks like : - -```nix -services.netbird.server = { - enable = true; - - domain = "netbird.example.selfhosted"; - - enableNginx = true; - - coturn = { - enable = true; - - passwordFile = "/path/to/a/secret/password"; - }; - - management = { - oidcConfigEndpoint = "https://sso.example.selfhosted/oauth2/openid/netbird/.well-known/openid-configuration"; - - settings = { - TURNConfig = { - Turns = [ - { - Proto = "udp"; - URI = "turn:netbird.example.selfhosted:3478"; - Username = "netbird"; - Password._secret = "/path/to/a/secret/password"; - } - ]; - }; - }; - }; -}; -``` diff --git a/modules/netbird/server.nix b/modules/netbird/server.nix deleted file mode 100644 index 40ff29b..0000000 --- a/modules/netbird/server.nix +++ /dev/null @@ -1,89 +0,0 @@ -{ - config, - lib, - ... -}: let - inherit - (lib) - mkDefault - mkEnableOption - mkIf - mkOption - optionalAttrs - ; - - inherit (lib.types) str; - - cfg = config.services.netbird.server; -in { - meta = { - maintainers = with lib.maintainers; [thubrecht patrickdag]; - doc = ./server.md; - }; - - # Import the separate components - imports = [ - ./coturn.nix - ./dashboard.nix - ./management.nix - ./signal.nix - ]; - - options.services.netbird.server = { - enable = mkEnableOption "Netbird Server stack, comprising the dashboard, management API and signal service"; - - enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird server services."; - - domain = mkOption { - type = str; - description = "The domain under which the netbird server runs."; - }; - }; - - config = mkIf cfg.enable { - services.netbird.server = { - dashboard = { - domain = mkDefault cfg.domain; - enable = mkDefault cfg.enable; - enableNginx = mkDefault cfg.enableNginx; - - managementServer = "https://${cfg.domain}"; - }; - - management = - { - domain = mkDefault cfg.domain; - enable = mkDefault cfg.enable; - enableNginx = mkDefault cfg.enableNginx; - } - // (optionalAttrs cfg.coturn.enable rec { - turnDomain = cfg.domain; - turnPort = config.services.coturn.tls-listening-port; - # We cannot merge a list of attrsets so we have to redefine the whole list - settings = { - TURNConfig.Turns = mkDefault [ - { - Proto = "udp"; - URI = "turn:${turnDomain}:${builtins.toString turnPort}"; - Username = "netbird"; - Password = - if (cfg.coturn.password != null) - then cfg.coturn.password - else {_secret = cfg.coturn.passwordFile;}; - } - ]; - }; - }); - - signal = { - domain = mkDefault cfg.domain; - enable = mkDefault cfg.enable; - enableNginx = mkDefault cfg.enableNginx; - }; - - coturn = { - domain = mkDefault cfg.domain; - }; - }; - }; -} diff --git a/modules/netbird/signal.nix b/modules/netbird/signal.nix deleted file mode 100644 index 471eb28..0000000 --- a/modules/netbird/signal.nix +++ /dev/null @@ -1,120 +0,0 @@ -{ - config, - lib, - pkgs, - utils, - ... -}: let - inherit - (lib) - getExe' - mkEnableOption - mkIf - mkPackageOption - mkOption - ; - - inherit (lib.types) enum port str; - - inherit (utils) escapeSystemdExecArgs; - - cfg = config.services.netbird.server.signal; -in { - options.services.netbird.server.signal = { - enable = mkEnableOption "Netbird's Signal Service"; - - package = mkPackageOption pkgs "netbird" {}; - - enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird signal service."; - - domain = mkOption { - type = str; - description = "The domain name for the signal service."; - }; - - port = mkOption { - type = port; - default = 8012; - description = "Internal port of the signal server."; - }; - - logLevel = mkOption { - type = enum [ - "ERROR" - "WARN" - "INFO" - "DEBUG" - ]; - default = "INFO"; - description = "Log level of the netbird signal service."; - }; - }; - - config = mkIf cfg.enable { - systemd.services.netbird-signal = { - after = ["network.target"]; - wantedBy = ["multi-user.target"]; - - serviceConfig = { - ExecStart = escapeSystemdExecArgs [ - (getExe' cfg.package "netbird-signal") - "run" - # Port to listen on - "--port" - cfg.port - # Log to stdout - "--log-file" - "console" - # Log level - "--log-level" - cfg.logLevel - ]; - - Restart = "always"; - RuntimeDirectory = "netbird-mgmt"; - StateDirectory = "netbird-mgmt"; - WorkingDirectory = "/var/lib/netbird-mgmt"; - - # hardening - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateMounts = true; - PrivateTmp = true; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectSystem = true; - RemoveIPC = true; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - }; - - stopIfChanged = false; - }; - - services.nginx = mkIf cfg.enableNginx { - enable = true; - - virtualHosts.${cfg.domain} = { - locations."/signalexchange.SignalExchange/".extraConfig = '' - # This is necessary so that grpc connections do not get closed early - # see https://stackoverflow.com/a/67805465 - client_body_timeout 1d; - - grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - grpc_pass grpc://localhost:${builtins.toString cfg.port}; - grpc_read_timeout 1d; - grpc_send_timeout 1d; - grpc_socket_keepalive on; - ''; - }; - }; - }; -}