Active Record Encryption and Rollover with ENV's instead of credentials
Tags: rails, programming, security
Published: 2023-01-11 21:12:32

If you want to use environment variables (ENVs) instead of the credentials files because you're in a cloud service like AWS or Heroku you can still do it and rollover your keys. 

Just add your ENVs to your .env and/or .env.development , etc. like so...

```
PRIMARY_KEY=EGY8WhulUOXixybod7ZWwMIL68R9o5kC
PRIMARY_KEY_ROLLOVER= <- empty until you need to rollover
DETERMINISTIC_KEY="aPA5XyALhf75NNnMzaspW7akTfZp0lPY"
KEY_DERIVATION_SALT=xEY0dt6TZcAMg52K7O84wYzkjvbA62Hz
```
You don't need the deterministic key if you're not going to use it. I just have it for incase I want to. 

You can create your keys in the console:

`SecureRandom.alphanumeric(32)`

Now that you have the ENVs you can enter them into your Parameter Store however your cloud provider allows.

Now lets boot up our app with the correct configuration.

If you added ENVs to the .env file and you're not going to care about per environment basis then add these configs to your `application.rb`

```
  config.active_record.encryption.primary_key = ENV["PRIMARY_KEY"]
  config.active_record.encryption.deterministic_key = ENV["DETERMINISTIC_KEY"]
  config.active_record.encryption.key_derivation_salt = ENV["KEY_DERIVATION_SALT"]
```
However you probably should do everything by environment and add this to your `(environment).rb` unless your deploy process imports different ENVs based on the environment it's deploying.

``` ruby
# development.rb
  config.active_record.encryption.primary_key = ENV["PRIMARY_KEY"]
  config.active_record.encryption.deterministic_key = ENV["DETERMINISTIC_KEY"]
  config.active_record.encryption.key_derivation_salt = ENV["KEY_DERIVATION_SALT"]
```

But what about rollover?

Lets make a change to your primary key.

``` ruby 
# development.rb

  config.active_record.encryption.primary_key = [ENV["PRIMARY_KEY"], ENV["PRIMARY_KEY_ROLLOVER"]].compact_blank
  config.active_record.encryption.deterministic_key = ENV["DETERMINISTIC_KEY"]
  config.active_record.encryption.key_derivation_salt = ENV["KEY_DERIVATION_SALT"]
```

If you don't want a rollover you can just add `PRIMARY_KEY_V2, etc.` and keep the array growing.

As rails initializes the environment, it will have a single value or an array of values, the last index will be the newest key.

`.compact_blank` will clear the array of `""` values in the array for when your rollover key is empty. You only need a rollover key when you're going to rollover.

Save a new 32 char random value to your rollover ENV

`PRIMARY_KEY_ROLLOVER=LsnKbJ3Gyg9Ee2Klfq1gKa6UUR54aCh6`

Every time my rails app deploys it will load ENVs so now the rollover key isn't blank.

Now you need to write a script to encrypt your data to the rollover key. 

You can call this script from an admin or write a data migration to run it. 

Example:

``` ruby
  Documents.find_each do |document| #encrypt in batches of 1000
    document.encrypt
  end
```

Calling `.encrypt` on a document will decrypt with any key it finds that works and re-encrypt with a new key (highest array index).

For the rollover approach I would copy the primary key temporarily, and then move the rollover key to the primary key env.

```
PRIMARY_KEY=(ROLLOVER_KEY value)
PRIMARY_KEY_ROLLOVER= (empty)
```

Re deploy and all code should now be decrypting with the new rolled over key. If it didn't work add that encrypted_key that you temporarily saved back to the ENVs and fix your code, you dummy.

Remember you must add the new keys to the end of the array config.

Now you're set to rollover your keys at whatever interval you decide. Without credential.yml.

Thanks for reading.