James_E
I’m developing an application that’s going to be (for the forseeable future) installed and managed in a pretty boring, standard way — not using bespoke containerization, clustering, or anything like that.
I’ve currently got this code to manage SECRET_KEY_BASE, and it seems to work, but it also seems like a really messy hack:
# /config/runtime.exs
# …
if config_env() == :prod do
# …
secret_key_base =
System.get_env("SECRET_KEY_BASE")
|| Foo.Application.Util.app_secret_key_auto()
# …
end
# /lib/foo/util.ex
defmodule Foo.Application.Util do
# …
def app_secret_key_auto do
p = Path.expand("secret_key_base.txt", :filename.basedir(:user_config, "foo-app"))
# 1. Generate key if it doesn't exist
:ok = mkdir_p(Path.expand("..", p))
:ok = case File.open(p, [:write, :exclusive]) do # FIXME surely there must be some built-in tooling for this???
{:error, :eexist} -> :ok # happy path
{:ok, h} ->
case File.chmod(p, 0o600) do
:ok ->
# https://github.com/phoenixframework/phoenix/blob/v1.7.17/lib/mix/tasks/phx.gen.secret.ex#L17
data_ascii = (&:crypto.strong_rand_bytes(&1) |> Base.encode64(padding: false) |> binary_part(0, &1)).(64)
result = IO.write(h, data_ascii)
:ok = File.close(h)
result
{:error, e} ->
:ok = File.close(h)
{:error, e}
end
{:error, e} -> {:error, e}
end
# 2. load key
{:ok, data_ascii} = File.open(p, [:read], &Enum.fetch!(IO.stream(&1, :line), 0))
data_ascii
end
end
Is there any existing utility function or simple pattern that I should be using instead of this pile of spaghetti, or is Phoenix really just not meant to be used outside of cloud containers?
The documentation didn’t say much about it.
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted”
Version...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
axelson
Hmm, generally I wouldn’t generate the
SECRET_KEY_BASEif it doesn’t exist, I’d instead always pass it in via Env variables. Specifically these are the situations I’ve dealt with before that didn’t involve Kubernetes/Docker:EnvironmentFile=/path/to/some/.envfileThat way the only snippet you need is something like:
LostKobrakai
Adding one more option:
env $(cat .env | xargs) ./bin/app remoteIn the end system env is the complete opposite than catered to the cloud. You can provide those in a million ways and there’s likely one fitting your workflow – and it’s completely independent from the fact that you’re running elixir at all.
linusdm
Just as a side note: there is a mix task to create a secret key.
James_E
The snippet you posted is actually the stock behavior of the
phx.newtemplate; I edited it away from that because I don’t want my server to crash on startup just because the technician attempted a perfectly reasonable ZIP installation.The app isn’t being deployed in any kind of fancy cloud service, either. Heck, right now it’s targeting Windows server (though we hope to move over to Linux at some point.)
I guess that I could bundle extra instructions with the application demanding that the installing technician fiddle with the registry to get basic functionality… but is there really not any established pattern for making Phoenix “just work” in the absence of a fancy managed environment? (And even on Linux, putting a systemd service that references an external file just passes the buck of generating that file…)
That task seems to be hard-coded to send the result to standard output. I actually linked to that line in the comment at the top the bit of my code that does what that task would.
James_E
Or maybe I’m X-Y problemming this.
What, specifically, would go wrong if I simply set
:secret_key_baserandomly on startup whenever the environment doesn’t specify it? Would everything work perfectly so long as the app isn’t being clustered/distributed/whatever, or would I be laying the seeds of—for example—a catastrophic Ecto failure next time the server restarts?If the former, it seems like it would be pretty simple to raise if and only if it’s unspecified and the app is being run in a way that actually requires a non-arbitrary value for it… am I missing something?
LostKobrakai
Sessions are signed with the secret key. So all session cookies of user will become invalid, effectively logging them out if the were logged in and dropping any other stuff you might have stored in the session. Similarly csrf token given out (e.g. as part of forms) will become invalid, so users still having a form open won’t be able to successfully submit their forms.
Phoenix.Tokens also reply on the secret if you happen to use those.James_E
I see… so if I just randomize it on startup, then users authenticated based on session cookies will be logged out, any CSRF-protected flows will be interrupted, and any
Phoenix.tokens will be ungracefully invalidated. Definitely not great.Since keeping it stable is apparently so crucial to so much of Phoenix’s operation, what are the risks/drawbacks to just storing it in Ecto after automatically generating it on the first run?
LostKobrakai
I’m wondering if you’re not better off figuring out system env on windows over figuring out how to work around it. Releases support being installed as windows services, which based on the docs allow you to define additional system env values.
erlsrv — OTP 29.0.2 (erts 17.0.2) (See Env parameter)
James_E
Hmm, one difficulty I’m running into there is that Ecto isn’t started during config-load time:
Is there any best practice for making parts of Endpoint config depend on values stored in Ecto, like that? If I were to start Ecto within this file, I’m pretty sure that’d break the supervisor structure, and I don’t see any obvious clean way to transform the
phx.newtemplate to do that. Maybe a separate:ignoreprocess that runs immediately afterEcto.Migrator?benwilson512
This seems like a LOT of hoops to jump through just to avoid the actual good practice of doing a rolling deploy. And if you can’t rely on an environment variable being present on boot, how are you going to connect to the database anyway? Is that not also configured via an env var?
Zooming out: your app has preconditions to booting. The correct thing to do when those preconditions are not met is to not boot, which allows the currently running instance to continue to serve traffic. The boot failure goes to your logs, which gives you alerts, and you fix it.
None of that is docker or container specific, that’s been the normal rolling deploy pattern for 20+ years now.