Skip to content

Ejson-compatible encrypted secrets

License

Notifications You must be signed in to change notification settings

cheddar-me/eyaml

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

eyaml

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.

Installation

To install eyaml, run:

gem install eyaml

Or alternatively, you can add it to your Gemfile:

gem 'eyaml'

Dependencies

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

Usage

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.

CLI

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

eyaml encrypt

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

eyaml decrypt

Decrypts the whole provided EYAML file.

-> % eyaml decrypt config/secrets.production.eyaml
_public_key: d1c7ba73c520445c5ba14984da8119f2f7b8df7bcdb3f37f5afe9613b118936a
secret: password

eyaml keygen

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

Rails

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.

Example setup

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 the EJSON_KEYDIR to the keypair file in rails application.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 calling rails 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 _).

Apple M1 Support

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

Underscored vs de-underscored keys

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.

Development

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.