Active KMS

Simple, secure key management for Active Record encryption

Note: This project is experimental until Rails 7 is released. At the moment, encryption requires three encryption requests and one decryption request. See this Rails issue for more info. As a result, there’s no way to grant encryption and decryption permission separately.

Build Status

Installation

Add this line to your application’s Gemfile:

gem "active_kms"

And follow the instructions for your key management service:

AWS KMS

Add this line to your application’s Gemfile:

gem "aws-sdk-kms"

Create an Amazon Web Services account if you don’t have one. KMS works great whether or not you run your infrastructure on AWS.

Create a KMS master key and set it in your environment along with your AWS credentials (dotenv is great for this)

KMS_KEY_ID=alias/my-key
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...

And add to config/application.rb:

config.active_record.encryption.key_provider = ActiveKms::AwsKeyProvider.new(key_id: ENV["KMS_KEY_ID"])

Google Cloud KMS

Add this line to your application’s Gemfile:

gem "google-cloud-kms"

Create a Google Cloud Platform account if you don’t have one. KMS works great whether or not you run your infrastructure on GCP.

Create a KMS key ring and key and set it in your environment along with your GCP credentials (dotenv is great for this)

KMS_KEY_ID=projects/my-project/locations/global/keyRings/my-key-ring/cryptoKeys/my-key

And add to config/application.rb:

config.active_record.encryption.key_provider = ActiveKms::GoogleCloudKeyProvider.new(key_id: ENV["KMS_KEY_ID"])

Vault

Add this line to your application’s Gemfile:

gem "vault"

Enable the transit secrets engine

vault secrets enable transit

And create a key

vault write -f transit/keys/my-key

Set it in your environment along with your Vault credentials (dotenv is great for this)

KMS_KEY_ID=my-key
VAULT_ADDR=http://127.0.0.1:8200
VAULT_TOKEN=secret

And add to config/application.rb:

config.active_record.encryption.key_provider = ActiveKms::VaultKeyProvider.new(key_id: ENV["KMS_KEY_ID"])

Per-Attribute Keys

Specify per-attribute keys

class User < ApplicationRecord
  encrypts :email, key_provider: ActiveKms::AwsKeyProvider.new(key_id: "...")
end

Testing

For testing, you can prevent network calls to KMS by adding to config/environments/test.rb:

config.active_record.encryption.key_provider = ActiveKms::TestKeyProvider.new

Key Rotation

Key management services allow you to rotate the master key without any code changes.

  • For AWS KMS, you can use automatic key rotation
  • For Google Cloud, use the Google Cloud Console or API
  • For Vault, use:
vault write -f transit/keys/my-key/rotate

New data will be encrypted with the new master key version.

Switching Keys

You can change keys within your current KMS or move to a different KMS without downtime.

Set globally in config/application.rb:

config.active_record.encryption.previous = [{key_provider: ActiveKms::AwsKeyProvider.new(key_id: "...")}]

Or per-attribute:

class User < ApplicationRecord
  encrypts :email, previous: [{key_provider: ActiveKms::AwsKeyProvider.new(key_id: "...")}]
end

Reference

Specify a client

ActiveKms::AwsKeyProvider.new(client: Aws::KMS::Client.new, ...)
# or
ActiveKms::GoogleCloudKeyProvider.new(client: Google::Cloud::Kms.key_management_service, ...)
# or
ActiveKms::VaultKeyProvider.new(client: Vault::Client.new, ...)

History

View the changelog

Contributing

Everyone is encouraged to help improve this project. Here are a few ways you can help:

To get started with development:

git clone https://github.com/ankane/active_kms.git
cd active_kms
bundle install
bundle exec rake test