In a Ruby on Rails app, there are settings. Many settings. Some can vary from development to production. Some are private as they may be passwords, secret tokens…
This is how I deal with those, using the tiny lib SettingsLogic.
A French political party (far and ultra left wing, Gasp!) recently released the source code of their main website under GPL license on Github. As it is made with Rails, I was curious. I found a few flaws: they unintentionally leaked some credentials and security settings. This is the occasion for talking about SettingsLogic, and how to deal with sensitive settings.
SettingsLogic
SettingsLogic lets you have yaml files to store your settings.
First of all, let’s add the gem in the Gemfile
:
gem "settingslogic"
Let’s create the settings class. In app/models/settings.rb
:
# encoding: utf-8
class Settings < Settingslogic
source "#{Rails.root}/config/settings.yml"
namespace Rails.env
end
Pretty simple. I let you create the corresponding yaml file as specified in the class. Here is a template with an example:
defaults: &defaults
pagination:
comments: 10
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults
It lets you managing different values according to the environment. To use it where you need to, just call:
Settings.pagination.comments
# An example:
@post.comments.desc(:published_at).page(params[:page]).per(Settings.pagination.comments)
Voilà, the basics. Now let’s see how I manage the settings_private.yml file.
The private file
I’m used to separate general settings (pagination values…) from private settings (credentials, secret tokens…) as I don’t want the later ones to be included in my CVS that’s backed on Github or Bitbucket. These credentials are plenty: SMTP, Amazon S3, Facebook, Twitter… Besides the many tokens which include the “pepper” of Devise. And the great forgotten: the secret_token
(located in config/initializers/secret_token.rb
) that secures your cookies !
Doing that in app/models/settings_private.rb
:
# encoding: utf-8
class SettingsPrivate < Settingslogic
source "#{Rails.root}/config/settings_private.yml"
namespace Rails.env
end
And you have a settings_private.yml
file. An idea is to have a settings_private-sample.yml
that is synced with the real one. I use dummy values but the keys are synced, and it appears in my CVS. It’s cleaner and it permits to quickly generate the real one without browsing the source code of the whole app.
As it actually contains the stuff I use in dev and prod, I prevent the settings_private.yml
file from being in my CVS by adding it to the .gitignore
file:
# Do not include by mistake the private settings
/config/settings_private.yml
If you use capistrano, you can add a task that setup your private file while deploying. In my deploy path, next to the releases
and shared
directories, I create a private_files
directory and manually place a settings_private.yml
file that is setup for production. And I add a dedicated task in the config/deploy.rb
capistrano’s file. It creates a symbolic link in the right place in the release path to the private file:
namespace :deploy do
task :setup_private_files do
["settings_private.yml", "mongoid.yml"].each do |file|
run "rm -fr #{release_path}/config/#{file}"
run "cd #{release_path}/config && ln -sf #{shared_path}/../private_files/#{file} #{file}"
end
end
end
before "deploy:finalize_update", "deploy:setup_private_files"
You can see that I’m doing the same thing with my mongoid.yml
or database.yml
files.
Enjoy a simple, more secure and deployment-proof way to deal with your settings, even the secret ones.