sezaru

sezaru

Automatically updating Ecto pool connections credentials

I’m using HashiCorp Vault to safely store secrets for my system. These secrets are read during system initialization (via runtime.exs).

One of the features that Vault gives is creating database credentials with TTL, meaning that your database connection will be available for a specific time (say, 1 hour) and then you will need to ask vault for a new credential and restart the connection.

This way you never have a perpetual credential to the database limiting the window for a data leak in case your credentials are compromised.

This can easily be done using Ecto dynamic repos, but this means that I will have to manage the connection pool by myself.

So, is there some way to add support for dynamic credentials with TTL for the already implemented Ecto pool?

My guess is that I would be able to configure my repo something like this:

config :ecto, Repo,
   update_credentials: &MyCredModule.get_credentials/0

This function would return a tuple, something like

{
  %{username: "...", password: "..."}, # New credentials
  ~U[2020-01-01 01:01:00.000000Z] # TTL or when pool needs to get new credentials again
}

I thought about creating an issue in Ecto’s GitHub suggesting something like this, but decided to try here first in case a solution already exists.

PS. I already saw some topics here discussing this, but they are old and/or don’t have any solution, so I decided to create a new one instead of resurrecting an older one.

Marked As Solved

sezaru

sezaru

So, I think I found a good (not great) workaround to it:

defmodule DBAuth do
  def configure(opts) do
    # Get username and password from vault or something else
    {username, password} = Credentials.get_new_cred()

    opts
    |> Keyword.replace!(:username, username)
    |> Keyword.replace!(:password, password)
  end
end

#### runtime.exs:
config :my_db, MyRepo,
  configure: {DBAuth, :configure, []},
  disconnect_on_error_codes: [:insufficient_privilege],
  ...

So, basically, I added a module DBAuth with a configure function that queries Vault, retrieves the new credentials, and updates the opts parameter with it.

This function is used when you set your repo configuration using configure: {DBAuth, :configure, []}.

This will ensure that you will request a new credential every time a new connection is created, but this alone doesn’t handle closing the connection when the credential expires.

To handle that, I added the line disconnect_on_error_codes: [:insufficient_privilege] to my repo configuration. What that line does is inform Ecto that if some query returns error :insufficient_privilege (which is the error I receive when my credential expires), that connection should be killed.

With these changes now my connections are automatically (in a lazy fashion) updated when the credential expires. The only issue is that the query that triggered the :insufficient_privilege error will fail with that error.

Maybe there is some way to handle that in the repo layer with some retry strategy, but I’m not aware how.

But regardless you can handle that in your end checking the error and retrying or simply using a lib like this one.

What do you guys think? Any suggestions are appreciated.

Also Liked

ahamez

ahamez

An update to this subject: thanks to a recent update in db_connection, it’s now possible to cleanly ask connections to disconnect (they will automatically reconnect), using disconnect_all/3.

To test this, I’ve written a minimal example here: GitHub - ahamez/vault_ecto: Vault + Ecto = 🌈.
The gist of it is simply this:

{_, %{pid: pid}} = Ecto.Repo.Registry.lookup(MyRepo)
DBConnection.disconnect_all(pid, 1_000)

I hope it will help :slightly_smiling_face:

sezaru

sezaru

Wouldn’t that crash all queries that are currently running in the pool? I guess in some systems that is ok, but in mine, I need to avoid this as much as possible.

An improvement to that would be to somehow flag the connection as “must_die” and when that connection becomes idle (it finished the current transaction), then it would kill itself and consequently, a new one would be created with the newer credentials.

Where Next?

Popular in Questions Top

ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New

We're in Beta

About us Mission Statement