philippe_b
philippe_b

Reputation: 41388

How to manage credentials with a multi-stages, single-environment Rails app?

TL; DR

How to use production.yml.enc and staging.yml.enc credential files in two production- and staging- Rails apps, while the app has only the regular development, test and production environments?

I'm using Heroku and refer to it in this question. Yet this is not specific to this vendor.

In detail

An application is often deployed multiple times. An instance serves as the production, while another is the staging app, expected to be put in production. Rails facilitates this pattern, since creating new environments is easy.

However, Heroku suggests not to do this, with good reasons. For example, one can be tempted to put some if Rails.env.production? here and there, paving the way for some "but it works in staging!?" on Friday evening. It is better to have a single production environment, with different sets of parameters to actually differentiate the stages (eg. a different AWS S3 bucket name, a different API key, etc.). To achieve this, Heroku's advice is to rely on environment variables.

Since Rails 5.2 and later in Rails 6, credentials are conveniently handled via encrypted Yaml files in config/credentials. This is typically where one would like to store all these variables that change from an environment to another, instead of using messy environment variables. This mechanism can be used in Heroku thanks to a single RAILS_MASTER_KEY environment variable that contains the key used to decrypt the credentials file.

But these pieces do not fit well. How can we have a single production environment, whereas credentials files are per-environment?

Upvotes: 7

Views: 2303

Answers (2)

philippe_b
philippe_b

Reputation: 41388

This can be implemented with a staging credentials file and key, and a switch in application.rb based on an environment variable:

  • Create and populate a staging credentials file and key with EDITOR=vi rails credentials:edit --environment staging.

  • In application.rb, add:

    # Introduce the environment variable RAILS_CREDENTIALS_ENVIRONMENT to specify a custom
    # environment name of which to use the credentials file. Specifically aimed to use in staging,
    # where the environment is set to "production", but we need to use the "staging" environment variables.
    # This environment variable is also used to select the corresponding key file or -if that does not exist-
    # the corresponding environment variable.
    if ENV['RAILS_CREDENTIALS_ENVIRONMENT'].present?
      new_filename_no_ext = Rails.root.join 'config', 'credentials', ENV['RAILS_CREDENTIALS_ENVIRONMENT']
      config.credentials.content_path = "#{new_filename_no_ext}.yml.enc"
    
      if File.exist? "#{new_filename_no_ext}.key"
        config.credentials.key_path = "#{new_filename_no_ext}.key"
      end
    end
    
  • Set an environment variable RAILS_CREDENTIALS_ENVIRONMENT to production or staging, depending on the case. For example, Heroku does this with:

    heroku config:set -a theapp-staging RAILS_CREDENTIALS_ENVIRONMENT=staging
    
  • If on staging or produciton you want to store your key in an environment variable instead of a key file, then simply assign the key to the RAILS_MASTER_KEY environment variable. As documented, this takes precendence over keys stored in files. Note that on your development machine you wouldn't want to have a RAILS_MASTER_KEY set, otherwise the credential files for all environments still get the same key and are thus accessible by everyone that needs to have access to (e.g.) only the development credentials.

Upvotes: 7

JRichardsz
JRichardsz

Reputation: 16564

Have several files one by each environment in your source code repository o local server disk, works but you will have several files with hardcoded values.

But, if you expect several teams with several requirements on your single app, you will need several development and test environments in order to keep an independent teams which are fully responsible for their Services:

  • Development
  • Release / Deployment
  • Ops (not platform/system administration)

An approach to management this is : externalize your configurations on platforms called: Configurations Manager

This platforms, must have the following features:

  • key-vaue pair creation by app. Like heroku web dashboard
  • expose http endpoint to get this variables from remote apps
  • security

Your rails app must get the variables at the startup stage or instantaneous if your language support hot reload variables.

Here some Configurations Managers:

Whit this approach you will have a platform to management several apps for several environments like heroku web variables creation but more sophisticated.

Upvotes: 1

Related Questions