SecretVault -- store your secrets inside your repository

SecretVault :lock:

All-in-one solution for storing your secrets inside repository.

Overview

It is a lightweight zero-dependency solution for Elixir projects to store secrets inside your repository that my friend and I made during #spawnfest 2022.

Features

  1. No external dependencies, pure Elixir
    You don’t need to setup and external binaries or services. And you can extend this library in plain Elixir.

  2. Mix-aware.
    Secrets are separated per-environment and there is a bunch of useful mix tasks to manipulate these secrets. Building releases with SecretVault is nothing different from building regular release.

  3. Easy to use.
    There is a straightforward tutorial for simple installation, and the whole project is well documented.

  4. Secure.
    Uses :crypto’s aes256gcm cipher by default. Plus there is a special task for performing very basic but useful audit of your secrets to detect weak or similar passwords.


Sidenote

This project is not available on hex.pm yet, but we’re planning to release a version as a soon as spawnfest evaluation finishes.

13 Likes

This is awesome.

1 Like

I meant it when I said this is awesome!!

Here’s why:

LiveBook has this open ticket about storing secret directly on a markdown file, which people generally push to Git repo.

So I was hoping this project of yours fit the bill. :upside_down_face:

@hst337 please reach out to @josevalim.

1 Like

1.0.0 released in hex :tada:

Some after-spawnfest fixes were added, documentation, errors and warnings were improved to provide first-class developer experience

By the way

This project won second place in Spawnfest 2022, and I want to thank organizers and sponsors for the hackaton and generous prizes!

7 Likes

Hello @hst337

Cool project :slight_smile: , just one thing:

When I click on the link under Usage section, I am redirected to a 404 GitHub with the next prompt
image

Don’t know if it is still on process XD

Try to click from here: SecretVault v1.0.0 — Documentation

1 Like

Working!! Thanks :slight_smile:

Hey @hst337, congratulations on your win.

Well deserved. :clap:

Will it be added to LiveBook, to seamlessly store env secrets directly on .livemd files?

Not for me to tell

Hey,

This seems really cool!

I tried to add it to a project, but I am struggling with some problems.

When I use SecretVault.Config.fetch_from_current_env/1 the returned config is for prod always. If I use fetch_from_env with dev (which is the env I am in) it works.

Could you elaborate on practical examples for the library integration?

As a test I tried to put my database password in the vault and use it to start the db.
I followed the docs, and tried to read the value in the config.exs and in the runtime.exs files, but it doesn’t seem to work.
How would one go about that? Is that even an intended use case?

Thanks

Ooh, I am really sorry for this, it is a bug I just forgot to fix. I’ll release patch version right now, and I am still planning add more practical tests during this week


UDP, check out the 1.0.1

1 Like

Thanks, the fix works!

I am looking forward to the examples!

How did you configure your app?

SecretVault doesn’t interact with passwords and values in the config.exs, it uses it’s own storage inside priv directory. To set password in the application env for database you can just write something like

password = SecretVault.read(config, "db_password")
ecto_config =
  :ecto
  |> Application.get_env(Repo, [])
  |> Keyword.put(:password, password)
Application.put_env(:ecto, Repo, ecto_config)

Would I do this in the Application start/2?

I tried to read in the config/runtime file from the vault, in essence replace System.get_env

EDIT:

Yes works in start

Yes, this should be done in Applicaiton.start/2 callback

1 Like

Is it possible to change the config for other applications?
In case we have our application and configure others from deps.

For example here:

config :xxxx,
  application_id: System.get_env("APP_ID"),
  api_key: System.get_env("API_KEY"),
  search_api_key: System.get_env("SEARCH_API_KEY"),
  batch_size: 500,
  recv_timeout: 5000

Is getting and setting one by one the best option?

If the application (mine should be ok, but the other) were to crash and restart will the config still be ok?

Yes, just like I’ve showed above

For existing dependencies, yes. If you’re writing your own, you can use one of SecretVault.Storage functions to store secrets in env or persistent_term for example.

Applications do not restart by default. When any included application crashes, VM writes erl_crash.dump and stops.

1 Like

Would it be possible to call SecretVault from runtime.exs? I tried it, but it always returns the default empty string.

$ bat config/runtime.exs
import Config

{:ok, config} = SecretVault.Config.fetch_from_current_env(:playground)
password = SecretVault.fetch!(config, "database_password")

config :playground, :key, password
$ bat config/config.exs
import Config

config :playground, :secret_vault,
  default: [password: "password"]
$ iex -S mix
Erlang/OTP 24 [erts-12.3.2.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Interactive Elixir (1.14.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Application.get_all_env(:playground)
[
  key: "My Super Secret Password",
  secret_vault: [default: [password: "password"]]
]
1 Like

If I want to use SecretVault in runtime.exs, do I need to somehow include the files that I generated for the env in the release?

When I try to release I am getting this error:

** (MatchError) no match of right hand side value: {:error, {:no_configuration_for_app, :xxx}}

It comes from the line where I do

{:ok, config} = SecretVault.Config.fetch_from_current_env(:xxx)

in runtime.exs.

Edit:

I see this error comes when there is no :secret_vault config.
I have it in my config.exs:

config :xxx, :secret_vault, default: [password: System.fetch_env!("SECRET_VAULT_PASSWORD")]

The SECRET_VAULT_PASSWORD env var is available.

I did some logging, if I get the secret_vault env in runtime.exs it returns nil even thought in config.exs it is ok.
Any idea why this might happen?

1 Like