eyaml
is a tool for asymmetric encryption of YAML and JSON files. It's largely based on ejson
and backwards compatible with any *.ejson
file.
Assymetric encryption is handled by RubyCrypto/rbnacl using a sealed box.
To install eyaml
, run:
gem install eyaml
Or alternatively, you can add it to your Gemfile:
gem 'eyaml'
eyaml
depends on libsodium. At least 1.0.0
is required.
For MacOS users, libsodium is available via homebrew and can be installed with:
brew install libsodium
eyaml
requires that a file has a _public_key
attribute that corresponds to the value generated by running eyaml keygen
. Adding a plaintext value into the file and running eyaml encrypt secrets.eyaml
(for a file called secrets.eyaml
) will encrypt the value using the public key in the same file. To decrypt, ensure a private key is accessible and run eyaml decrypt secrets.eyaml
eyaml
supports both JSON and YAML with the extensions eyaml
, eyml
, and ejson
. It will using the extension to determine the format of its output.
eyaml
is primarily interacted through its CLI.
-> % eyaml help
Commands:
eyaml decrypt # Decrypt an EYAML file
eyaml encrypt # (Re-)encrypt one or more EYAML files
eyaml help [COMMAND] # Describe available commands or one specific command
eyaml keygen # Generate a new EYAML keypair
Options:
-k, [--keydir=KEYDIR] # Directory containing EYAML keys
(Re-)encrypt one or more EYAML files. This is used whenever you add a new value to the config file.
-> % eyaml encrypt config/secrets.production.eyaml
Wrote 517 bytes to config/secrets.production.eyaml.
Note: entries starting with an `_` won't be encrypted. This has to be the case for the `_public_key`, but can be handy if you want to add keys in there that you dont't want to encrypt. Like a public key for ex.
Decrypts the whole provided EYAML file.
-> % eyaml decrypt config/secrets.production.eyaml
_public_key: d1c7ba73c520445c5ba14984da8119f2f7b8df7bcdb3f37f5afe9613b118936a
secret: password
Generates the keypair for the encryption flow to work. The public key must be placed into the file at _public_key
like this:
e.g.
-> % cat config/credentials.development.eyaml
_public_key: a3dbdef9efd1e52a34588de56a6cf9b03bbc2aaf0edda145cfbd9a6370a0a849
my_secret: 85d1fca99d98c4e7b83b868f75f809e1e33346317b0c354b593cdcdc8793ad4e
The private key must be saved in the default key directory (/opt/ejson/keys
) or the EJSON_PRIVATE_KEY
must point to the right directory, with the filename being the public key and the contents, the private key, a key directory you'll provide later, or just pass the --write
flag for eyaml
to handle it for you.
-> % eyaml keygen
Public Key: a3dbdef9efd1e52a34588de56a6cf9b03bbc2aaf0edda145cfbd9a6370a0a849
Private Key: b01592942ba10f152bcf7c6b6734f6392554c578ff24cebcc62f9e3da6fcf302
# Or by using the --write flag
-> % eyaml keygen --write
Public Key: a3dbdef9efd1e52a34588de56a6cf9b03bbc2aaf0edda145cfbd9a6370a0a849
-> % cat /opt/ejson/keys/a3dbdef9efd1e52a34588de56a6cf9b03bbc2aaf0edda145cfbd9a6370a0a849
b01592942ba10f152bcf7c6b6734f6392554c578ff24cebcc62f9e3da6fcf302
eyaml
comes with baked in Rails support.
It will search for a rails secrets or credentials file in config/
, decrypt, and load the first valid one it finds.
For this a public-private keyfile needs to be present, which you can generate with eyaml keygen
. For a development/test environment you can keep this in your
version control, but on production you want to take the proper precautions since this can contain your rails master key as well.
Credential files have priority over secrets before rails 7.2:
credentials.{eyaml|eyml|ejson}
(e.g. config/credentials.eyaml
) then credentials.$env.{eyaml|eyml|ejson}
(e.g. credentials.production.eyml
).
Then if no credentials are found it will look for a secrets file:
secrets.{eyaml|eyml|ejson}
(e.g. config/secrets.eyaml
) then secrets.$env.{eyaml|eyml|ejson}
(e.g. secrets.production.eyml
).
Note: From rails 7.2 onwards secrets are deprecated and eyaml will only look for credential files.
Instead of needing a private key locally, you can provide it to EYAML by setting EJSON_PRIVATE_KEY
and it'll be automatically used for decrypting the secrets file.
If you put your rails master key encrypted in the eyaml file, make sure you don't have another master.key
file somewhere, since that can interfere.
When setting the SECRET_KEY_BASE_DUMMY environment variable, the secrets/credentials loading will be skipped. This can be handy to for example do an asset precompilation step in production where you don't need any secrets/credentials.
To add encryption + credentials to a rails project do the following things:
-
Generate a private-public keypair with (or add the --write flag and a keypair file will be written to
/opt/ejson/keys/
):eyaml keygen Public Key: a3dbdef9efd1e52a34588de56a6cf9b03bbc2aaf0edda145cfbd9a6370a0a849 Private Key: b01592942ba10f152bcf7c6b6734f6392554c578ff24cebcc62f9e3da6fcf302
For this example I show you a dev setup, but for test, production etc. it works the same.
-
Create a file with the name of the public key that contains the private key. If you don't want to add the file to the
/opt/ejson/keys/
(for for example a dev/test environment) so you can check it in with your version management you can set theEJSON_KEYDIR
to the keypair file in railsapplication.rb
like so:ENV["EJSON_KEYDIR"] = File.expand_path("../dev/ejson-keys", __dir__) unless Rails.env.production?
and rails will look there for the file decryption when the environment loads. You can test this by calling
Rails.application.credentials.secret_key_base
in a rails console and it should give you back the unencrypted key.
Note that you should not have a
config/master.key
file present (created by rails when using it's credentials management like for ex when callingrails credentials:edit
) when using eyaml. Eyaml is a replacement for rails's credentials management and currently conflicts with it. Eyaml will raise when a master.key is present. -
Create a
config/credentials.development.eyaml
file -
In the credentials file add:
_public_key: a3dbdef9efd1e52a34588de56a6cf9b03bbc2aaf0edda145cfbd9a6370a0a849
on top
-
You can then add your rails
secret_key_base
like so:secret_key_base: <secret>
And any other key you need in there.
-
Then every time you edit your eyaml file(s) run (for ex for development):
eyaml encrypt config/credentials.development.eyaml
And you can see that the key put in there is encrypted afterwards (except the ones starting with an
_
).
If you're using the new Apple M1, you need to ensure that you're using a ffi
that is working. We've temporarily been including a fork with a fix in any Gemfile
where we've included eyaml
:
gem "ffi", github: "cheddar-me/ffi", branch: "apple-m1", submodules: true
Keys that start with an underscore are treated as-is and are assumed unencrypted in the secrets/credentials files.
To make our lives a little easier in calling them in the application they are callable without the underscore. So a _secret
can be called with
Rails.application.credentials.secret
and
Rails.application.credentials._secret
To prevent conflicts with having the same name underscored and not, we don't allow that and the gem will raise an exception. This makes sense since we believe it could be a security hazard to have an encrypted key also unencrypted. The best solution is to give either a different name to make the intention clear.
To get started, make sure you have a working version of Ruby locally. Then clone the repo, and run bin/setup
(this will install libsodium
if you're on a Mac and setup bundler). Running bundle exec rake
or bundle exec rake spec
will run the test suite.