Rails secrets.yml VS Dotenv VS Figaro with Capistrano on AWS
Asked Answered
E

2

8

There are several posts ans Stack Overflow questions about how to manage API tokens on the web, but I see a lot of people repeat what they read somewhere else, often with contradictions...

How do you deal with API Tokens, secrets and the like ?

Here's what I have read so far using 3 different gems

secrets.yml

Introduced with Rails 4.1, then updated to encrypted secrets around rails 5

When initially released on rails 4, they were (or were not ?) meant to be pushed on repositories. However I often saw examples using environment variables for production

# config/secrets.yml
development:
  secret_key_base: super_long_secret_key_for_development
  ...

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  ...

And at this point someone asked "Why do we use ENV for production ?". A legit question back then, often answered "We don't want production token to be hard coded in the application" (hence how it is not clear anymore if the secrets should have been committed or not).

Later, with Rails 5, secrets became encrypted with a master key, so the encrypted secrets.yml file could be committed to the repository, but then the problem remained the same with the master key used to read the secrets.

PROs:

  • Rails convention.
  • Easy deploy with capistrano-secrets gem and cap [stage] setup (and it only deploys stage secrets nice) or similar gems
  • YML data structure (array/hash ok) + can use Ruby code via ERB
  • With encrypted secrets since rails 5, easy to collaborate/share the secrets

CONs:

  • Need to use Rails.application.secrets.xxx
  • Many services like AWS still read from ENV to automatically setup their gems/services
  • Is not the 12 factors way (or is it ?)
  • Quite new, so not really used yet ?
  • If using encrypted secrets, need to manage the master key

Bkeepers dotenv

Simply defining a .env file at the root that is read by Rails on startup

Plugins like capistrano-env allow to easily inject environment specific ENV on servers, and secrets can still must be managed using .env.staging, .env.production

PROs

  • ENV is in 12factor rules
  • 3.5k stars... maybe not for nothing ?
  • the dotenv approach is now available on almost all other languages (JS, Go, etc)
  • Recent versions allow reusing variables (API_ROOT=X, SOME_ENDPOINT=${X}/endpoint)

CONs

  • No Ruby interpolation (unless extra code is added to parse the .env with the ERB templating engine for example)
  • limited to string-string key/val

Figaro

Some sort of hybrid secrets/ENV. With 12factors/Rails/Heroku in mind, but in the end doesn't seem better than the rest...

From the above and the rest I didn't write, it would seem like secrets.yml would be a great winner if those secrets were put in ENV instead (and tbh I feel lazy about writing Rails.Application.secrets each time).

So, suppose I start a quite new Rails app, and also based on your experience. Which one would you choose ?

(My particular stack uses AWS + Capistrano, no Heroku)

Euclid answered 19/8, 2016 at 23:28 Comment(0)
M
0

I personally think that the "right" approach depends on your environment.

In my case, I have servers which are managed by IT and I don't have access to the vhost or anything else to easily set environment variables. Because of this, I commit a secrets.yml file which doesn't contain the production stanza, then set up Capistrano to add this file to shared_files. On each server, I add the file.

If I had easy access the the vhost, and I was managing the server vhosts with Puppet, Chef, Ansible, or similar, I would use the environment variable approach like the default secrets.yml file.

Your case with AWS appears to be the latter. Ultimately, either option is fine. There is little downside to committing the secrets.yml file without a production stanza.

Mali answered 20/8, 2016 at 11:37 Comment(0)
A
0

First, all three methods can be 12-factor compatible. It is compatible if you pass the config value by ENV variable, instead of copying one file to the server first.

My thoughts are each of these solutions:

Rails secrets

Developers are forced to go 12-factor, either manually set it on production server, or have another file on local machine and then pass it as ENV every time during deploy. (Didn't know about capistrano-secrets, it probably handles this)

(I think what you said in CON #2 and #3 are the opposite to secret.yml solution)

The accessor is also quite long as you mentioned.

dotenv

It does not encourage 12-factor, and was not originally designed for production env anyways. Either you write code to pass its value as ENV to production during deploy (making it 12 factor compatible), or you copy your .env.production file to the production server.

Also it forces you to use the flat key:value configuration. No nesting.

Figaro

Though it uses YAML, it does not allow nested hash.

It is 12 factor compatible, e.g. it includes code to transfer the config to heroku.

My Solution

I wrote a gem, in which settings are stored in gitignored YAML file. Nesting is allowed. When accessing some value, do Setting.dig(:fb,:api).

It provides mechanism for 12-factor app deploy, by serializing the whole YAML file into a string and pass it to production as ENV.

I no longer have to distinguish between secret config and non-secret config. They are in one place and secret by default. I get benefit of 12-factor app while using easy to namespace YAML files.

Alphorn answered 6/3, 2018 at 14:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.