feat: add file env support for secrets
This commit is contained in:
parent
a143c4e243
commit
ee094eec2f
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.
|
# any compile-time configuration in here, as it won't be applied.
|
||||||
# The block below contains prod specific runtime configuration.
|
# 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)
|
||||||
|
|
||||||
|
{: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
|
# ## Using releases
|
||||||
#
|
#
|
||||||
# If you use `mix release`, you need to explicitly enable the server
|
# If you use `mix release`, you need to explicitly enable the server
|
||||||
|
|
@ -21,12 +90,7 @@ if System.get_env("PHX_SERVER") do
|
||||||
end
|
end
|
||||||
|
|
||||||
if config_env() == :prod do
|
if config_env() == :prod do
|
||||||
database_url =
|
database_url = build_database_url.()
|
||||||
System.get_env("DATABASE_URL") ||
|
|
||||||
raise """
|
|
||||||
environment variable DATABASE_URL is missing.
|
|
||||||
For example: ecto://USER:PASS@HOST/DATABASE
|
|
||||||
"""
|
|
||||||
|
|
||||||
maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
|
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
|
# 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
|
# to check this value into version control, so we use an environment
|
||||||
# variable instead.
|
# variable instead.
|
||||||
|
# Supports SECRET_KEY_BASE or SECRET_KEY_BASE_FILE for Docker secrets.
|
||||||
secret_key_base =
|
secret_key_base =
|
||||||
System.get_env("SECRET_KEY_BASE") ||
|
get_env_or_file!.("SECRET_KEY_BASE", """
|
||||||
raise """
|
environment variable SECRET_KEY_BASE (or SECRET_KEY_BASE_FILE) is missing.
|
||||||
environment variable SECRET_KEY_BASE is missing.
|
|
||||||
You can generate one by calling: mix phx.gen.secret
|
You can generate one by calling: mix phx.gen.secret
|
||||||
"""
|
""")
|
||||||
|
|
||||||
host = System.get_env("PHX_HOST") || raise "Please define the PHX_HOST environment variable."
|
host = System.get_env("PHX_HOST") || raise "Please define the PHX_HOST environment variable."
|
||||||
port = String.to_integer(System.get_env("PORT") || "4000")
|
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")
|
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
||||||
|
|
||||||
# Rauthy OIDC configuration
|
# Rauthy OIDC configuration
|
||||||
|
# Supports OIDC_CLIENT_SECRET or OIDC_CLIENT_SECRET_FILE for Docker secrets.
|
||||||
config :mv, :rauthy,
|
config :mv, :rauthy,
|
||||||
client_id: System.get_env("OIDC_CLIENT_ID") || "mv",
|
client_id: System.get_env("OIDC_CLIENT_ID") || "mv",
|
||||||
base_url: System.get_env("OIDC_BASE_URL") || "http://localhost:8080/auth/v1",
|
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),
|
||||||
redirect_uri:
|
redirect_uri:
|
||||||
System.get_env("OIDC_REDIRECT_URI") || "http://#{host}:#{port}/auth/user/rauthy/callback"
|
System.get_env("OIDC_REDIRECT_URI") || "http://#{host}:#{port}/auth/user/rauthy/callback"
|
||||||
|
|
||||||
# Token signing secret from environment variable
|
# Token signing secret from environment variable
|
||||||
# This overrides the placeholder value set in prod.exs
|
# This overrides the placeholder value set in prod.exs
|
||||||
|
# Supports TOKEN_SIGNING_SECRET or TOKEN_SIGNING_SECRET_FILE for Docker secrets.
|
||||||
token_signing_secret =
|
token_signing_secret =
|
||||||
System.get_env("TOKEN_SIGNING_SECRET") ||
|
get_env_or_file!.("TOKEN_SIGNING_SECRET", """
|
||||||
raise """
|
environment variable TOKEN_SIGNING_SECRET (or TOKEN_SIGNING_SECRET_FILE) is missing.
|
||||||
environment variable TOKEN_SIGNING_SECRET is missing.
|
|
||||||
You can generate one by calling: mix phx.gen.secret
|
You can generate one by calling: mix phx.gen.secret
|
||||||
"""
|
""")
|
||||||
|
|
||||||
config :mv, :token_signing_secret, token_signing_secret
|
config :mv, :token_signing_secret, token_signing_secret
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue