diff --git a/modules/firezone-server.nix b/modules/firezone-server.nix index d3f1ca2..12495d9 100644 --- a/modules/firezone-server.nix +++ b/modules/firezone-server.nix @@ -13,6 +13,7 @@ let types ; + cfg = config.services.firezone.server; apiCfg = config.services.firezone.server.api; domainCfg = config.services.firezone.server.domain; webCfg = config.services.firezone.server.web; @@ -58,98 +59,347 @@ let StateDirectory = "firezone"; WorkingDirectory = "/var/lib/firezone"; }; + + commonEnv = { + # **EXTERNAL_URL** + # PHOENIX_SECURE_COOKIES + # PHOENIX_HTTP_PORT + # PHOENIX_HTTP_PROTOCOL_OPTIONS + # PHOENIX_EXTERNAL_TRUSTED_PROXIES + # PHOENIX_PRIVATE_CLIENTS + # HTTP_CLIENT_SSL_OPTS + # DATABASE_HOST + # DATABASE_PORT + # DATABASE_NAME + # DATABASE_USER + # DATABASE_PASSWORD + # DATABASE_POOL_SIZE + # DATABASE_SSL_ENABLED + # DATABASE_SSL_OPTS + # RESET_ADMIN_ON_BOOT + # DEFAULT_ADMIN_EMAIL + # DEFAULT_ADMIN_PASSWORD + # **GUARDIAN_SECRET_KEY** + # **DATABASE_ENCRYPTION_KEY** + # **SECRET_KEY_BASE** + # **LIVE_VIEW_SIGNING_SALT** + # **COOKIE_SIGNING_SALT** + # **COOKIE_ENCRYPTION_SALT** + # ALLOW_UNPRIVILEGED_DEVICE_MANAGEMENT + # ALLOW_UNPRIVILEGED_DEVICE_CONFIGURATION + # VPN_SESSION_DURATION + # DEFAULT_CLIENT_PERSISTENT_KEEPALIVE + # DEFAULT_CLIENT_MTU + # DEFAULT_CLIENT_ENDPOINT + # DEFAULT_CLIENT_DNS + # DEFAULT_CLIENT_ALLOWED_IPS + # MAX_DEVICES_PER_USER + # LOCAL_AUTH_ENABLED + # DISABLE_VPN_ON_OIDC_ERROR + # SAML_ENTITY_ID + # SAML_KEYFILE_PATH + # SAML_CERTFILE_PATH + # OPENID_CONNECT_PROVIDERS + # SAML_IDENTITY_PROVIDERS + # WIREGUARD_PORT + # OUTBOUND_EMAIL_FROM + # OUTBOUND_EMAIL_ADAPTER + # OUTBOUND_EMAIL_ADAPTER_OPTS + # CONNECTIVITY_CHECKS_ENABLED + # CONNECTIVITY_CHECKS_INTERVAL + # TELEMETRY_ENABLED + # LOGO + + TZDATA_DIR = "/var/lib/firezone/tzdata"; + TELEMETRY_ENABLED = "false"; + + RELEASE_COOKIE = "agfea"; # TODO make option, if null generate automatically on first start + + # Database; + DATABASE_SOCKET_DIR = "/run/postgresql"; + DATABASE_PORT = "5432"; + DATABASE_NAME = "firezone"; + DATABASE_USER = "firezone"; + DATABASE_POOL_SIZE = "16"; + #DATABASE_HOST = "localhost"; + #DATABASE_PASSWORD = ""; + + # Auth; + AUTH_PROVIDER_ADAPTERS = "email,openid_connect,userpass,token"; + + # Secrets; + TOKENS_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; + SECRET_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; + TOKENS_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; + LIVE_VIEW_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; + COOKIE_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; + COOKIE_ENCRYPTION_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; + + OUTBOUND_EMAIL_ADAPTER = "Elixir.Swoosh.Adapters.Mua"; + OUTBOUND_EMAIL_ADAPTER_OPTS = builtins.toJSON { + }; + + # Feature flags; + FEATURE_FLOW_ACTIVITIES_ENABLED = "true"; + FEATURE_POLICY_CONDITIONS_ENABLED = "true"; + FEATURE_MULTI_SITE_RESOURCES_ENABLED = "true"; + FEATURE_SELF_HOSTED_RELAYS_ENABLED = "true"; + FEATURE_IDP_SYNC_ENABLED = "true"; + FEATURE_REST_API_ENABLED = "true"; + FEATURE_INTERNET_RESOURCE_ENABLED = "true"; + FEATURE_TRAFFIC_FILTERS_ENABLED = "true"; + FEATURE_SIGN_UP_ENABLED = "true"; + }; + + componentOptions = component: { + enable = mkEnableOption "the Firezone ${component} server"; + # TODO: single package plus web and api passthrough. + # package = mkPackageOption pkgs "firezone-server" { }; + + environment = { + exclude = mkOption { + type = types.attrsOf types.bool; + default = { }; + description = '' + Each environment variable specified through either + {option}`services.firezone.server.settings` or + {option}`services.firezone.server.secretSettings` + will only be passed to this component if it is not excluded + by this option. + ''; + example = { + PHOENIX_HTTP_PORT = true; + }; + }; + + replaceWithDummy = mkOption { + type = types.attrsOf types.bool; + default = { }; + description = '' + Any environment variable specified here will receive a dummy value + instead of the actual value specified through either + {option}`services.firezone.server.settings` or + {option}`services.firezone.server.secretSettings`. + + This can be used to hide secret information from components that + require a certain variable to be set while not actually using its + value. + ''; + example = { + SECRET_KEY_BASE = true; + }; + }; + + override = mkOption { + description = '' + Any environment variable specified here will receive the associated + value just for this component instead of the actual value specified + through either {option}`services.firezone.server.settings` or + {option}`services.firezone.server.secretSettings`. + ''; + default = { }; + type = lib.types.submodule { + freeformType = types.attrsOf ( + types.oneOf [ + types.bool + types.float + types.int + types.str + types.path + types.package + ] + ); + }; + }; + }; + + settings = lib.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 { /* ... */ }`. + + Configuration variables in Firezone are generally defined across all + components, so certain variables need to be present before any componen + will start up, even if that particular component does not actually + utilize its value. + + Each component has an additional `environment` option group which + allows you to exclude, replace or override certain variables passed to + that component. A sensible default filter is provided which you can + modify if necessary. + ''; + default = { }; + type = lib.types.submodule { + freeformType = types.attrsOf ( + types.oneOf [ + types.bool + types.float + types.int + types.str + types.path + types.package + ] + ); + }; + }; + }; in { options.services.firezone.server = { - domain = { - enable = mkEnableOption "the Firezone domain server"; - # TODO: single package plus web and api passthrough. - # package = mkPackageOption pkgs "firezone-server" { }; - enableLocalDB = mkEnableOption "a local postgresql database for Firezone"; + enableLocalDB = mkEnableOption "a local postgresql database for Firezone"; + # FIXME: enableNginx = mkEnableOption "a local nginx endpoint"; - settings = lib.mkOption { - default = { }; - description = '' - TODO - ''; - example = { - }; - type = lib.types.submodule { - freeformType = types.attrsOf ( - types.oneOf [ - types.bool - types.float - types.int - types.str - types.path - types.package - ] - ); - options = { + 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 `@`. + ''; + }; + + secretSettings = 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 = lib.types.submodule { + freeformType = types.attrsOf types.path; + options = { + # FIXME: SECRET_KEY_BASE + 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. + ''; }; }; }; }; - web = { - enable = mkEnableOption "the Firezone web server"; - # TODO: single package plus web and api passthrough. - # package = mkPackageOption pkgs "firezone-server" { }; - settings = lib.mkOption { - default = { }; - description = '' - TODO - ''; - example = { - }; - type = lib.types.submodule { - freeformType = types.attrsOf ( - types.oneOf [ - types.bool - types.float - types.int - types.str - types.path - types.package - ] - ); - options = { - }; - }; + settings = lib.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 { /* ... */ }`. + + Configuration variables in Firezone are generally defined across all + components, so certain variables need to be present before any componen + will start up, even if that particular component does not actually + utilize its value. + + Each component has an additional `environment` option group which + allows you to exclude, replace or override certain variables passed to + that component. A sensible default filter is provided which you can + modify if necessary. + ''; + default = { }; + type = lib.types.submodule { + freeformType = types.attrsOf ( + types.oneOf [ + types.bool + types.float + types.int + types.str + types.path + types.package + ] + ); }; }; - api = { - enable = mkEnableOption "the Firezone api server"; - # TODO: single package plus web and api passthrough. - # package = mkPackageOption pkgs "firezone-server" { }; - settings = lib.mkOption { - default = { }; - description = '' - TODO - ''; - example = { - }; - type = lib.types.submodule { - freeformType = types.attrsOf ( - types.oneOf [ - types.bool - types.float - types.int - types.str - types.path - types.package - ] - ); - options = { - }; - }; - }; + domain = componentOptions "domain" // { + }; + web = componentOptions "web" // { + }; + api = componentOptions "api" // { }; }; config = { - services.postgresql = mkIf domainCfg.enableLocalDB { + # Specify sensible defaults + services.firezone.server = { + settings = { + LOG_LEVEL = "debug"; + RELEASE_HOSTNAME = "localhost.localdomain"; + + ERLANG_CLUSTER_ADAPTER = "Elixir.Cluster.Strategy.Epmd"; + ERLANG_CLUSTER_ADAPTER_CONFIG = builtins.toJSON { + hosts = cfg.clusterHosts; + }; + + TZDATA_DIR = "/var/lib/firezone/tzdata"; + TELEMETRY_ENABLED = false; + + WEB_EXTERNAL_URL = "http://localhost:8080/"; + API_EXTERNAL_URL = "http://localhost:8081/"; + }; + + domain.settings = { + ERLANG_DISTRIBUTION_PORT = 9000; + HEALTHZ_PORT = 4000; + }; + domain.environment.exclude = { + WEB_EXTERNAL_URL = true; + API_EXTERNAL_URL = true; + }; + + web.settings = { + ERLANG_DISTRIBUTION_PORT = 9001; + HEALTHZ_PORT = 4001; + + # Web Server + PHOENIX_HTTP_WEB_PORT = 8080; + PHOENIX_HTTP_API_PORT = 8081; + PHOENIX_SECURE_COOKIES = false; + }; + + api.settings = { + ERLANG_DISTRIBUTION_PORT = 9002; + HEALTHZ_PORT = 4002; + }; + }; + + # FIXME: mkIf openClusterFirewall {}; + + services.postgresql = mkIf cfg.enableLocalDB { enable = true; ensureUsers = [ { @@ -162,288 +412,97 @@ in systemd.services.firezone-server-domain = mkIf domainCfg.enable { description = "Firezone domain server"; - after = mkIf domainCfg.enableLocalDB [ "postgresql.service" ]; - wants = mkIf domainCfg.enableLocalDB [ "postgresql.service" ]; + after = mkIf cfg.enableLocalDB [ "postgresql.service" ]; + wants = mkIf cfg.enableLocalDB [ "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; preStart = '' mkdir -p tzdata ''; + script = '' + ${getExe pkgs.firezone-server-domain} eval Domain.Release.migrate + exec ${getExe pkgs.firezone-server-domain} start + ''; + serviceConfig = commonServiceConfig // { - ExecStart = "${getExe pkgs.firezone-server-domain} start"; }; - environment = { - # **EXTERNAL_URL** - # PHOENIX_SECURE_COOKIES - # PHOENIX_HTTP_PORT - # PHOENIX_HTTP_PROTOCOL_OPTIONS - # PHOENIX_EXTERNAL_TRUSTED_PROXIES - # PHOENIX_PRIVATE_CLIENTS - # HTTP_CLIENT_SSL_OPTS - # DATABASE_HOST - # DATABASE_PORT - # DATABASE_NAME - # DATABASE_USER - # DATABASE_PASSWORD - # DATABASE_POOL_SIZE - # DATABASE_SSL_ENABLED - # DATABASE_SSL_OPTS - # RESET_ADMIN_ON_BOOT - # DEFAULT_ADMIN_EMAIL - # DEFAULT_ADMIN_PASSWORD - # **GUARDIAN_SECRET_KEY** - # **DATABASE_ENCRYPTION_KEY** - # **SECRET_KEY_BASE** - # **LIVE_VIEW_SIGNING_SALT** - # **COOKIE_SIGNING_SALT** - # **COOKIE_ENCRYPTION_SALT** - # ALLOW_UNPRIVILEGED_DEVICE_MANAGEMENT - # ALLOW_UNPRIVILEGED_DEVICE_CONFIGURATION - # VPN_SESSION_DURATION - # DEFAULT_CLIENT_PERSISTENT_KEEPALIVE - # DEFAULT_CLIENT_MTU - # DEFAULT_CLIENT_ENDPOINT - # DEFAULT_CLIENT_DNS - # DEFAULT_CLIENT_ALLOWED_IPS - # MAX_DEVICES_PER_USER - # LOCAL_AUTH_ENABLED - # DISABLE_VPN_ON_OIDC_ERROR - # SAML_ENTITY_ID - # SAML_KEYFILE_PATH - # SAML_CERTFILE_PATH - # OPENID_CONNECT_PROVIDERS - # SAML_IDENTITY_PROVIDERS - # WIREGUARD_PORT - # OUTBOUND_EMAIL_FROM - # OUTBOUND_EMAIL_ADAPTER - # OUTBOUND_EMAIL_ADAPTER_OPTS - # CONNECTIVITY_CHECKS_ENABLED - # CONNECTIVITY_CHECKS_INTERVAL - # TELEMETRY_ENABLED - # LOGO - - TZDATA_DIR = "/var/lib/firezone/tzdata"; - TELEMETRY_ENABLED = "false"; - - RELEASE_COOKIE = "agfea"; # TODO make option, if null generate automatically on first start + environment = commonEnv // { RELEASE_NAME = "domain"; RELEASE_HOSTNAME = "localhost.localdomain"; + ERLANG_DISTRIBUTION_PORT = "9000"; HEALTHZ_PORT = "4000"; - # Debugging; - LOG_LEVEL = "debug"; - - # Erlang; - ERLANG_DISTRIBUTION_PORT = "9002"; - ERLANG_CLUSTER_ADAPTER = "Elixir.Cluster.Strategy.Epmd"; - ERLANG_CLUSTER_ADAPTER_CONFIG = builtins.toJSON { - hosts = [ - "api@localhost.localdomain" - "web@localhost.localdomain" - "domain@localhost.localdomain" - ]; - }; - - # Database; - DATABASE_SOCKET_DIR = "/run/postgresql"; - DATABASE_PORT = "5432"; - DATABASE_NAME = "firezone"; - DATABASE_USER = "firezone"; - #DATABASE_HOST = "localhost"; - #DATABASE_PASSWORD = ""; - - # Auth; - AUTH_PROVIDER_ADAPTERS = "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta,jumpcloud"; - - # Secrets; - TOKENS_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; - SECRET_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; - TOKENS_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - LIVE_VIEW_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - COOKIE_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - COOKIE_ENCRYPTION_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - - # Seeds; - STATIC_SEEDS = "true"; - - OUTBOUND_EMAIL_FROM = "public-noreply@firez.one"; - OUTBOUND_EMAIL_ADAPTER = "Elixir.Swoosh.Adapters.Postmark"; - ## Warning= The token is for the blackhole Postmark server created in a separate isolated account,; - ## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.; - OUTBOUND_EMAIL_ADAPTER_OPTS = ''{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}''; - - # Feature flags; - FEATURE_FLOW_ACTIVITIES_ENABLED = "true"; - FEATURE_POLICY_CONDITIONS_ENABLED = "true"; - FEATURE_MULTI_SITE_RESOURCES_ENABLED = "true"; - FEATURE_SELF_HOSTED_RELAYS_ENABLED = "true"; - FEATURE_IDP_SYNC_ENABLED = "true"; - FEATURE_REST_API_ENABLED = "true"; - FEATURE_INTERNET_RESOURCE_ENABLED = "true"; + RESET_ADMIN_ON_BOOT = "true"; + DEFAULT_ADMIN_EMAIL = "admin@example.com"; + DEFAULT_ADMIN_PASSWORD = "admin@example.com"; }; }; systemd.services.firezone-server-web = mkIf webCfg.enable { description = "Firezone web server"; + after = mkIf cfg.enableLocalDB [ "postgresql.service" ]; + wants = mkIf cfg.enableLocalDB [ "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; preStart = '' mkdir -p tzdata ''; + script = '' + ${getExe pkgs.firezone-server-web} eval Domain.Release.migrate + exec ${getExe pkgs.firezone-server-web} start + ''; + serviceConfig = commonServiceConfig // { - ExecStart = "${getExe pkgs.firezone-server-web} start"; }; - environment = { - TZDATA_DIR = "/var/lib/firezone/tzdata"; - TELEMETRY_ENABLED = "false"; - - RELEASE_COOKIE = "agfea"; # TODO make option, if null generate automatically on first start + environment = commonEnv // { RELEASE_NAME = "web"; RELEASE_HOSTNAME = "localhost.localdomain"; + ERLANG_DISTRIBUTION_PORT = "9001"; + HEALTHZ_PORT = "4001"; + # Web Server WEB_EXTERNAL_URL = "http://localhost:8080/"; API_EXTERNAL_URL = "http://localhost:8081/"; PHOENIX_HTTP_WEB_PORT = "8080"; PHOENIX_HTTP_API_PORT = "8081"; PHOENIX_SECURE_COOKIES = "false"; - - HEALTHZ_PORT = "4001"; - - # Debugging; - LOG_LEVEL = "debug"; - - # Erlang; - ERLANG_DISTRIBUTION_PORT = "9001"; - ERLANG_CLUSTER_ADAPTER = "Elixir.Cluster.Strategy.Epmd"; - ERLANG_CLUSTER_ADAPTER_CONFIG = builtins.toJSON { - hosts = [ - "api@localhost.localdomain" - "web@localhost.localdomain" - "domain@localhost.localdomain" - ]; - }; - - # Database; - DATABASE_SOCKET_DIR = "/run/postgresql"; - DATABASE_PORT = "5432"; - DATABASE_NAME = "firezone"; - DATABASE_USER = "firezone"; - #DATABASE_HOST = "localhost"; - #DATABASE_PASSWORD = ""; - - # Auth; - AUTH_PROVIDER_ADAPTERS = "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta,jumpcloud"; - - # Secrets; - TOKENS_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; - SECRET_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; - TOKENS_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - LIVE_VIEW_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - COOKIE_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - COOKIE_ENCRYPTION_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - - # Seeds; - STATIC_SEEDS = "true"; - - OUTBOUND_EMAIL_FROM = "public-noreply@firez.one"; - OUTBOUND_EMAIL_ADAPTER = "Elixir.Swoosh.Adapters.Postmark"; - ## Warning= The token is for the blackhole Postmark server created in a separate isolated account,; - ## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.; - OUTBOUND_EMAIL_ADAPTER_OPTS = ''{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}''; - - # Feature flags; - FEATURE_FLOW_ACTIVITIES_ENABLED = "true"; - FEATURE_POLICY_CONDITIONS_ENABLED = "true"; - FEATURE_MULTI_SITE_RESOURCES_ENABLED = "true"; - FEATURE_SELF_HOSTED_RELAYS_ENABLED = "true"; - FEATURE_IDP_SYNC_ENABLED = "true"; - FEATURE_REST_API_ENABLED = "true"; - FEATURE_INTERNET_RESOURCE_ENABLED = "true"; }; }; systemd.services.firezone-server-api = mkIf apiCfg.enable { description = "Firezone api server"; + after = mkIf cfg.enableLocalDB [ "postgresql.service" ]; + wants = mkIf cfg.enableLocalDB [ "postgresql.service" ]; wantedBy = [ "multi-user.target" ]; preStart = '' mkdir -p tzdata ''; + script = '' + ${getExe pkgs.firezone-server-api} eval Domain.Release.migrate + exec ${getExe pkgs.firezone-server-api} start + ''; + serviceConfig = commonServiceConfig // { - ExecStart = "${getExe pkgs.firezone-server-api} start"; }; - environment = { - TZDATA_DIR = "/var/lib/firezone/tzdata"; - TELEMETRY_ENABLED = "false"; - - RELEASE_COOKIE = "agfea"; # TODO make option, if null generate automatically on first start + environment = commonEnv // { RELEASE_NAME = "api"; RELEASE_HOSTNAME = "localhost.localdomain"; + ERLANG_DISTRIBUTION_PORT = "9002"; + HEALTHZ_PORT = "4002"; + # Web Server WEB_EXTERNAL_URL = "http://localhost:8080/"; API_EXTERNAL_URL = "http://localhost:8081/"; - - HEALTHZ_PORT = "4002"; - - # Debugging; - LOG_LEVEL = "debug"; - - # Erlang; - ERLANG_DISTRIBUTION_PORT = "9000"; - ERLANG_CLUSTER_ADAPTER = "Elixir.Cluster.Strategy.Epmd"; - ERLANG_CLUSTER_ADAPTER_CONFIG = builtins.toJSON { - hosts = [ - "api@localhost.localdomain" - "web@localhost.localdomain" - "domain@localhost.localdomain" - ]; - }; - - # Database; - DATABASE_SOCKET_DIR = "/run/postgresql"; - DATABASE_PORT = "5432"; - DATABASE_NAME = "firezone"; - DATABASE_USER = "firezone"; - #DATABASE_HOST = "localhost"; - #DATABASE_PASSWORD = ""; - - # Auth; - AUTH_PROVIDER_ADAPTERS = "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta,jumpcloud"; - - # Secrets; - TOKENS_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; - SECRET_KEY_BASE = "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"; - TOKENS_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - LIVE_VIEW_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - COOKIE_SIGNING_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - COOKIE_ENCRYPTION_SALT = "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"; - - # Seeds; - STATIC_SEEDS = "true"; - - OUTBOUND_EMAIL_FROM = "public-noreply@firez.one"; - OUTBOUND_EMAIL_ADAPTER = "Elixir.Swoosh.Adapters.Postmark"; - ## Warning= The token is for the blackhole Postmark server created in a separate isolated account,; - ## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.; - OUTBOUND_EMAIL_ADAPTER_OPTS = ''{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}''; - - # Feature flags; - FEATURE_FLOW_ACTIVITIES_ENABLED = "true"; - FEATURE_POLICY_CONDITIONS_ENABLED = "true"; - FEATURE_MULTI_SITE_RESOURCES_ENABLED = "true"; - FEATURE_SELF_HOSTED_RELAYS_ENABLED = "true"; - FEATURE_IDP_SYNC_ENABLED = "true"; - FEATURE_REST_API_ENABLED = "true"; - FEATURE_INTERNET_RESOURCE_ENABLED = "true"; }; }; }; diff --git a/pkgs/firezone-server-domain/a.patch b/pkgs/firezone-server-domain/a.patch index 7f82320..d991822 100644 --- a/pkgs/firezone-server-domain/a.patch +++ b/pkgs/firezone-server-domain/a.patch @@ -1,5 +1,5 @@ diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex -index 8cd2e8d0f..f27d67c69 100644 +index 8cd2e8d0f..92e18b10b 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 @@ -22,6 +22,14 @@ index 8cd2e8d0f..f27d67c69 100644 @doc """ PostgreSQL port. """ +@@ -584,6 +590,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/domain/lib/domain/telemetry.ex b/apps/domain/lib/domain/telemetry.ex index af430358d..a544e706e 100644 --- a/apps/domain/lib/domain/telemetry.ex @@ -35,8 +43,29 @@ index af430358d..a544e706e 100644 # Telemetry poller will execute the given period measurements # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics +diff --git a/apps/web/lib/web/live/sign_up.ex b/apps/web/lib/web/live/sign_up.ex +index c4e06bc58..89533fb81 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,8 @@ 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.info("aaaaaaaaaaaaaaaaaa", reason: inspect(reason)) + changeset = + Ecto.Changeset.add_error( + changeset, diff --git a/config/runtime.exs b/config/runtime.exs -index 15037e0a3..948f62cc8 100644 +index 15037e0a3..475c4ddfb 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -8,15 +8,17 @@ if config_env() == :prod do @@ -66,3 +95,54 @@ index 15037e0a3..948f62cc8 100644 config :domain, Domain.Tokens, key_base: compile_config!(:tokens_key_base), +@@ -226,8 +228,15 @@ if config_env() == :prod do + config :domain, + Domain.Mailer, + [ +- adapter: compile_config!(:outbound_email_adapter), +- from_email: compile_config!(:outbound_email_from) ++ adapter: compile_config!(:outbound_email_adapter), ++ from_email: compile_config!(:outbound_email_from), ++ protocol: :ssl, ++ relay: System.get_env("OUTBOUND_EMAIL_RELAY"), ++ port: 465, ++ auth: [ ++ username: System.get_env("OUTBOUND_EMAIL_USERNAME"), ++ password: System.get_env("OUTBOUND_EMAIL_PASSWORD") ++ ] + ] ++ compile_config!(:outbound_email_adapter_opts) + + config :workos, WorkOS.Client, +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 8c4b65959..3d2f9faca 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/mix.nix b/pkgs/firezone-server-domain/mix.nix index 9d595c3..46f741a 100644 --- a/pkgs/firezone-server-domain/mix.nix +++ b/pkgs/firezone-server-domain/mix.nix @@ -775,6 +775,19 @@ let ]; }; + mail = buildMix rec { + name = "mail"; + version = "0.3.1"; + + src = fetchHex { + pkg = "mail"; + version = "${version}"; + sha256 = "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"; + }; + + beamDeps = [ ]; + }; + metrics = buildRebar3 rec { name = "metrics"; version = "1.0.1"; @@ -846,6 +859,19 @@ let ]; }; + mua = buildMix rec { + name = "mua"; + version = "0.2.4"; + + src = fetchHex { + pkg = "mua"; + version = "${version}"; + sha256 = "e7e4dacd5ad65f13e3542772e74a159c00bd2d5579e729e9bb72d2c73a266fb7"; + }; + + beamDeps = [ castore ]; + }; + multipart = buildMix rec { name = "multipart"; version = "0.4.0"; @@ -1476,7 +1502,9 @@ let gen_smtp hackney jason + mail mime + mua multipart plug plug_cowboy