Add file_envs for secrets and allow passing database url via separate envs #246
1 changed files with 82 additions and 17 deletions
|
|
@ -7,6 +7,75 @@ import Config
|
|||
# any compile-time configuration in here, as it won't be applied.
|
||||
# The block below contains prod specific runtime configuration.
|
||||
|
||||
# Helper function to read environment variables with Docker secrets support.
|
||||
# Supports the _FILE suffix pattern: if VAR_FILE is set, reads the value from
|
||||
# that file path. Otherwise falls back to VAR directly.
|
||||
# VAR_FILE takes priority and must contain the full absolute path to the secret file.
|
||||
get_env_or_file = fn var_name, default ->
|
||||
file_var = "#{var_name}_FILE"
|
||||
|
||||
case System.get_env(file_var) do
|
||||
nil ->
|
||||
System.get_env(var_name, default)
|
||||
|
||||
file_path ->
|
||||
case File.read(file_path) do
|
||||
{:ok, content} ->
|
||||
String.trim(content)
|
||||
|
simon marked this conversation as resolved
Outdated
|
||||
|
||||
{:error, reason} ->
|
||||
raise """
|
||||
Failed to read secret from file specified in #{file_var}="#{file_path}".
|
||||
Error: #{inspect(reason)}
|
||||
"""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Same as get_env_or_file but raises if the value is not set
|
||||
get_env_or_file! = fn var_name, error_message ->
|
||||
case get_env_or_file.(var_name, nil) do
|
||||
nil -> raise error_message
|
||||
value -> value
|
||||
end
|
||||
end
|
||||
|
||||
# Build database URL from individual components or use DATABASE_URL directly.
|
||||
# Supports both approaches:
|
||||
# 1. DATABASE_URL (or DATABASE_URL_FILE) - full connection URL
|
||||
# 2. Separate vars: DATABASE_HOST, DATABASE_USER, DATABASE_PASSWORD (or _FILE), DATABASE_NAME, DATABASE_PORT
|
||||
build_database_url = fn ->
|
||||
case get_env_or_file.("DATABASE_URL", nil) do
|
||||
nil ->
|
||||
# Build URL from separate components
|
||||
host =
|
||||
System.get_env("DATABASE_HOST") ||
|
||||
raise "DATABASE_HOST is required when DATABASE_URL is not set"
|
||||
|
||||
user =
|
||||
System.get_env("DATABASE_USER") ||
|
||||
raise "DATABASE_USER is required when DATABASE_URL is not set"
|
||||
|
||||
password =
|
||||
get_env_or_file!.("DATABASE_PASSWORD", """
|
||||
DATABASE_PASSWORD or DATABASE_PASSWORD_FILE is required when DATABASE_URL is not set.
|
||||
""")
|
||||
|
||||
database =
|
||||
System.get_env("DATABASE_NAME") ||
|
||||
raise "DATABASE_NAME is required when DATABASE_URL is not set"
|
||||
|
||||
port = System.get_env("DATABASE_PORT", "5432")
|
||||
|
||||
# URL-encode the password to handle special characters
|
||||
encoded_password = URI.encode_www_form(password)
|
||||
"ecto://#{user}:#{encoded_password}@#{host}:#{port}/#{database}"
|
||||
|
||||
url ->
|
||||
url
|
||||
end
|
||||
end
|
||||
|
||||
# ## Using releases
|
||||
#
|
||||
# If you use `mix release`, you need to explicitly enable the server
|
||||
|
|
@ -21,12 +90,7 @@ if System.get_env("PHX_SERVER") do
|
|||
end
|
||||
|
||||
if config_env() == :prod do
|
||||
database_url =
|
||||
System.get_env("DATABASE_URL") ||
|
||||
raise """
|
||||
environment variable DATABASE_URL is missing.
|
||||
For example: ecto://USER:PASS@HOST/DATABASE
|
||||
"""
|
||||
database_url = build_database_url.()
|
||||
|
||||
maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
|
||||
|
||||
|
|
@ -41,12 +105,12 @@ if config_env() == :prod do
|
|||
# want to use a different value for prod and you most likely don't want
|
||||
# to check this value into version control, so we use an environment
|
||||
# variable instead.
|
||||
# Supports SECRET_KEY_BASE or SECRET_KEY_BASE_FILE for Docker secrets.
|
||||
secret_key_base =
|
||||
System.get_env("SECRET_KEY_BASE") ||
|
||||
raise """
|
||||
environment variable SECRET_KEY_BASE is missing.
|
||||
You can generate one by calling: mix phx.gen.secret
|
||||
"""
|
||||
get_env_or_file!.("SECRET_KEY_BASE", """
|
||||
environment variable SECRET_KEY_BASE (or SECRET_KEY_BASE_FILE) is missing.
|
||||
You can generate one by calling: mix phx.gen.secret
|
||||
""")
|
||||
|
||||
host = System.get_env("PHX_HOST") || raise "Please define the PHX_HOST environment variable."
|
||||
port = String.to_integer(System.get_env("PORT") || "4000")
|
||||
|
|
@ -54,21 +118,22 @@ if config_env() == :prod do
|
|||
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
||||
|
||||
# Rauthy OIDC configuration
|
||||
# Supports OIDC_CLIENT_SECRET or OIDC_CLIENT_SECRET_FILE for Docker secrets.
|
||||
config :mv, :rauthy,
|
||||
client_id: System.get_env("OIDC_CLIENT_ID") || "mv",
|
||||
base_url: System.get_env("OIDC_BASE_URL") || "http://localhost:8080/auth/v1",
|
||||
client_secret: System.get_env("OIDC_CLIENT_SECRET"),
|
||||
client_secret: get_env_or_file.("OIDC_CLIENT_SECRET", nil),
|
||||
|
simon marked this conversation as resolved
Outdated
rafael
commented
Let's add an error message here, similar to the other calls Let's add an error message here, similar to the other calls
|
||||
redirect_uri:
|
||||
System.get_env("OIDC_REDIRECT_URI") || "http://#{host}:#{port}/auth/user/rauthy/callback"
|
||||
|
||||
# Token signing secret from environment variable
|
||||
# This overrides the placeholder value set in prod.exs
|
||||
# Supports TOKEN_SIGNING_SECRET or TOKEN_SIGNING_SECRET_FILE for Docker secrets.
|
||||
token_signing_secret =
|
||||
System.get_env("TOKEN_SIGNING_SECRET") ||
|
||||
raise """
|
||||
environment variable TOKEN_SIGNING_SECRET is missing.
|
||||
You can generate one by calling: mix phx.gen.secret
|
||||
"""
|
||||
get_env_or_file!.("TOKEN_SIGNING_SECRET", """
|
||||
environment variable TOKEN_SIGNING_SECRET (or TOKEN_SIGNING_SECRET_FILE) is missing.
|
||||
You can generate one by calling: mix phx.gen.secret
|
||||
""")
|
||||
|
||||
config :mv, :token_signing_secret, token_signing_secret
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue
Is this
trimspecifically needed? Seems like it could lead to some surprising behavior in some edge cases.replaced by trim_trailing