Files
firezone/elixir/config/runtime.exs
Jamil 968db2ae39 feat(portal): Receive WAL events (#8909)
Firezone's control plane is a realtime, distributed system that relies
on a broadcast/subscribe system to function. In many cases, these events
are broadcasted whenever relevant data in the DB changes, such as an
actor losing access to a policy, a membership being deleted, and so
forth.

Today, this is handled in the application layer, typically happening at
the place where the relevant DB call is made (i.e. in an
`after_commit`). While this approach has worked thus far, it has several
issues:

1. We have no guarantee that the DB change will issue a broadcast. If
the application is deployed or the process crashes after the DB changes
are made but before the broadcast happens, we will have potentially
failed to update any connected clients or gateways with the changes.
2. We have no guarantee that the order of DB updates will be maintained
in order for broadcasts. In other words, app server A could win its DB
operation against app server B, but then proceed to lose being the first
to broadcast.
3. If the cluster is in a bad state where broadcasts may return an error
(i.e. https://github.com/firezone/firezone/issues/8660), we will never
retry the broadcast.

To fix the above issues, we introduce a WAL logical decoder that process
the event stream one message at a time and performs any needed work.
Serializability is guaranteed since we only process the WAL in a single,
cluster-global process, `ReplicationConnection`. Durability is also
guaranteed since we only ACK WAL segments after we've successfully
ingested the event.

This means we will only advance the position of our WAL stream after
successfully broadcasting the event.

This PR only introduces the WAL stream processing system but does not
introduce any changes to our current broadcasting behavior - that's
saved for another PR.
2025-04-29 23:53:06 -07:00

280 lines
10 KiB
Elixir

import Config
if config_env() == :prod do
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)},
{: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.Events.ReplicationConnection,
connection_opts: [
# Automatically reconnect if we lose connection.
auto_reconnect: true,
hostname: compile_config!(:database_host),
port: compile_config!(:database_port),
ssl: compile_config!(:database_ssl_enabled),
ssl_opts: compile_config!(:database_ssl_opts),
parameters: compile_config!(:database_parameters),
username: compile_config!(:database_replication_user),
password: compile_config!(:database_replication_password),
database: compile_config!(:database_name)
]
config :domain, Domain.Tokens,
key_base: compile_config!(:tokens_key_base),
salt: compile_config!(:tokens_salt)
config :domain, Domain.Gateways,
gateway_ipv4_masquerade: compile_config!(:gateway_ipv4_masquerade),
gateway_ipv6_masquerade: compile_config!(:gateway_ipv6_masquerade)
config :domain, Domain.Auth.Adapters.GoogleWorkspace.APIClient,
finch_transport_opts: compile_config!(:http_client_ssl_opts)
config :domain, Domain.Billing.Stripe.APIClient,
endpoint: "https://api.stripe.com",
finch_transport_opts: []
config :domain, Domain.Billing,
enabled: compile_config!(:billing_enabled),
secret_key: compile_config!(:stripe_secret_key),
webhook_signing_secret: compile_config!(:stripe_webhook_signing_secret),
default_price_id: compile_config!(:stripe_default_price_id)
config :domain, platform_adapter: compile_config!(:platform_adapter)
if platform_adapter = compile_config!(:platform_adapter) do
config :domain, platform_adapter, compile_config!(:platform_adapter_config)
end
config :domain, Domain.Cluster,
adapter: compile_config!(:erlang_cluster_adapter),
adapter_config: compile_config!(:erlang_cluster_adapter_config)
config :domain, Domain.Instrumentation,
client_logs_enabled: compile_config!(:instrumentation_client_logs_enabled),
client_logs_bucket: compile_config!(:instrumentation_client_logs_bucket)
config :domain, Domain.Analytics,
mixpanel_token: compile_config!(:mixpanel_token),
hubspot_workspace_id: compile_config!(:hubspot_workspace_id)
config :domain, :enabled_features,
idp_sync: compile_config!(:feature_idp_sync_enabled),
sign_up: compile_config!(:feature_sign_up_enabled),
flow_activities: compile_config!(:feature_flow_activities_enabled),
self_hosted_relays: compile_config!(:feature_self_hosted_relays_enabled),
policy_conditions: compile_config!(:feature_policy_conditions_enabled),
multi_site_resources: compile_config!(:feature_multi_site_resources_enabled),
rest_api: compile_config!(:feature_rest_api_enabled),
internet_resource: compile_config!(:feature_internet_resource_enabled)
config :domain, sign_up_whitelisted_domains: compile_config!(:sign_up_whitelisted_domains)
config :domain, docker_registry: compile_config!(:docker_registry)
config :domain, outbound_email_adapter_configured?: !!compile_config!(:outbound_email_adapter)
config :domain, web_external_url: compile_config!(:web_external_url)
# Enable background jobs only on dedicated nodes
config :domain, Domain.Tokens.Jobs.DeleteExpiredTokens,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Billing.Jobs.CheckAccountLimits,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Auth.Adapters.GoogleWorkspace.Jobs.RefreshAccessTokens,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Auth.Adapters.GoogleWorkspace.Jobs.SyncDirectory,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Auth.Adapters.MicrosoftEntra.Jobs.RefreshAccessTokens,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Auth.Adapters.MicrosoftEntra.Jobs.SyncDirectory,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Auth.Adapters.Okta.Jobs.RefreshAccessTokens,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Auth.Adapters.Okta.Jobs.SyncDirectory,
enabled: compile_config!(:background_jobs_enabled)
config :domain, Domain.Auth.Adapters.JumpCloud.Jobs.SyncDirectory,
enabled: compile_config!(:background_jobs_enabled)
# Don't enable the mock sync directory job in production
config :domain, Domain.Auth.Adapters.Mock.Jobs.SyncDirectory, enabled: false
if web_external_url = compile_config!(:web_external_url) do
%{
scheme: web_external_url_scheme,
host: web_external_url_host,
port: web_external_url_port,
path: web_external_url_path
} = URI.parse(web_external_url)
###############################
##### Web #####################
###############################
config :web, Web.Endpoint,
http: [
ip: compile_config!(:phoenix_listen_address).address,
port: compile_config!(:phoenix_http_web_port),
protocol_options: compile_config!(:phoenix_http_protocol_options)
],
url: [
scheme: web_external_url_scheme,
host: web_external_url_host,
port: web_external_url_port,
path: web_external_url_path
],
secret_key_base: compile_config!(:secret_key_base),
check_origin: [
"#{web_external_url_scheme}://#{web_external_url_host}:#{web_external_url_port}",
"#{web_external_url_scheme}://*.#{web_external_url_host}:#{web_external_url_port}",
"#{web_external_url_scheme}://#{web_external_url_host}",
"#{web_external_url_scheme}://*.#{web_external_url_host}"
],
live_view: [
signing_salt: compile_config!(:live_view_signing_salt)
]
config :web,
external_trusted_proxies: compile_config!(:phoenix_external_trusted_proxies),
private_clients: compile_config!(:phoenix_private_clients)
config :web,
cookie_secure: compile_config!(:phoenix_secure_cookies),
cookie_signing_salt: compile_config!(:cookie_signing_salt),
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
config :web, api_url_override: compile_config!(:api_url_override)
end
if api_external_url = compile_config!(:api_external_url) do
%{
scheme: api_external_url_scheme,
host: api_external_url_host,
port: api_external_url_port,
path: api_external_url_path
} = URI.parse(api_external_url)
###############################
##### API #####################
###############################
config :api, API.Endpoint,
http: [
ip: compile_config!(:phoenix_listen_address).address,
port: compile_config!(:phoenix_http_api_port),
protocol_options: compile_config!(:phoenix_http_protocol_options)
],
url: [
scheme: api_external_url_scheme,
host: api_external_url_host,
port: api_external_url_port,
path: api_external_url_path
],
secret_key_base: compile_config!(:secret_key_base)
config :api,
cookie_secure: compile_config!(:phoenix_secure_cookies),
cookie_signing_salt: compile_config!(:cookie_signing_salt),
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
config :api,
external_trusted_proxies: compile_config!(:phoenix_external_trusted_proxies),
private_clients: compile_config!(:phoenix_private_clients)
config :api, API.RateLimit,
refill_rate: compile_config!(:api_refill_rate),
capacity: compile_config!(:api_capacity)
config :web,
api_external_url: api_external_url
end
###############################
##### Third-party configs #####
###############################
if System.get_env("OTLP_ENDPOINT") do
config :opentelemetry, resource_detectors: [:otel_resource_env_var, :otel_resource_app_env]
config :opentelemetry,
span_processor: :batch,
traces_exporter: :otlp
config :opentelemetry_exporter,
otlp_protocol: :http_protobuf,
otlp_traces_protocol: :http_protobuf,
otlp_endpoint: System.get_env("OTLP_ENDPOINT")
end
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)
end
config :domain,
http_client_ssl_opts: compile_config!(:http_client_ssl_opts)
config :openid_connect,
finch_transport_opts: compile_config!(:http_client_ssl_opts)
config :domain,
Domain.Mailer,
[
adapter: compile_config!(:outbound_email_adapter),
from_email: compile_config!(:outbound_email_from)
] ++ compile_config!(:outbound_email_adapter_opts)
config :workos, WorkOS.Client,
api_key: compile_config!(:workos_api_key),
client_id: compile_config!(:workos_client_id)
# Sentry
with api_external_url <- compile_config!(:api_external_url),
api_external_url_host <- URI.parse(api_external_url).host,
environment_name when environment_name in [:staging, :production] <-
(case api_external_url_host do
"api.firezone.dev" -> :production
"api.firez.one" -> :staging
_ -> :unknown
end) do
config :sentry,
environment_name: environment_name,
dsn:
"https://29f4ab7c6c473c17bc01f8aeffb0ac16@o4507971108339712.ingest.us.sentry.io/4508756715569152"
end
end