encrypted_secrets_ex icon indicating copy to clipboard operation
encrypted_secrets_ex copied to clipboard

Encrypted runtime config

Open JoeWoodward opened this issue 4 years ago • 9 comments

I've been scratching my head with a problem which your post on elixirforum helped me squash. https://elixirforum.com/t/loading-custom-module-in-config-exs/23232 The problem being how to use the encrypted secrets inside a config file.

I am a long time ruby developer so this also seemed intuitive to me and was what I was looking for a solution to! Surprisingly hard to google, only found that thread after reading 10s of posts on compilation.

Anyway, based on the fact that it's not a good idea to decrypt these secrets during compilation as they'll be in the build unencrypted... maybe another solution is to create a runtime config module that behaves in a similar way but is called during startup. Similar to how you've proposed in the thread.

use EncryptedCredentials.RuntimeConfig
credentials = EncryptedCredentials.read!(file, key)

config :app, config_var: credentials[:config_var]

Then in Application#start/2 call this file

EncryptedCredentials.init_config

Just thinking out loud. For now I'm probably just going to build a module to do this for myself but while it was fresh thought it was worth writing down. The readme could also do with updating on how people actually get these credentials into the app and the limitations e.g. dependencies that require compiled configs rather than runtime

JoeWoodward avatar May 05 '20 14:05 JoeWoodward

Ok, I think I have something. Looking into how Plug works and found this article http://rrrene.org/2018/04/09/flow-elixir-metaprogramming/ on using metaprogramming for flow.

JoeWoodward avatar May 05 '20 15:05 JoeWoodward

Thanks for the research here! I don't have much time these days to chase this down but I'll gladly accept a PR or any notes toward getting this implemented :+1:

kieraneglin avatar May 05 '20 16:05 kieraneglin

I think I have something. Will write specs for it soon and get a PR out to you if you think this DSL makes sense? The config looks like this (can change the name of the module too, not sure if RuntimeConfig is the best name

defmodule MyApp.EncryptedConfig do
  use EncryptedSecrets.RuntimeConfig

  config :flashcards, MyApp.Mailer, api_key: :mailer_api_key
end

where :mailer_api_key is the yaml key.

mailer_api_key: 'my_api_key'
some_other_key: 'testing'

Or you can pass in nested keys by using a list

  config :flashcards, MyApp.Mailer, api_key: [:mailer_provider, :api_key]
mailer_provider:
  api_key: 'my_api_key'

JoeWoodward avatar May 06 '20 07:05 JoeWoodward

I like that DSL! What you've got makes sense to me so far.

As an aside, I've been thinking about what approach is the most flexible while being the least likely to leak secret data. I'm wondering about an approach similar to dotenv but with encrypted secrets. Loading these secrets into env pre-compilation should work for normal Elixir apps as well as Distillery releases, but I'd have to look deeper into security implications (secrets in build artifacts as well as leaking ENV globally).

kieraneglin avatar May 06 '20 16:05 kieraneglin

Any updates on this? I'd love to use in my project; this is the blocker currently.

chevinbrown avatar Oct 13 '21 13:10 chevinbrown

Sorry I haven't completed this and sadly don't have time to continue. I'm not using elixir anymore for the time being and have been very busy with other projects.

JoeWoodward avatar Oct 13 '21 14:10 JoeWoodward

Now that I have more experience with releases I think that this library's approach is totally incompatible with releases (or Distillery, for that matter).

I'm thinking that instead of trying to fight against the current, perhaps this project could change direction and encrypt your *.secrets.exs for storage in VCS and add some explicit decryption step for these files which can then be consumed by your app in the conventional way.

kieraneglin avatar Oct 13 '21 17:10 kieraneglin

Anyway, based on the fact that it's not a good idea to decrypt these secrets during compilation as they'll be in the build unencrypted ..

Do believe this concern applies here, I am using cloak and this library to encrypt the encryption key:

defmodule MyApp.Vault do
  use Cloak.Vault, otp_app: :my_app

  @impl GenServer
  def init(config) do
    config =
      Keyword.put(config, :ciphers,
        default:
          {Cloak.Ciphers.AES.GCM,
           tag: "AES.GCM.V1", key: Base.decode64!(Application.get_env(:my_app, :secrets).enc_key)} // here I read the encryption key.
      )

    {:ok, config}
  end
end

Why would providing / decrypting them in config.exs be safer?

acrolink avatar Jan 23 '22 13:01 acrolink

It seems that loading the secrets in config/runtime.exs solves this issue. Can anybody confirm that?

aus70 avatar Feb 28 '22 21:02 aus70