kieraneglin / encrypted_secrets_ex

Store your application's secrets inside your VCS
MIT License
46 stars 5 forks source link

Encrypted runtime config #10

Open JoeWoodward opened 4 years ago

JoeWoodward commented 4 years ago

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 commented 4 years ago

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.

kieraneglin commented 4 years ago

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:

JoeWoodward commented 4 years ago

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'
kieraneglin commented 4 years ago

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).

chevinbrown commented 2 years ago

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

JoeWoodward commented 2 years ago

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.

kieraneglin commented 2 years ago

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.

acrolink commented 2 years ago

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?

aus70 commented 2 years ago

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