Read secrets using AWS secret manager in Rails
Storing secrets in a version control system is not a good idea.
Rails provides a way to store
credentials
in an encrypted manner.
In such approach,
we need to just store secret key (called as master.key
)
separately which is used to decrypt credentials.
AWS has released a feature called AWS secret manager. It solves a few problems.
- Storing environment credentials in a secure manner
- Auto rotate secrets using AWS Lambda in an automated manner
- Storage and access of secrets in environment specific manner
Read more about AWS secrets here. To create secrets using AWS secrets manager follow this article
In this article, we will learn how to use secrets created using AWS secret manager and using those in the Rails application.
Step 1: Add aws-sdk-secretsmanager gem
To access services specific to AWS secret manager, add the gem in Rails application.
gem 'aws-sdk-secretsmanager'
Step 2: Create secret manager initializer
Create an initializer file in config/initializers
directory.
Let’s name it as secret_manager.rb
require 'aws-sdk-secretsmanager'
def set_aws_managed_secrets
# secret name created in aws secret manager
secret_name = "#{ENV['RAILS_ENV']}/repository_name/postgres/username"
# region name
region_name = 'ap-east-1'
client = Aws::SecretsManager::Client.new(region: region_name)
begin
get_secret_value_response = client.get_secret_value(secret_id: secret_name)
rescue Aws::SecretsManager::Errors::DecryptionFailure => e
raise
rescue Aws::SecretsManager::Errors::InternalServiceError => e
raise
rescue Aws::SecretsManager::Errors::InvalidParameterException => e
raise
rescue Aws::SecretsManager::Errors::InvalidRequestException => e
raise
rescue Aws::SecretsManager::Errors::ResourceNotFoundException => e
raise
else
if get_secret_value_response.secret_string
secret_json = get_secret_value_response.secret_string
secret_hash = JSON.parse(secret_json)
ENV['DATABASE_HOST'] = secret_hash['host']
ENV['DATABASE_USERNAME'] = secret_hash['username']
ENV['DATABASE_PASSWORD'] = secret_hash['password']
end
end
end
Below are a few noteworthy things:
We have created secrets specific to environment name in AWS secret manager. e. g.
staging/example/postgres/test_username
And the code below is used to define the secret name based on rails environment.
secret_name = "#{ENV['RAILS_ENV']}/repository_name/postgres/username"
We receive the json response in lines given below.
secret_json = get_secret_value_response.secret_string
secret_hash = JSON.parse(secret_json)
secret_hash
is a Hash with
values we have stored in aws secret manager.
Now, we just make those values available in ENV
variable in lines given below.
ENV['DATABASE_HOST'] = secret_hash['host']
ENV['DATABASE_USERNAME'] = secret_hash['username']
ENV['DATABASE_PASSWORD'] = secret_hash['password']
3. Use secrets from ENV
Now, wherever we need those values in the codebase (e.g. config/database.yml
),
we can directly access them with ENV['DATABASE_HOST']
, ENV['DATABASE_USERNAME']
and so on.
Posting sample database.yml
file below.
# config/database.yml.sample
default: &default
adapter: postgresql
encoding: utf8
username: "<%= ENV['DATABASE_USERNAME'] %>"
password: "<%= ENV['DATABASE_PASSWORD'] %>"
host: "<%= ENV['DATABASE_HOST'] %>"
database: "<%= ENV['DATABASE_NAME'] %>"
port: 5432
development:
<<: *default
staging:
<<: *default
test:
<<: *default
production:
<<: *default
Subscribe to Ruby in Rails
Get the latest posts delivered right to your inbox
