diff --git a/config/users.nix b/config/users.nix index 427ea78..8ffd1cd 100644 --- a/config/users.nix +++ b/config/users.nix @@ -43,6 +43,5 @@ unifi = uidGid 968; plugdev.gid = 967; tss = uidGid 966; - firezone-client = uidGid 965; }; } diff --git a/hosts/kroma/default.nix b/hosts/kroma/default.nix index 5b89c21..9e3a12d 100644 --- a/hosts/kroma/default.nix +++ b/hosts/kroma/default.nix @@ -1,5 +1,4 @@ { - globals, inputs, lib, minimal, @@ -89,28 +88,6 @@ # }; #}; - # FIXME: the ui is not directly accessible via environment.systemPackages - # FIXME: to control it as a user (and to allow SSO) we need to be in the netbird-home group - services.netbird.ui.enable = true; - services.netbird.clients.home = { - port = 51820; - name = "netbird-home"; - interface = "wt-home"; - autoStart = false; - openFirewall = true; - config.ServerSSHAllowed = false; - environment = rec { - NB_MANAGEMENT_URL = "https://${globals.services.netbird.domain}"; - NB_ADMIN_URL = NB_MANAGEMENT_URL; - }; - }; - environment.persistence."/persist".directories = [ - { - directory = "/var/lib/netbird-home"; - mode = "0700"; - } - ]; - programs.nix-ld.enable = true; topology.self.icon = "devices.desktop"; @@ -122,9 +99,4 @@ dockerCompat = true; defaultNetwork.settings.dns_enabled = true; }; - - services.firezone.server.domain.enable = true; - services.firezone.server.domain.enableLocalDB = true; - services.firezone.server.web.enable = true; - services.firezone.server.api.enable = true; } diff --git a/modules/default.nix b/modules/default.nix index 0db5116..260bdb2 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -8,9 +8,6 @@ ./backups.nix ./deterministic-ids.nix ./distributed-config.nix - ./firezone-relay.nix - ./firezone-gateways.nix - ./firezone-server.nix ./globals.nix ./meta.nix ./netbird-client.nix diff --git a/modules/firezone-gateways.nix b/modules/firezone-gateways.nix deleted file mode 100644 index c09cca8..0000000 --- a/modules/firezone-gateways.nix +++ /dev/null @@ -1,170 +0,0 @@ -{ - lib, - pkgs, - config, - ... -}: -let - inherit (lib) - boolToString - concatMapAttrs - flip - getExe - mkEnableOption - mkOption - mkPackageOption - types - ; -in -{ - options = { - services.firezone.gateways = mkOption { - description = '' - A set of gateway clients to deploy on this machine. Each gateway can - connect to exactly one firezone server. - ''; - default = { }; - type = types.attrsOf ( - types.submodule (gatewaysSubmod: { - options = { - package = mkPackageOption pkgs "firezone-gateway" { }; - - name = mkOption { - type = types.str; - default = gatewaysSubmod.config._module.args.name; - description = "The name of this gateway as shown in firezone"; - }; - - user = mkOption { - type = types.strMatching "^[a-zA-Z0-9_-]{1,32}$"; - default = "firezone-gw-${gatewaysSubmod.config._module.args.name}"; - description = "The DynamicUser name under which the gateway will run. Cannot exceed 32 characters."; - }; - - interface = mkOption { - type = types.strMatching "^[a-zA-Z0-9_-]{1,15}$"; - default = "tun-${gatewaysSubmod.config._module.args.name}"; - description = "The name of the TUN interface which will be created by this gateway"; - }; - - apiUrl = mkOption { - type = types.str; - example = "wss://firezone.example.com/api"; - description = '' - The URL of your firezone server's API. This should be the same - as your server's setting for {option}`services.firezone.server.settings.api.externalUrl`, - but with `wss://` instead of `https://`. - ''; - }; - - tokenFile = mkOption { - type = types.path; - example = "/run/secrets/firezone-gateway-token"; - description = '' - A file containing the firezone gateway token. Do not use a nix-store path here - as it will make the token publicly readable! - - This file will be passed via systemd credentials, it should only be accessible - by the root user. - ''; - }; - - logLevel = mkOption { - type = types.str; - default = "info"; - description = '' - The log level for the firezone application. See - [RUST_LOG](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) - for the format. - ''; - }; - - enableTelemetry = mkEnableOption "telemetry"; - }; - }) - ); - }; - }; - - config = { - systemd.services = flip concatMapAttrs config.services.firezone.gateways ( - gatewayName: gatewayCfg: { - "firezone-gateway-${gatewayName}" = { - description = "Gateway service for the Firezone zero-trust access platform"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - - path = [ pkgs.util-linux ]; - script = '' - # If FIREZONE_ID is not given by the user, use a persisted (or newly generated) uuid. - if [[ -z "''${FIREZONE_ID:-}" ]]; then - if [[ ! -e gateway_id ]]; then - uuidgen -r > gateway_id - fi - export FIREZONE_ID=$(< gateway_id) - fi - - export FIREZONE_TOKEN=$(< "$CREDENTIALS_DIRECTORY/firezone-token") - exec ${getExe gatewayCfg.package} - ''; - - environment = { - FIREZONE_API_URL = gatewayCfg.apiUrl; - FIREZONE_NAME = gatewayCfg.name; - FIREZONE_NO_TELEMETRY = boolToString gatewayCfg.enableTelemetry; - FIREZONE_TUN_INTERFACE = gatewayCfg.interface; - RUST_LOG = gatewayCfg.logLevel; - }; - - serviceConfig = { - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateMounts = true; - PrivateTmp = true; - PrivateUsers = false; - ProcSubset = "pid"; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectProc = "invisible"; - ProtectSystem = "strict"; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_NETLINK" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - SystemCallFilter = "@system-service"; - UMask = "077"; - - Type = "exec"; - DynamicUser = true; - User = gatewayCfg.user; - LoadCredential = [ "firezone-token:${gatewayCfg.tokenFile}" ]; - - DeviceAllow = "/dev/net/tun"; - AmbientCapabilities = [ "CAP_NET_ADMIN" ]; - CapabilityBoundingSet = [ "CAP_NET_ADMIN" ]; - - StateDirectory = "firezone-gateways/${gatewayName}"; - WorkingDirectory = "/var/lib/firezone-gateways/${gatewayName}"; - }; - }; - } - ); - }; - - meta.maintainers = with lib.maintainers; [ - oddlama - patrickdag - ]; -} diff --git a/modules/firezone-relay.nix b/modules/firezone-relay.nix deleted file mode 100644 index 702a4ab..0000000 --- a/modules/firezone-relay.nix +++ /dev/null @@ -1,177 +0,0 @@ -{ - lib, - pkgs, - config, - ... -}: -let - inherit (lib) - boolToString - getExe - mkEnableOption - mkIf - mkOption - mkPackageOption - types - ; - - cfg = config.services.firezone.relay; -in -{ - options = { - services.firezone.relay = { - enable = mkEnableOption "the firezone relay server"; - package = mkPackageOption pkgs "firezone-relay" { }; - - publicIpv4 = mkOption { - type = types.str; - description = "The public ipv4 address of this relay"; - }; - - publicIpv6 = mkOption { - type = types.str; - description = "The public ipv6 address of this relay"; - }; - - openFirewall = mkOption { - type = types.bool; - default = true; - description = "Opens up the main STUN port and the TURN allocation range."; - }; - - port = mkOption { - type = types.port; - default = 3478; - description = "The port to listen on for STUN messages"; - }; - - lowestPort = mkOption { - type = types.port; - default = 49152; - description = "The lowest port to use in TURN allocation"; - }; - - highestPort = mkOption { - type = types.port; - default = 65535; - description = "The highest port to use in TURN allocation"; - }; - - apiUrl = mkOption { - type = types.str; - example = "wss://firezone.example.com/api"; - description = '' - The URL of your firezone server's API. This should be the same - as your server's setting for {option}`services.firezone.server.settings.api.externalUrl`, - but with `wss://` instead of `https://`. - ''; - }; - - tokenFile = mkOption { - type = types.path; - example = "/run/secrets/firezone-relay-token"; - description = '' - A file containing the firezone relay token. Do not use a nix-store path here - as it will make the token publicly readable! - - This file will be passed via systemd credentials, it should only be accessible - by the root user. - ''; - }; - - logLevel = mkOption { - type = types.str; - default = "firezone_relay=info,firezone_tunnel=info,connlib_shared=info,tunnel_state=info,phoenix_channel=info,snownet=info,str0m=info,warn"; - description = '' - The log level for the firezone application. See - [RUST_LOG](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) - for the format. - ''; - }; - - enableTelemetry = mkEnableOption "telemetry"; - }; - }; - - config = mkIf cfg.enable { - systemd.services."firezone-relay" = { - description = "relay service for the Firezone zero-trust access platform"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - - path = [ pkgs.util-linux ]; - script = '' - # If FIREZONE_ID is not given by the user, use a persisted (or newly generated) uuid. - if [[ -z "''${FIREZONE_ID:-}" ]]; then - if [[ ! -e relay_id ]]; then - uuidgen -r > relay_id - fi - export FIREZONE_ID=$(< relay_id) - fi - - export FIREZONE_TOKEN=$(< "$CREDENTIALS_DIRECTORY/firezone-token") - exec ${getExe cfg.package} - ''; - - environment = { - FIREZONE_API_URL = cfg.apiUrl; - FIREZONE_NAME = cfg.name; - FIREZONE_TELEMETRY = boolToString cfg.enableTelemetry; - - PUBLIC_IPV4_ADDRESS = cfg.publicIpv4; - PUBLIC_IPV6_ADDRESS = cfg.publicIpv6; - - LISTEN_PORT = toString cfg.port; - LOWEST_PORT = toString cfg.lowestPort; - HIGHEST_PORT = toString cfg.highestPort; - - RUST_LOG = cfg.logLevel; - LOG_FORMAT = "human"; - }; - - serviceConfig = { - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateMounts = true; - PrivateTmp = true; - PrivateUsers = false; - ProcSubset = "pid"; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectProc = "invisible"; - ProtectSystem = "strict"; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_NETLINK" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - SystemCallFilter = "@system-service"; - UMask = "077"; - - Type = "exec"; - DynamicUser = true; - User = "firezone-relay"; - LoadCredential = [ "firezone-token:${cfg.tokenFile}" ]; - - StateDirectory = "firezone-relay"; - WorkingDirectory = "/var/lib/firezone-relay"; - }; - }; - }; - - meta.maintainers = with lib.maintainers; [ - oddlama - patrickdag - ]; -} diff --git a/modules/firezone-server.nix b/modules/firezone-server.nix deleted file mode 100644 index 62ae9d8..0000000 --- a/modules/firezone-server.nix +++ /dev/null @@ -1,948 +0,0 @@ -{ - lib, - pkgs, - config, - ... -}: -let - inherit (lib) - attrNames - boolToString - concatLines - concatLists - concatMapAttrs - concatStringsSep - filterAttrs - filterAttrsRecursive - flip - forEach - getExe - isBool - mapAttrs - mapAttrsToList - mkDefault - mkEnableOption - mkIf - mkMerge - mkOption - mkPackageOption - optionalAttrs - recursiveUpdate - subtractLists - toUpper - types - ; - - cfg = config.services.firezone.server; - jsonFormat = pkgs.formats.json { }; - availableAuthAdapters = [ - "email" - "openid_connect" - "userpass" - "token" - "google_workspace" - "microsoft_entra" - "okta" - "jumpcloud" - ]; - - # All non-secret environment variables or the given component - collectEnvironment = - component: - mapAttrs (_: v: if isBool v then boolToString v else toString v) ( - cfg.settings // cfg.${component}.settings - ); - - # All mandatory secrets which were not explicitly provided by the user will - # have to be generated, if they do not yet exist. - generateSecrets = - let - requiredSecrets = filterAttrs (_: v: v == null) cfg.settingsSecret; - in - '' - mkdir -p secrets - chmod 700 secrets - '' - + concatLines ( - forEach (attrNames requiredSecrets) (secret: '' - if [[ ! -e secrets/${secret} ]]; then - echo "Generating ${secret}" - # Some secrets like TOKENS_KEY_BASE require a value >=64 bytes. - head -c 64 /dev/urandom | base64 -w 0 > secrets/${secret} - chmod 600 secrets/${secret} - fi - '') - ); - - # All secrets given in `cfg.settingsSecret` must be loaded from a file and - # exported into the environment. Also exclude any variables that were - # overwritten by the local component settings. - loadSecretEnvironment = - component: - let - relevantSecrets = subtractLists (attrNames cfg.${component}.settings) ( - attrNames cfg.settingsSecret - ); - in - concatLines ( - forEach relevantSecrets ( - secret: - ''export ${secret}=$(< ${ - if cfg.settingsSecret.${secret} == null then - "secrets/${secret}" - else - "\"$CREDENTIALS_DIRECTORY/${secret}\"" - })'' - ) - ); - - provisionStateJson = - let - # Convert clientSecretFile options into the real counterpart - augmentedAccounts = flip mapAttrs cfg.provision.accounts ( - accountName: account: - account - // { - auth = flip mapAttrs account.auth ( - authName: auth: - recursiveUpdate auth ( - optionalAttrs (auth.adapter_config.clientSecretFile != null) { - adapter_config.client_secret = "{env:AUTH_CLIENT_SECRET_${toUpper accountName}_${toUpper authName}}"; - } - ) - ); - } - ); - in - jsonFormat.generate "provision-state.json" { - # Do not include any clientSecretFile attributes in the resulting json - accounts = filterAttrsRecursive (k: _: k != "clientSecretFile") augmentedAccounts; - }; - - commonServiceConfig = { - AmbientCapablities = [ ]; - CapabilityBoundingSet = [ ]; - LockPersonality = true; - MemoryDenyWriteExecute = true; - NoNewPrivileges = true; - PrivateMounts = true; - PrivateTmp = true; - PrivateUsers = false; - ProcSubset = "pid"; - ProtectClock = true; - ProtectControlGroups = true; - ProtectHome = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - ProtectProc = "invisible"; - ProtectSystem = "strict"; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_NETLINK" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - SystemCallArchitectures = "native"; - SystemCallFilter = "@system-service"; - UMask = "077"; - - DynamicUser = true; - User = "firezone"; - - Slice = "system-firezone.slice"; - StateDirectory = "firezone"; - WorkingDirectory = "/var/lib/firezone"; - - LoadCredential = mapAttrsToList (secretName: secretFile: "${secretName}:${secretFile}") ( - filterAttrs (_: v: v != null) cfg.settingsSecret - ); - Type = "exec"; - Restart = "on-failure"; - RestartSec = 10; - }; - - componentOptions = component: { - enable = mkEnableOption "the Firezone ${component} server"; - package = mkPackageOption pkgs "firezone-server-${component}" { }; - - settings = mkOption { - description = '' - Environment variables for this component of the Firezone server. For a - list of available variables, please refer to the [upstream definitions](https://github.com/firezone/firezone/blob/main/elixir/apps/domain/lib/domain/config/definitions.ex). - Some variables like `OUTBOUND_EMAIL_ADAPTER_OPTS` require json values - for which you can use `VAR = builtins.toJSON { /* ... */ }`. - - This component will automatically inherit all variables defined via - {option}`services.firezone.server.settings` and - {option}`services.firezone.server.settingsSecret`, but which can be - overwritten by this option. - ''; - default = { }; - type = types.submodule { - freeformType = types.attrsOf ( - types.oneOf [ - types.bool - types.float - types.int - types.str - types.path - types.package - ] - ); - }; - }; - }; -in -{ - options.services.firezone.server = { - enable = mkEnableOption "all Firezone components"; - enableLocalDB = mkEnableOption "a local postgresql database for Firezone"; - nginx.enable = mkEnableOption "nginx virtualhost definition"; - - openClusterFirewall = mkOption { - type = types.bool; - default = false; - description = '' - Opens up the erlang distribution port of all enabled components to - allow reaching the server cluster from the internet. You only need to - set this if you are actually distributing your cluster across multiple - machines. - ''; - }; - - clusterHosts = mkOption { - type = types.listOf types.str; - default = [ - "api@localhost.localdomain" - "web@localhost.localdomain" - "domain@localhost.localdomain" - ]; - description = '' - A list of components and their hosts that are part of this cluster. For - a single-machine setup, the default value will be sufficient. This - value will automatically set `ERLANG_CLUSTER_ADAPTER_CONFIG`. - - The format is `@`. - ''; - }; - - settingsSecret = mkOption { - default = { }; - description = '' - This is a convenience option which allows you to set secret values for - environment variables by specifying a file which will contain the value - at runtime. Before starting the server, the content of each file will - be loaded into the respective environment variable. - - Otherwise, this option is equivalent to - {option}`services.firezone.server.settings`. Refer to the settings - option for more information regarding the actual variables and how - filtering rules are applied for each component. - ''; - type = types.submodule { - freeformType = types.attrsOf types.path; - options = { - RELEASE_COOKIE = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a unique secret identifier for the Erlang - cluster. All Firezone components in your cluster must use the - same value. - - If this is `null`, a shared value will automatically be generated - on startup and used for all components on this machine. You do - not need to set this except when you spread your cluster over - multiple hosts. - ''; - }; - - TOKENS_KEY_BASE = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a unique base64 encoded secret for the - `TOKENS_KEY_BASE`. All Firezone components in your cluster must - use the same value. - - If this is `null`, a shared value will automatically be generated - on startup and used for all components on this machine. You do - not need to set this except when you spread your cluster over - multiple hosts. - ''; - }; - - SECRET_KEY_BASE = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a unique base64 encoded secret for the - `SECRET_KEY_BASE`. All Firezone components in your cluster must - use the same value. - - If this is `null`, a shared value will automatically be generated - on startup and used for all components on this machine. You do - not need to set this except when you spread your cluster over - multiple hosts. - ''; - }; - - TOKENS_SALT = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a unique base64 encoded secret for the - `TOKENS_SALT`. All Firezone components in your cluster must - use the same value. - - If this is `null`, a shared value will automatically be generated - on startup and used for all components on this machine. You do - not need to set this except when you spread your cluster over - multiple hosts. - ''; - }; - - LIVE_VIEW_SIGNING_SALT = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a unique base64 encoded secret for the - `LIVE_VIEW_SIGNING_SALT`. All Firezone components in your cluster must - use the same value. - - If this is `null`, a shared value will automatically be generated - on startup and used for all components on this machine. You do - not need to set this except when you spread your cluster over - multiple hosts. - ''; - }; - - COOKIE_SIGNING_SALT = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a unique base64 encoded secret for the - `COOKIE_SIGNING_SALT`. All Firezone components in your cluster must - use the same value. - - If this is `null`, a shared value will automatically be generated - on startup and used for all components on this machine. You do - not need to set this except when you spread your cluster over - multiple hosts. - ''; - }; - - COOKIE_ENCRYPTION_SALT = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a unique base64 encoded secret for the - `COOKIE_ENCRYPTION_SALT`. All Firezone components in your cluster must - use the same value. - - If this is `null`, a shared value will automatically be generated - on startup and used for all components on this machine. You do - not need to set this except when you spread your cluster over - multiple hosts. - ''; - }; - }; - }; - }; - - settings = mkOption { - description = '' - Environment variables for the Firezone server. For a list of available - variables, please refer to the [upstream definitions](https://github.com/firezone/firezone/blob/main/elixir/apps/domain/lib/domain/config/definitions.ex). - Some variables like `OUTBOUND_EMAIL_ADAPTER_OPTS` require json values - for which you can use `VAR = builtins.toJSON { /* ... */ }`. - - Each component has an additional `settings` option which allows you to - override specific variables passed to that component. - ''; - default = { }; - type = types.submodule { - freeformType = types.attrsOf ( - types.oneOf [ - types.bool - types.float - types.int - types.str - types.path - types.package - ] - ); - }; - }; - - smtp = { - configureManually = mkOption { - type = types.bool; - default = false; - description = '' - Outbound email configuration is mandatory for Firezone and supports - many different delivery adapters. Yet, most users will only need an - SMTP relay to send emails, so this configuration enforced by default. - - If you want to utilize an alternative way to send emails (e.g. via a - supportd API-based service), enable this option and define - `OUTBOUND_EMAIL_FROM`, `OUTBOUND_EMAIL_ADAPTER` and - `OUTBOUND_EMAIL_ADAPTER_OPTS` manually via - {option}`services.firezone.server.settings` and/or - {option}`services.firezone.server.settingsSecret`. - - The Firezone documentation holds [a list of supported Swoosh adapters](https://github.com/firezone/firezone/blob/main/website/src/app/docs/reference/env-vars/readme.mdx#outbound-emails). - ''; - }; - - from = mkOption { - type = types.str; - example = "firezone@example.com"; - description = "Outbound SMTP FROM address"; - }; - - host = mkOption { - type = types.str; - example = "mail.example.com"; - description = "Outbound SMTP host"; - }; - - port = mkOption { - type = types.port; - example = 465; - description = "Outbound SMTP port"; - }; - - implicitTls = mkOption { - type = types.bool; - default = false; - description = "Whether to use implicit TLS instead of STARTTLS (usually port 465)"; - }; - - username = mkOption { - type = types.str; - example = "firezone@example.com"; - description = "Username to authenticate against the SMTP relay"; - }; - - passwordFile = mkOption { - type = types.path; - example = "/run/secrets/smtp-password"; - description = "File containing the password for the given username. Beware that a file in the nix store will be world readable."; - }; - }; - - domain = componentOptions "domain"; - - web = componentOptions "web" // { - externalUrl = mkOption { - type = types.strMatching "^https://.+/$"; - example = "https://firezone.example.com/"; - description = '' - The external URL under which you will serve the web interface. You - need to setup a reverse proxy for TLS termination, either with - {option}`services.firezone.server.nginx.enable` or manually. - ''; - }; - - address = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "The address to listen on"; - }; - - port = mkOption { - type = types.port; - default = 8080; - description = "The port under which the web interface will be served locally"; - }; - - trustedProxies = mkOption { - type = types.listOf types.str; - default = [ ]; - description = "A list of trusted proxies"; - }; - }; - - api = componentOptions "api" // { - externalUrl = mkOption { - type = types.strMatching "^https://.+/$"; - example = "https://firezone.example.com/api/"; - description = '' - The external URL under which you will serve the api. You need to - setup a reverse proxy for TLS termination, either with - {option}`services.firezone.server.nginx.enable` or manually. - ''; - }; - - address = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "The address to listen on"; - }; - - port = mkOption { - type = types.port; - default = 8081; - description = "The port under which the api will be served locally"; - }; - - trustedProxies = mkOption { - type = types.listOf types.str; - default = [ ]; - description = "A list of trusted proxies"; - }; - }; - - provision = { - enable = mkEnableOption "provisioning of the Firezone domain server"; - accounts = mkOption { - type = types.attrsOf ( - types.submodule { - freeformType = jsonFormat.type; - options = { - name = mkOption { - type = types.str; - description = "The account name"; - example = "My Organization"; - }; - - features = - let - mkFeatureOption = - name: default: - mkOption { - type = types.bool; - inherit default; - description = "Whether to enable the `${name}` feature for this account."; - }; - in - { - flow_activities = mkFeatureOption "flow_activities" true; - policy_conditions = mkFeatureOption "policy_conditions" true; - multi_site_resources = mkFeatureOption "multi_site_resources" true; - traffic_filters = mkFeatureOption "traffic_filters" true; - self_hosted_relays = mkFeatureOption "self_hosted_relays" true; - idp_sync = mkFeatureOption "idp_sync" true; - rest_api = mkFeatureOption "rest_api" true; - internet_resource = mkFeatureOption "internet_resource" true; - }; - - actors = mkOption { - type = types.attrsOf ( - types.submodule { - freeformType = jsonFormat.type; - options = { - type = mkOption { - type = types.enum [ - "account_admin_user" - "account_user" - "service_account" - "api_client" - ]; - description = "The account type"; - }; - - email = mkOption { - type = types.str; - description = "The email address used to authenticate as this account"; - }; - }; - } - ); - default = { }; - example = { - admin = { - type = "account_admin_user"; - email = "admin@myorg.example.com"; - }; - }; - description = "All actors (users) to provision."; - }; - - auth = mkOption { - type = types.attrsOf ( - types.submodule { - freeformType = jsonFormat.type; - options = { - adapter = mkOption { - type = types.enum availableAuthAdapters; - description = "The auth adapter type"; - }; - - adapter_config.clientSecretFile = mkOption { - type = types.nullOr types.path; - default = null; - description = '' - A file containing a the client secret for an openid_connect adapter. - You only need to set this if this is an openid_connect provider. - ''; - }; - }; - } - ); - default = { }; - example = { - myoidcprovider = { - adapter = "openid_connect"; - adapter_config = { - client_id = "clientid"; - clientSecretFile = "/run/secrets/oidc-client-secret"; - response_type = "code"; - scope = "openid email name"; - discorvery_document_uri = "https://auth.example.com/.well-known/openid-configuration"; - }; - }; - }; - description = "All authentication providers to provision."; - }; - }; - } - ); - default = { }; - example = { - main = { - name = "My Account / Organization"; - metadata.stripe.billing_email = "org@myorg.example.com"; - features.rest_api = false; - }; - }; - description = '' - All accounts to provision. The key specified here will become the - account slug. By using `"{file:/path/to/file}"` as a string value - anywhere in these settings, the provisioning script will replace that - value with the content of the given file at runtime. - - Please refer to the [Firezone source code](https://github.com/firezone/firezone/blob/main/elixir/apps/domain/lib/domain/accounts/account.ex) - for all available properties. - ''; - }; - }; - }; - - config = mkMerge [ - { - assertions = - [ - { - assertion = cfg.provision.enable -> cfg.domain.enable; - message = "Provisioning must be done on a machine running the firezone domain server"; - } - ] - ++ concatLists ( - flip mapAttrsToList cfg.provision.accounts ( - accountName: accountCfg: - [ - { - assertion = (builtins.match "^[[:lower:]_-]+$" accountName) != null; - message = "An account name must contain only lowercase characters and underscores, as it will be used as the URL slug for this account."; - } - ] - ++ flip mapAttrsToList accountCfg.auth ( - authName: _: { - assertion = (builtins.match "^[[:alnum:]_-]+$" authName) != null; - message = "An authentication provider name must contain only letters, numbers, underscores or dashes."; - } - ) - ) - ); - } - # Enable all components if the main server is enabled - (mkIf cfg.enable { - services.firezone.server.domain.enable = true; - services.firezone.server.web.enable = true; - services.firezone.server.api.enable = true; - }) - # Create (and configure) a local database if desired - (mkIf cfg.enableLocalDB { - services.postgresql = { - enable = true; - ensureUsers = [ - { - name = "firezone"; - ensureDBOwnership = true; - } - ]; - ensureDatabases = [ "firezone" ]; - }; - - services.firezone.server.settings = { - DATABASE_SOCKET_DIR = "/run/postgresql"; - DATABASE_PORT = "5432"; - DATABASE_NAME = "firezone"; - DATABASE_USER = "firezone"; - }; - }) - # Create a local nginx reverse proxy - (mkIf cfg.nginx.enable { - services.nginx = mkMerge [ - { - enable = true; - } - ( - let - urlComponents = builtins.elemAt (builtins.split "https://([^/]*)(/?.*)" cfg.web.externalUrl) 1; - domain = builtins.elemAt urlComponents 0; - location = builtins.elemAt urlComponents 1; - in - { - virtualHosts.${domain} = { - forceSSL = mkDefault true; - locations.${location} = { - # The trailing slash is important to strip the location prefix from the request - proxyPass = "http://${cfg.web.address}:${toString cfg.web.port}/"; - proxyWebsockets = true; - }; - }; - } - ) - ( - let - urlComponents = builtins.elemAt (builtins.split "https://([^/]*)(/?.*)" cfg.api.externalUrl) 1; - domain = builtins.elemAt urlComponents 0; - location = builtins.elemAt urlComponents 1; - in - { - virtualHosts.${domain} = { - forceSSL = mkDefault true; - locations.${location} = { - # The trailing slash is important to strip the location prefix from the request - proxyPass = "http://${cfg.api.address}:${toString cfg.api.port}/"; - proxyWebsockets = true; - }; - }; - } - ) - ]; - }) - # Specify sensible defaults - { - services.firezone.server = { - settings = { - LOG_LEVEL = mkDefault "info"; - RELEASE_HOSTNAME = mkDefault "localhost.localdomain"; - - ERLANG_CLUSTER_ADAPTER = mkDefault "Elixir.Cluster.Strategy.Epmd"; - ERLANG_CLUSTER_ADAPTER_CONFIG = mkDefault ( - builtins.toJSON { - hosts = cfg.clusterHosts; - } - ); - - TZDATA_DIR = mkDefault "/var/lib/firezone/tzdata"; - TELEMETRY_ENABLED = mkDefault false; - - # By default this will open nproc * 2 connections for each component, - # which can exceeds the (default) maximum of 100 connections for - # postgresql on a 12 core +SMT machine. 16 connections will be - # sufficient for small to medium deployments - DATABASE_POOL_SIZE = "16"; - - AUTH_PROVIDER_ADAPTERS = mkDefault (concatStringsSep "," availableAuthAdapters); - - FEATURE_FLOW_ACTIVITIES_ENABLED = mkDefault true; - FEATURE_POLICY_CONDITIONS_ENABLED = mkDefault true; - FEATURE_MULTI_SITE_RESOURCES_ENABLED = mkDefault true; - FEATURE_SELF_HOSTED_RELAYS_ENABLED = mkDefault true; - FEATURE_IDP_SYNC_ENABLED = mkDefault true; - FEATURE_REST_API_ENABLED = mkDefault true; - FEATURE_INTERNET_RESOURCE_ENABLED = mkDefault true; - FEATURE_TRAFFIC_FILTERS_ENABLED = mkDefault true; - FEATURE_SIGN_UP_ENABLED = mkDefault true; - }; - - domain.settings = { - ERLANG_DISTRIBUTION_PORT = mkDefault 9000; - HEALTHZ_PORT = mkDefault 4000; - BACKGROUND_JOBS_ENABLED = mkDefault true; - }; - - web.settings = { - ERLANG_DISTRIBUTION_PORT = mkDefault 9001; - HEALTHZ_PORT = mkDefault 4001; - BACKGROUND_JOBS_ENABLED = mkDefault false; - - PHOENIX_LISTEN_ADDRESS = mkDefault cfg.web.address; - PHOENIX_EXTERNAL_TRUSTED_PROXIES = mkDefault (builtins.toJSON cfg.web.trustedProxies); - PHOENIX_HTTP_WEB_PORT = mkDefault cfg.web.port; - PHOENIX_HTTP_API_PORT = mkDefault cfg.api.port; - PHOENIX_SECURE_COOKIES = mkDefault true; # enforce HTTPS on cookies - WEB_EXTERNAL_URL = mkDefault cfg.web.externalUrl; - API_EXTERNAL_URL = mkDefault cfg.api.externalUrl; - }; - - api.settings = { - ERLANG_DISTRIBUTION_PORT = mkDefault 9002; - HEALTHZ_PORT = mkDefault 4002; - BACKGROUND_JOBS_ENABLED = mkDefault false; - - PHOENIX_LISTEN_ADDRESS = mkDefault cfg.api.address; - PHOENIX_EXTERNAL_TRUSTED_PROXIES = mkDefault (builtins.toJSON cfg.api.trustedProxies); - PHOENIX_HTTP_WEB_PORT = mkDefault cfg.web.port; - PHOENIX_HTTP_API_PORT = mkDefault cfg.api.port; - PHOENIX_SECURE_COOKIES = mkDefault true; # enforce HTTPS on cookies - WEB_EXTERNAL_URL = mkDefault cfg.web.externalUrl; - API_EXTERNAL_URL = mkDefault cfg.api.externalUrl; - }; - }; - } - (mkIf (!cfg.smtp.configureManually) { - services.firezone.server.settings = { - OUTBOUND_EMAIL_ADAPTER = "Elixir.Swoosh.Adapters.Mua"; - OUTBOUND_EMAIL_ADAPTER_OPTS = builtins.toJSON { }; - OUTBOUND_EMAIL_FROM = cfg.smtp.from; - OUTBOUND_EMAIL_SMTP_HOST = cfg.smtp.host; - OUTBOUND_EMAIL_SMTP_PORT = toString cfg.smtp.port; - OUTBOUND_EMAIL_SMTP_PROTOCOL = if cfg.smtp.implicitTls then "ssl" else "tcp"; - OUTBOUND_EMAIL_SMTP_USERNAME = cfg.smtp.username; - }; - services.firezone.server.settingsSecret = { - OUTBOUND_EMAIL_SMTP_PASSWORD = cfg.smtp.passwordFile; - }; - }) - (mkIf cfg.provision.enable { - # Load client secrets from authentication providers - services.firezone.server.settingsSecret = flip concatMapAttrs cfg.provision.accounts ( - accountName: accountCfg: - flip concatMapAttrs accountCfg.auth ( - authName: authCfg: - optionalAttrs (authCfg.adapter_config.clientSecretFile != null) { - "AUTH_CLIENT_SECRET_${toUpper accountName}_${toUpper authName}" = - authCfg.adapter_config.clientSecretFile; - } - ) - ); - }) - (mkIf (cfg.openClusterFirewall && cfg.domain.enable) { - networking.firewall.allowedTCPPorts = [ - cfg.domain.settings.ERLANG_DISTRIBUTION_PORT - ]; - }) - (mkIf (cfg.openClusterFirewall && cfg.web.enable) { - networking.firewall.allowedTCPPorts = [ - cfg.web.settings.ERLANG_DISTRIBUTION_PORT - ]; - }) - (mkIf (cfg.openClusterFirewall && cfg.api.enable) { - networking.firewall.allowedTCPPorts = [ - cfg.api.settings.ERLANG_DISTRIBUTION_PORT - ]; - }) - (mkIf (cfg.domain.enable || cfg.web.enable || cfg.api.enable) { - systemd.slices.system-firezone = { - description = "Firezone Slice"; - }; - - systemd.targets.firezone = { - description = "Common target for all Firezone services."; - wantedBy = [ "multi-user.target" ]; - }; - - systemd.services.firezone-initialize = { - description = "Backend initialization service for the Firezone zero-trust access platform"; - - after = mkIf cfg.enableLocalDB [ "postgresql.service" ]; - requires = mkIf cfg.enableLocalDB [ "postgresql.service" ]; - wantedBy = [ "firezone.target" ]; - partOf = [ "firezone.target" ]; - - script = '' - mkdir -p "$TZDATA_DIR" - - # Generate and load secrets - ${generateSecrets} - ${loadSecretEnvironment "domain"} - - echo "Running migrations" - ${getExe cfg.domain.package} eval Domain.Release.migrate - ''; - - # We use the domain environment to be able to run migrations - environment = collectEnvironment "domain"; - serviceConfig = commonServiceConfig // { - Type = "oneshot"; - RemainAfterExit = true; - }; - }; - - systemd.services.firezone-server-domain = mkIf cfg.domain.enable { - description = "Backend domain server for the Firezone zero-trust access platform"; - after = [ "firezone-initialize.service" ]; - bindsTo = [ "firezone-initialize.service" ]; - wantedBy = [ "firezone.target" ]; - partOf = [ "firezone.target" ]; - - script = '' - ${loadSecretEnvironment "domain"} - exec ${getExe cfg.domain.package} start; - ''; - - path = [ pkgs.curl ]; - postStart = mkIf cfg.provision.enable '' - ${loadSecretEnvironment "domain"} - - # Wait for the firezone server to come online - count=0 - while [[ "$(curl -s "http://localhost:${toString cfg.domain.settings.HEALTHZ_PORT}" 2>/dev/null || echo)" != '{"status":"ok"}' ]] - do - sleep 1 - if [[ "$count" -eq 30 ]]; then - echo "Tried for at least 30 seconds, giving up..." - exit 1 - fi - count=$((count++)) - done - - sleep 1 # Wait for server to fully come up. Not ideal to use sleep, but at least it works. - ln -sTf ${provisionStateJson} provision-state.json - ${getExe cfg.domain.package} rpc 'Code.eval_file("${./provision.exs}")' - ''; - - environment = collectEnvironment "domain"; - serviceConfig = commonServiceConfig; - }; - - systemd.services.firezone-server-web = mkIf cfg.web.enable { - description = "Backend web server for the Firezone zero-trust access platform"; - after = [ "firezone-initialize.service" ]; - bindsTo = [ "firezone-initialize.service" ]; - wantedBy = [ "firezone.target" ]; - partOf = [ "firezone.target" ]; - - script = '' - ${loadSecretEnvironment "web"} - exec ${getExe cfg.web.package} start; - ''; - - environment = collectEnvironment "web"; - serviceConfig = commonServiceConfig; - }; - - systemd.services.firezone-server-api = mkIf cfg.api.enable { - description = "Backend api server for the Firezone zero-trust access platform"; - after = [ "firezone-initialize.service" ]; - bindsTo = [ "firezone-initialize.service" ]; - wantedBy = [ "firezone.target" ]; - partOf = [ "firezone.target" ]; - - script = '' - ${loadSecretEnvironment "api"} - exec ${getExe cfg.api.package} start; - ''; - - environment = collectEnvironment "api"; - serviceConfig = commonServiceConfig; - }; - }) - ]; - - meta.maintainers = with lib.maintainers; [ - oddlama - patrickdag - ]; -} diff --git a/modules/provision.exs b/modules/provision.exs deleted file mode 100644 index 98e9c93..0000000 --- a/modules/provision.exs +++ /dev/null @@ -1,155 +0,0 @@ -defmodule Provision do - alias Domain.{Repo, Accounts, Auth, Actors} - require Logger - - defp resolve_references(value) when is_map(value) do - Enum.into(value, %{}, fn {k, v} -> {k, resolve_references(v)} end) - end - - defp resolve_references(value) when is_list(value) do - Enum.map(value, &resolve_references/1) - end - - defp resolve_references(value) when is_binary(value) do - Regex.replace(~r/\{env:([^}]+)\}/, value, fn _, var -> - System.get_env(var) || raise "Environment variable #{var} not set" - end) - end - - defp resolve_references(value), do: value - - defp atomize_keys(map) when is_map(map) do - Enum.into(map, %{}, fn {k, v} -> - { - if(is_binary(k), do: String.to_atom(k), else: k), - if(is_map(v), do: atomize_keys(v), else: v) - } - end) - end - - def provision() do - IO.inspect("Starting provisioning", label: "INFO") - json_file = "provision-state.json" - {:ok, raw_json} = File.read(json_file) - {:ok, %{"accounts" => accounts}} = Jason.decode(raw_json) - accounts = resolve_references(accounts) - - multi = Enum.reduce(accounts, Ecto.Multi.new(), fn {slug, account_data}, multi -> - account_attrs = atomize_keys(%{ - name: account_data["name"], - slug: slug, - features: Map.get(account_data, "features", %{}), - metadata: Map.get(account_data, "metadata", %{}), - limits: Map.get(account_data, "limits", %{}), - }) - - multi = multi - |> Ecto.Multi.run({:account, slug}, fn repo, _changes -> - case Accounts.fetch_account_by_id_or_slug(slug) do - {:ok, acc} -> - IO.inspect("Updating existing account #{slug}", label: "INFO") - updated_acc = acc |> Ecto.Changeset.change(account_attrs) |> repo.update!() - {:ok, {:existing, updated_acc}} - _ -> - IO.inspect("Creating new account #{slug}", label: "INFO") - {:ok, account} = Accounts.create_account(account_attrs) - {:ok, {:new, account}} - end - end) - |> Ecto.Multi.run({:everyone_group, slug}, fn _repo, changes -> - case Map.get(changes, {:account, slug}) do - {:new, account} -> - IO.inspect("Creating Everyone group for new account", label: "INFO") - Actors.create_managed_group(account, %{name: "Everyone", membership_rules: [%{operator: true}]}) - {:existing, _account} -> - {:ok, :skipped} - end - end) - |> Ecto.Multi.run({:provider, slug}, fn _repo, changes -> - case Map.get(changes, {:account, slug}) do - {:new, account} -> - IO.inspect("Creating default email provider for new account", label: "INFO") - Auth.create_provider(account, %{name: "Email", adapter: :email, adapter_config: %{}}) - {:existing, account} -> - Auth.Provider.Query.not_disabled() - |> Auth.Provider.Query.by_adapter(:email) - |> Auth.Provider.Query.by_account_id(account.id) - |> Repo.fetch(Auth.Provider.Query, []) - end - end) - - multi = Enum.reduce(account_data["actors"] || %{}, multi, fn {name, actor_data}, multi -> - actor_attrs = atomize_keys(%{ - name: name, - type: String.to_atom(actor_data["type"]), - }) - - Ecto.Multi.run(multi, {:actor, slug, name}, fn repo, changes -> - {_, account} = changes[{:account, slug}] - case Repo.get_by(Actors.Actor, account_id: account.id, name: name) do - nil -> - IO.inspect("Creating new actor #{name}", label: "INFO") - {:ok, actor} = Actors.create_actor(account, actor_attrs) - {:ok, {:new, actor}} - act -> - IO.inspect("Updating existing actor #{name}", label: "INFO") - updated_act = act |> Ecto.Changeset.change(actor_attrs) |> repo.update!() - {:ok, {:existing, updated_act}} - end - end) - |> Ecto.Multi.run({:actor_identity, slug, name}, fn repo, changes -> - email_provider = changes[{:provider, slug}] - case Map.get(changes, {:actor, slug, name}) do - {:new, actor} -> - IO.inspect("Creating actor email identity", label: "INFO") - Auth.create_identity(actor, email_provider, %{ - provider_identifier: actor_data["email"], - provider_identifier_confirmation: actor_data["email"] - }) - {:existing, actor} -> - IO.inspect("Updating actor email identity", label: "INFO") - {:ok, identity} = Auth.Identity.Query.not_deleted() - |> Auth.Identity.Query.by_actor_id(actor.id) - |> Auth.Identity.Query.by_provider_id(email_provider.id) - |> Repo.fetch(Auth.Identity.Query, []) - - {:ok, identity |> Ecto.Changeset.change(%{ - provider_identifier: actor_data["email"], - }) |> repo.update!()} - end - end) - end) - - multi = Enum.reduce(account_data["auth"] || %{}, multi, fn {name, provider_data}, multi -> - provider_attrs = %{ - name: name, - adapter: String.to_atom(provider_data["adapter"]), - adapter_config: provider_data["adapter_config"], - } - - Ecto.Multi.run(multi, {:provider, slug, name}, fn repo, changes -> - {_, account} = changes[{:account, slug}] - case Repo.get_by(Auth.Provider, account_id: account.id, name: name) do - nil -> - IO.inspect("Creating new provider #{name}", label: "INFO") - Auth.create_provider(account, provider_attrs) - existing -> - IO.inspect("Updating existing provider #{name}", label: "INFO") - {:ok, existing |> Ecto.Changeset.change(provider_attrs) |> repo.update!()} - end - end) - end) - - multi - end) - - case Repo.transaction(multi) do - {:ok, _result} -> - Logger.info("Provisioning completed successfully.") - {:error, step, reason, _changes} -> - Logger.error("Provisioning failed at step #{inspect(step)}: #{inspect(reason)}") - end - end -end - -Provision.provision() diff --git a/pkgs/default.nix b/pkgs/default.nix index db1e13b..763ae4b 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -19,13 +19,6 @@ _inputs: [ # (_pythonFinal: pythonPrev: { # }) # ]; - firezone-server-domain = prev.callPackage ./firezone-server-domain/package.nix { }; - firezone-server-web = prev.callPackage ./firezone-server-web/package.nix { }; - firezone-server-api = prev.callPackage ./firezone-server-api/package.nix { }; - firezone-gateway = prev.callPackage ./firezone-gateway/package.nix { }; - firezone-relay = prev.callPackage ./firezone-relay/package.nix { }; - firezone-gui-client = prev.callPackage ./firezone-gui-client/package.nix { }; - firezone-headless-client = prev.callPackage ./firezone-headless-client/package.nix { }; mdns-repeater = prev.callPackage ./mdns-repeater.nix { }; diff --git a/pkgs/firezone-gateway/package.nix b/pkgs/firezone-gateway/package.nix deleted file mode 100644 index bac49eb..0000000 --- a/pkgs/firezone-gateway/package.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ - lib, - rustPlatform, - fetchFromGitHub, - nix-update-script, -}: -rustPlatform.buildRustPackage rec { - pname = "firezone-gateway"; - version = "1.4.2"; - src = fetchFromGitHub { - owner = "oddlama"; - repo = "firezone"; - rev = "feat/configurable-gateway-interface"; - hash = "sha256-dsGQubRs/ZHdAd6MHpa+3K4vGm4OnCdf1yMlGG1AIJk="; - }; - - useFetchCargoVendor = true; - cargoHash = "sha256-odrexu5mBGmvHNF4cUu9PDIEdxZC451lWV7dsLYT/Sg="; - sourceRoot = "${src.name}/rust"; - buildAndTestSubdir = "gateway"; - - # Required to remove profiling arguments which conflict with this builder - postPatch = '' - rm .cargo/config.toml - ''; - - passthru.updateScript = nix-update-script { }; - - meta = { - description = "WireGuard tunnel server for the Firezone zero-trust access platform"; - homepage = "https://github.com/firezone/firezone"; - license = lib.licenses.asl20; - maintainers = with lib.maintainers; [ - oddlama - patrickdag - ]; - mainProgram = "firezone-gateway"; - }; -} diff --git a/pkgs/firezone-gui-client/0000-add-hyper-tls-dependency.patch b/pkgs/firezone-gui-client/0000-add-hyper-tls-dependency.patch deleted file mode 100644 index 1d4349c..0000000 --- a/pkgs/firezone-gui-client/0000-add-hyper-tls-dependency.patch +++ /dev/null @@ -1,281 +0,0 @@ -diff --git a/rust/Cargo.lock b/rust/Cargo.lock -index 9645ee069..558339b1f 100644 ---- a/rust/Cargo.lock -+++ b/rust/Cargo.lock -@@ -951,7 +951,7 @@ dependencies = [ - "cocoa-foundation 0.1.2", - "core-foundation 0.9.4", - "core-graphics 0.23.2", -- "foreign-types", -+ "foreign-types 0.5.0", - "libc", - "objc", - ] -@@ -967,7 +967,7 @@ dependencies = [ - "cocoa-foundation 0.2.0", - "core-foundation 0.10.0", - "core-graphics 0.24.0", -- "foreign-types", -+ "foreign-types 0.5.0", - "libc", - "objc", - ] -@@ -1173,7 +1173,7 @@ dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", -- "foreign-types", -+ "foreign-types 0.5.0", - "libc", - ] - -@@ -1186,7 +1186,7 @@ dependencies = [ - "bitflags 2.6.0", - "core-foundation 0.10.0", - "core-graphics-types 0.2.0", -- "foreign-types", -+ "foreign-types 0.5.0", - "libc", - ] - -@@ -1999,6 +1999,7 @@ dependencies = [ - "firezone-logging", - "firezone-telemetry", - "futures", -+ "hyper-tls", - "native-dialog", - "nix 0.29.0", - "rand 0.8.5", -@@ -2281,6 +2282,15 @@ version = "0.1.3" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" - -+[[package]] -+name = "foreign-types" -+version = "0.3.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -+dependencies = [ -+ "foreign-types-shared 0.1.1", -+] -+ - [[package]] - name = "foreign-types" - version = "0.5.0" -@@ -2288,7 +2298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" - dependencies = [ - "foreign-types-macros", -- "foreign-types-shared", -+ "foreign-types-shared 0.3.1", - ] - - [[package]] -@@ -2302,6 +2312,12 @@ dependencies = [ - "syn 2.0.87", - ] - -+[[package]] -+name = "foreign-types-shared" -+version = "0.1.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -+ - [[package]] - name = "foreign-types-shared" - version = "0.3.1" -@@ -3035,6 +3051,22 @@ dependencies = [ - "tower-service", - ] - -+[[package]] -+name = "hyper-tls" -+version = "0.6.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -+dependencies = [ -+ "bytes", -+ "http-body-util", -+ "hyper", -+ "hyper-util", -+ "native-tls", -+ "tokio", -+ "tokio-native-tls", -+ "tower-service", -+] -+ - [[package]] - name = "hyper-util" - version = "0.1.9" -@@ -3723,6 +3755,23 @@ dependencies = [ - "winapi", - ] - -+[[package]] -+name = "native-tls" -+version = "0.2.12" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -+dependencies = [ -+ "libc", -+ "log", -+ "openssl", -+ "openssl-probe", -+ "openssl-sys", -+ "schannel", -+ "security-framework", -+ "security-framework-sys", -+ "tempfile", -+] -+ - [[package]] - name = "ndk" - version = "0.9.0" -@@ -4286,6 +4335,50 @@ dependencies = [ - "pathdiff", - ] - -+[[package]] -+name = "openssl" -+version = "0.10.68" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" -+dependencies = [ -+ "bitflags 2.6.0", -+ "cfg-if", -+ "foreign-types 0.3.2", -+ "libc", -+ "once_cell", -+ "openssl-macros", -+ "openssl-sys", -+] -+ -+[[package]] -+name = "openssl-macros" -+version = "0.1.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -+dependencies = [ -+ "proc-macro2", -+ "quote", -+ "syn 2.0.87", -+] -+ -+[[package]] -+name = "openssl-probe" -+version = "0.1.5" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -+ -+[[package]] -+name = "openssl-sys" -+version = "0.9.104" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" -+dependencies = [ -+ "cc", -+ "libc", -+ "pkg-config", -+ "vcpkg", -+] -+ - [[package]] - name = "opentelemetry" - version = "0.26.0" -@@ -5385,6 +5478,15 @@ dependencies = [ - "winapi-util", - ] - -+[[package]] -+name = "schannel" -+version = "0.1.27" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -+dependencies = [ -+ "windows-sys 0.59.0", -+] -+ - [[package]] - name = "schemars" - version = "0.8.21" -@@ -5474,6 +5576,29 @@ dependencies = [ - "zbus", - ] - -+[[package]] -+name = "security-framework" -+version = "2.11.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -+dependencies = [ -+ "bitflags 2.6.0", -+ "core-foundation 0.9.4", -+ "core-foundation-sys", -+ "libc", -+ "security-framework-sys", -+] -+ -+[[package]] -+name = "security-framework-sys" -+version = "2.14.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" -+dependencies = [ -+ "core-foundation-sys", -+ "libc", -+] -+ - [[package]] - name = "selectors" - version = "0.22.0" -@@ -5947,7 +6072,7 @@ dependencies = [ - "bytemuck", - "cfg_aliases", - "core-graphics 0.24.0", -- "foreign-types", -+ "foreign-types 0.5.0", - "js-sys", - "log", - "objc2", -@@ -6786,6 +6911,16 @@ dependencies = [ - "syn 2.0.87", - ] - -+[[package]] -+name = "tokio-native-tls" -+version = "0.3.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -+dependencies = [ -+ "native-tls", -+ "tokio", -+] -+ - [[package]] - name = "tokio-rustls" - version = "0.26.0" -@@ -7395,6 +7530,12 @@ version = "0.1.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -+[[package]] -+name = "vcpkg" -+version = "0.2.15" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -+ - [[package]] - name = "version-compare" - version = "0.2.0" -diff --git a/rust/gui-client/src-tauri/Cargo.toml b/rust/gui-client/src-tauri/Cargo.toml -index 32ca56197..f0475e6bf 100644 ---- a/rust/gui-client/src-tauri/Cargo.toml -+++ b/rust/gui-client/src-tauri/Cargo.toml -@@ -25,6 +25,7 @@ firezone-headless-client = { workspace = true } - firezone-logging = { workspace = true } - firezone-telemetry = { workspace = true } - futures = { workspace = true } -+hyper-tls = "0.6.0" - native-dialog = { workspace = true } - rand = { workspace = true } - rustls = { workspace = true } diff --git a/pkgs/firezone-gui-client/package.nix b/pkgs/firezone-gui-client/package.nix deleted file mode 100644 index 975bbcd..0000000 --- a/pkgs/firezone-gui-client/package.nix +++ /dev/null @@ -1,153 +0,0 @@ -{ - lib, - rustPlatform, - fetchFromGitHub, - nix-update-script, - pkg-config, - dbus, - openssl, - zenity, - cargo-tauri, - gdk-pixbuf, - glib, - gobject-introspection, - gtk3, - kdialog, - libsoup_3, - libayatana-appindicator, - webkitgtk_4_1, - wrapGAppsHook3, - stdenvNoCC, - git, - pnpm_9, - nodejs, -}: -let - version = "1.4.0"; - src = fetchFromGitHub { - owner = "firezone"; - repo = "firezone"; - tag = "gui-client-${version}"; - hash = "sha256-gwlkHTFVd1gHwOOIar+5+LEP2ovoyEIMmLfPxYb/OCs="; - # FIXME: remove once https://github.com/firezone/firezone/pull/7808 is released - # FIXME: somehow still required... - postFetch = '' - ${lib.getExe git} -C $out apply ${./0000-add-hyper-tls-dependency.patch} - ''; - }; - - frontend = stdenvNoCC.mkDerivation rec { - pname = "firezone-gui-client-frontend"; - inherit version src; - - pnpmDeps = pnpm_9.fetchDeps { - inherit pname version; - src = "${src}/rust/gui-client"; - hash = "sha256-Gc+X4mbpHa8b/Ol6Fee7VFpTPG25LksoAEjFww1WH5I="; - }; - pnpmRoot = "rust/gui-client"; - - nativeBuildInputs = [ - pnpm_9.configHook - nodejs - ]; - - buildPhase = '' - runHook preBuild - - cd $pnpmRoot - cp node_modules/flowbite/dist/flowbite.min.js src/ - pnpm tailwindcss -i src/input.css -o src/output.css - node --max_old_space_size=1024000 ./node_modules/vite/bin/vite.js build - - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - cp -r dist $out - - runHook postInstall - ''; - }; -in -rustPlatform.buildRustPackage rec { - pname = "firezone-gui-client"; - inherit version src; - - useFetchCargoVendor = true; - cargoHash = "sha256-eqkYwP14kca7Q/P9wc+CvTBmKETTmbd06EBNx3MKLro="; - sourceRoot = "${src.name}/rust"; - buildAndTestSubdir = "gui-client"; - - nativeBuildInputs = [ - cargo-tauri.hook - - pkg-config - wrapGAppsHook3 - ]; - - buildInputs = [ - openssl - dbus - gdk-pixbuf - glib - gobject-introspection - gtk3 - libsoup_3 - - libayatana-appindicator - webkitgtk_4_1 - ]; - - # Required to remove profiling arguments which conflict with this builder - postPatch = '' - rm .cargo/config.toml - ln -s ${frontend} gui-client/dist - ''; - - # Required to run tests - preCheck = '' - export XDG_RUNTIME_DIR=$(mktemp -d) - ''; - - checkFlags = [ - # Requires network access - "--skip=notifiers" - ]; - - preFixup = '' - gappsWrapperArgs+=( - # Otherwise blank screen, see https://github.com/tauri-apps/tauri/issues/9304 - --set WEBKIT_DISABLE_DMABUF_RENDERER 1 - --prefix PATH ":" ${ - lib.makeBinPath [ - zenity - kdialog - ] - } - --prefix LD_LIBRARY_PATH ":" ${ - lib.makeLibraryPath [ - libayatana-appindicator - ] - } - ) - ''; - - passthru = { - inherit frontend; - updateScript = nix-update-script { }; - }; - - meta = { - description = "GUI client for the Firezone zero-trust access platform"; - homepage = "https://github.com/firezone/firezone"; - license = lib.licenses.asl20; - maintainers = with lib.maintainers; [ - oddlama - patrickdag - ]; - mainProgram = "firezone-gui-client"; - }; -} diff --git a/pkgs/firezone-headless-client/package.nix b/pkgs/firezone-headless-client/package.nix deleted file mode 100644 index 0ab970a..0000000 --- a/pkgs/firezone-headless-client/package.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ - lib, - rustPlatform, - fetchFromGitHub, - nix-update-script, -}: -rustPlatform.buildRustPackage rec { - pname = "firezone-headless-client"; - version = "1.4.0"; - src = fetchFromGitHub { - owner = "oddlama"; - repo = "firezone"; - rev = "feat/configurable-gateway-interface"; - hash = "sha256-dsGQubRs/ZHdAd6MHpa+3K4vGm4OnCdf1yMlGG1AIJk="; - }; - - useFetchCargoVendor = true; - cargoHash = "sha256-odrexu5mBGmvHNF4cUu9PDIEdxZC451lWV7dsLYT/Sg="; - sourceRoot = "${src.name}/rust"; - buildAndTestSubdir = "headless-client"; - - # Required to remove profiling arguments which conflict with this builder - postPatch = '' - rm .cargo/config.toml - ''; - - # Required to run tests - preCheck = '' - export XDG_RUNTIME_DIR=$(mktemp -d) - ''; - - passthru.updateScript = nix-update-script { }; - - meta = { - description = "CLI client for the Firezone zero-trust access platform"; - homepage = "https://github.com/firezone/firezone"; - license = lib.licenses.asl20; - maintainers = with lib.maintainers; [ - oddlama - patrickdag - ]; - mainProgram = "firezone-headless-client"; - }; -} diff --git a/pkgs/firezone-relay/package.nix b/pkgs/firezone-relay/package.nix deleted file mode 100644 index ee341ad..0000000 --- a/pkgs/firezone-relay/package.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ - lib, - rustPlatform, - fetchFromGitHub, - nix-update-script, -}: -rustPlatform.buildRustPackage rec { - pname = "firezone-relay"; - version = "unstable-2025-01-19"; - src = fetchFromGitHub { - owner = "firezone"; - repo = "firezone"; - rev = "8c9427b7b133e5050be34c2ac0e831c12c08f02c"; - hash = "sha256-yccplADHRJQQiKrmHcJ5rvouswHrbx4K6ysnIAoZJR0="; - }; - - useFetchCargoVendor = true; - cargoHash = "sha256-WpJL5ALFMvyYv3QI5gMazAj6BVr4oyGq+zOo40rxqOE="; - sourceRoot = "${src.name}/rust"; - buildAndTestSubdir = "relay"; - - # Required to remove profiling arguments which conflict with this builder - postPatch = '' - rm .cargo/config.toml - ''; - - passthru.updateScript = nix-update-script { }; - - meta = { - description = "STUN/TURN server for the Firezone zero-trust access platform"; - homepage = "https://github.com/firezone/firezone"; - license = lib.licenses.asl20; - maintainers = with lib.maintainers; [ - oddlama - patrickdag - ]; - mainProgram = "firezone-relay"; - }; -} diff --git a/pkgs/firezone-server-api/package.nix b/pkgs/firezone-server-api/package.nix deleted file mode 100644 index 16a5e7e..0000000 --- a/pkgs/firezone-server-api/package.nix +++ /dev/null @@ -1,3 +0,0 @@ -import ../firezone-server-domain/generic.nix { - mixReleaseName = "api"; -} diff --git a/pkgs/firezone-server-domain/0000-add-healthz-port.patch b/pkgs/firezone-server-domain/0000-add-healthz-port.patch deleted file mode 100644 index dc95561..0000000 --- a/pkgs/firezone-server-domain/0000-add-healthz-port.patch +++ /dev/null @@ -1,60 +0,0 @@ -diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex -index 8cd2e8d0f..de55c8e8f 100644 ---- a/apps/domain/lib/domain/config/definitions.ex -+++ b/apps/domain/lib/domain/config/definitions.ex -@@ -120,6 +120,7 @@ defmodule Domain.Config.Definitions do - ]}, - {"Instrumentation", - [ -+ :healthz_port, - :instrumentation_client_logs_enabled, - :instrumentation_client_logs_bucket, - :telemetry_metrics_reporter, -@@ -474,6 +475,19 @@ defmodule Domain.Config.Definitions do - ## Telemetry - ############################################## - -+ @doc """ -+ The port for the internal healthz endpoint. -+ """ -+ defconfig(:healthz_port, :integer, -+ default: 4000, -+ changeset: fn changeset, key -> -+ Ecto.Changeset.validate_number(changeset, key, -+ greater_than: 0, -+ less_than_or_equal_to: 65_535 -+ ) -+ end -+ ) -+ - @doc """ - Enable or disable the Firezone telemetry collection. - -diff --git a/apps/domain/lib/domain/telemetry.ex b/apps/domain/lib/domain/telemetry.ex -index af430358d..d154282b4 100644 ---- a/apps/domain/lib/domain/telemetry.ex -+++ b/apps/domain/lib/domain/telemetry.ex -@@ -13,7 +13,7 @@ defmodule Domain.Telemetry do - - children = [ - # We start a /healthz endpoint that is used for liveness probes -- {Bandit, plug: Telemetry.HealthzPlug, scheme: :http, port: 4000}, -+ {Bandit, plug: Telemetry.HealthzPlug, scheme: :http, port: Keyword.get(config, :healthz_port)}, - - # Telemetry poller will execute the given period measurements - # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics -diff --git a/config/runtime.exs b/config/runtime.exs -index 7817942fa..c6dfe9c31 100644 ---- a/config/runtime.exs -+++ b/config/runtime.exs -@@ -211,7 +211,9 @@ if config_env() == :prod do - otlp_endpoint: System.get_env("OTLP_ENDPOINT") - end - -- config :domain, Domain.Telemetry, metrics_reporter: compile_config!(:telemetry_metrics_reporter) -+ config :domain, Domain.Telemetry, -+ healthz_port: compile_config!(:healthz_port), -+ metrics_reporter: compile_config!(:telemetry_metrics_reporter) - - if telemetry_metrics_reporter = compile_config!(:telemetry_metrics_reporter) do - config :domain, telemetry_metrics_reporter, compile_config!(:telemetry_metrics_reporter_opts) diff --git a/pkgs/firezone-server-domain/0001-postgres-socket.patch b/pkgs/firezone-server-domain/0001-postgres-socket.patch deleted file mode 100644 index 9e8f0f4..0000000 --- a/pkgs/firezone-server-domain/0001-postgres-socket.patch +++ /dev/null @@ -1,100 +0,0 @@ -diff --git a/apps/domain/lib/domain/config.ex b/apps/domain/lib/domain/config.ex -index 11fdcc3a1..9254f210d 100644 ---- a/apps/domain/lib/domain/config.ex -+++ b/apps/domain/lib/domain/config.ex -@@ -42,6 +42,24 @@ defmodule Domain.Config do - end - end - -+ @doc """ -+ Similar to `compile_config/2` but returns nil if the configuration is invalid. -+ -+ This function does not resolve values from the database because it's intended use is during -+ compilation and before application boot (in `config/runtime.exs`). -+ -+ If you need to resolve values from the database, use `fetch_config/1` or `fetch_config!/1`. -+ """ -+ def compile_config(module \\ Definitions, key, env_config \\ System.get_env()) do -+ case Fetcher.fetch_source_and_config(module, key, %{}, env_config) do -+ {:ok, _source, value} -> -+ value -+ -+ {:error, reason} -> -+ nil -+ end -+ end -+ - def config_changeset(changeset, schema_key, config_key \\ nil) do - config_key = config_key || schema_key - -diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex -index 8cd2e8d0f..f27d67c69 100644 ---- a/apps/domain/lib/domain/config/definitions.ex -+++ b/apps/domain/lib/domain/config/definitions.ex -@@ -61,6 +61,7 @@ defmodule Domain.Config.Definitions do - {"Database", - [ - :database_host, -+ :database_socket_dir, - :database_port, - :database_name, - :database_user, -@@ -255,6 +256,11 @@ defmodule Domain.Config.Definitions do - """ - defconfig(:database_host, :string, default: "postgres") - -+ @doc """ -+ PostgreSQL socket directory (takes precedence over hostname). -+ """ -+ defconfig(:database_socket_dir, :string, default: nil) -+ - @doc """ - PostgreSQL port. - """ -diff --git a/config/runtime.exs b/config/runtime.exs -index 7817942fa..14cbe182f 100644 ---- a/config/runtime.exs -+++ b/config/runtime.exs -@@ -1,22 +1,31 @@ - import Config - - if config_env() == :prod do -- import Domain.Config, only: [compile_config!: 1] -+ import Domain.Config, only: [compile_config!: 1, compile_config: 1] - - ############################### - ##### Domain ################## - ############################### - -- config :domain, Domain.Repo, -- database: compile_config!(:database_name), -- username: compile_config!(:database_user), -- hostname: compile_config!(:database_host), -- port: compile_config!(:database_port), -- password: compile_config!(:database_password), -- pool_size: compile_config!(:database_pool_size), -- ssl: compile_config!(:database_ssl_enabled), -- ssl_opts: compile_config!(:database_ssl_opts), -- parameters: compile_config!(:database_parameters) -+ config :domain, -+ Domain.Repo, -+ [ -+ {:database, compile_config!(:database_name)}, -+ {:username, compile_config!(:database_user)}, -+ {:port, compile_config!(:database_port)}, -+ {:pool_size, compile_config!(:database_pool_size)}, -+ {:ssl, compile_config!(:database_ssl_enabled)}, -+ {:ssl_opts, compile_config!(:database_ssl_opts)}, -+ {:parameters, compile_config!(:database_parameters)} -+ ] ++ -+ if(compile_config(:database_password), -+ do: [{:password, compile_config!(:database_password)}], -+ else: [] -+ ) ++ -+ if(compile_config(:database_socket_dir), -+ do: [{:socket_dir, compile_config!(:database_socket_dir)}], -+ else: [{:hostname, compile_config!(:database_host)}] -+ ) - - config :domain, Domain.Tokens, - key_base: compile_config!(:tokens_key_base), diff --git a/pkgs/firezone-server-domain/0002-add-mua.patch b/pkgs/firezone-server-domain/0002-add-mua.patch deleted file mode 100644 index 458caf9..0000000 --- a/pkgs/firezone-server-domain/0002-add-mua.patch +++ /dev/null @@ -1,94 +0,0 @@ -diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex -index 8cd2e8d0f..2e6ca054b 100644 ---- a/apps/domain/lib/domain/config/definitions.ex -+++ b/apps/domain/lib/domain/config/definitions.ex -@@ -584,6 +584,7 @@ defmodule Domain.Config.Definitions do - Swoosh.Adapters.Mailgun, - Swoosh.Adapters.Mailjet, - Swoosh.Adapters.Mandrill, -+ Swoosh.Adapters.Mua, - Swoosh.Adapters.Postmark, - Swoosh.Adapters.ProtonBridge, - Swoosh.Adapters.SMTP, -diff --git a/apps/web/lib/web/live/sign_up.ex b/apps/web/lib/web/live/sign_up.ex -index dfbc94d9b..638e3b373 100644 ---- a/apps/web/lib/web/live/sign_up.ex -+++ b/apps/web/lib/web/live/sign_up.ex -@@ -1,5 +1,6 @@ - defmodule Web.SignUp do - use Web, {:live_view, layout: {Web.Layouts, :public}} -+ require Logger - alias Domain.{Auth, Accounts, Actors, Config} - alias Web.Registration - -@@ -358,7 +359,9 @@ defmodule Web.SignUp do - - {:noreply, assign(socket, form: to_form(changeset))} - -- {:error, :send_email, _reason, _effects_so_far} -> -+ {:error, :send_email, reason, _effects_so_far} -> -+ Logger.warning("Failed to send sign_up email", reason: inspect(reason)) -+ - changeset = - Ecto.Changeset.add_error( - changeset, -diff --git a/config/runtime.exs b/config/runtime.exs -index 7817942fa..8704ccde5 100644 ---- a/config/runtime.exs -+++ b/config/runtime.exs -@@ -228,7 +228,21 @@ if config_env() == :prod do - [ - adapter: compile_config!(:outbound_email_adapter), - from_email: compile_config!(:outbound_email_from) -- ] ++ compile_config!(:outbound_email_adapter_opts) -+ ] ++ -+ (if System.get_env("OUTBOUND_EMAIL_ADAPTER") == "Elixir.Swoosh.Adapters.Mua" do -+ [ -+ protocol: String.to_atom(System.get_env("OUTBOUND_EMAIL_SMTP_PROTOCOL")), -+ relay: System.get_env("OUTBOUND_EMAIL_SMTP_HOST"), -+ port: String.to_integer(System.get_env("OUTBOUND_EMAIL_SMTP_PORT")), -+ auth: [ -+ username: System.get_env("OUTBOUND_EMAIL_SMTP_USERNAME"), -+ password: System.get_env("OUTBOUND_EMAIL_SMTP_PASSWORD") -+ ] -+ ] -+ else -+ [] -+ end) ++ -+ compile_config!(:outbound_email_adapter_opts) - - config :workos, WorkOS.Client, - api_key: compile_config!(:workos_api_key), -diff --git a/mix.exs b/mix.exs -index 12782d631..dee1245d2 100644 ---- a/mix.exs -+++ b/mix.exs -@@ -47,7 +47,9 @@ defmodule Firezone.MixProject do - # Formatter doesn't track dependencies of children applications - {:phoenix, "~> 1.7.0"}, - {:phoenix_live_view, "~> 1.0.0-rc.0"}, -- {:floki, "~> 0.37.0"} -+ {:floki, "~> 0.37.0"}, -+ {:mua, "~> 0.2.0"}, -+ {:mail, "~> 0.3.0"} - ] - end - -diff --git a/mix.lock b/mix.lock -index 757829003..47775677d 100644 ---- a/mix.lock -+++ b/mix.lock -@@ -50,11 +50,13 @@ - "junit_formatter": {:hex, :junit_formatter, "3.4.0", "d0e8db6c34dab6d3c4154c3b46b21540db1109ae709d6cf99ba7e7a2ce4b1ac2", [:mix], [], "hexpm", "bb36e2ae83f1ced6ab931c4ce51dd3dbef1ef61bb4932412e173b0cfa259dacd"}, - "libcluster": {:hex, :libcluster, "3.3.3", "a4f17721a19004cfc4467268e17cff8b1f951befe428975dd4f6f7b84d927fe0", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "7c0a2275a0bb83c07acd17dab3c3bfb4897b145106750eeccc62d302e3bdfee5"}, - "logger_json": {:hex, :logger_json, "6.2.0", "13e2e9f5f7b195865c5c3ef3d296c3ad50e7ecb038d899433702a79e979b91d7", [:mix], [{:ecto, "~> 3.11", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "98366d02bedbb56e41b25a6d248d566d4f4bc224bae2b1e982df00ed04ba9219"}, -+ "mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, - "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, - "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, - "mix_audit": {:hex, :mix_audit, "2.1.4", "0a23d5b07350cdd69001c13882a4f5fb9f90fbd4cbf2ebc190a2ee0d187ea3e9", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "fd807653cc8c1cada2911129c7eb9e985e3cc76ebf26f4dd628bb25bbcaa7099"}, -+ "mua": {:hex, :mua, "0.2.4", "a9172ab0a1ac8732cf2699d739ceac3febcb9b4ffc540260ad2e32c0b6632af9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "e7e4dacd5ad65f13e3542772e74a159c00bd2d5579e729e9bb72d2c73a266fb7"}, - "multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"}, - "nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"}, - "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, diff --git a/pkgs/firezone-server-domain/generic.nix b/pkgs/firezone-server-domain/generic.nix deleted file mode 100644 index 7987334..0000000 --- a/pkgs/firezone-server-domain/generic.nix +++ /dev/null @@ -1,135 +0,0 @@ -{ - mixReleaseName, # "domain" "web" or "api" -}: -{ - lib, - nixosTests, - fetchFromGitHub, - beamPackages, - pnpm_9, - nodejs, - tailwindcss, - esbuild, -}: - -beamPackages.mixRelease rec { - pname = "firezone-server-${mixReleaseName}"; - version = "unstable-2025-01-19"; - - src = "${ - fetchFromGitHub { - owner = "firezone"; - repo = "firezone"; - rev = "8c9427b7b133e5050be34c2ac0e831c12c08f02c"; - hash = "sha256-yccplADHRJQQiKrmHcJ5rvouswHrbx4K6ysnIAoZJR0="; - } - }/elixir"; - patches = [ - ./0000-add-healthz-port.patch - ./0001-postgres-socket.patch - ./0002-add-mua.patch - ]; - - pnpmDeps = pnpm_9.fetchDeps { - inherit pname version; - src = "${src}/apps/web/assets"; - hash = "sha256-6rhhGv3jQY5MkOMNe1GEtNyrzJYXCSzvo8RLlKelP10="; - }; - pnpmRoot = "apps/web/assets"; - - preBuild = '' - cat >> config/config.exs <> config/runtime.exs <