Rails: Where to put configuration variables - ruby-on-rails

My team is maintaining a Rails application. The previous developer(s) decided to put some configuration variables (like facebook app id, twitter app id) as env variables. But this is not very versatile. If we have two applications (test and production), we are forced to use only one hardcoded configuration (or changing it manually). Also it is good practice to put these variables in the version control.
Is that the right way to do that? If not, what is the right way?

We are using following solution in 2 apps.
Store config variables in yml files for https://github.com/railsjedi/rails_config gem.
Apps are configured to use different yml files in production and dev modes via initializers.
On production servers directory with yml files are symlinked with 2 apps.
Task for capistrano looks so:
namespace :settings do
task :symlink, roles: :app do
run "ln -s /path_to_settings/#{rail_env} #{current_path}/config/settings"
end
end
after "deploy:create_symlink", "settings:symlink"

Related

How to store secret variables in capistrano

I am writing a Rails app with automated deployment using Capistrano. In the deploy.rb script I have:
set :deploy_to, '/my/deploy/path/'
And in production.rb I have:
server 'example.com', user: 'secret_user_name', roles: %w{web app db}
Currently the app is private. But assume I wanted an open source app like this. Then I wouldn't want secret_user_name and /my/deploy/path to be stored in the repo. In the Rails project if I encountered an issue like this I would store the secret values in secrets.yml and access them from there. However I don't have access to secrets.yml from Capistrano. So I could manually load the secrets file but I'm sure there is a better way to do this.
So my question is: How can I have an automated deployment process without exposing server information using Capistrano? Is there a recommended way to store secrets like there is in Rails?
If your intention is to maintain your own deployment environment (which you keep secret) but open source the code of the application itself, then I would simply move the Capistrano-related files to a separate private repository. Then you can open source the app itself, but keep the Capistrano config private.
There is no need for Capistrano's deploy.rb, etc. to live in the same directory structure or even the same repository as the app that is being deployed. After all, Capistrano deploys based on the :repo_url, which can be anything. It doesn't have to match repo where Capistrano's files are kept.
If you want to give other people (i.e. those that fork/clone the app) the ability to deploy to their own infrastructure, perhaps the easiest solution is to write up a wiki page explaining how they can set up their own Capistrano config. Deployment environments can vary widely and therefore it is probably not something you can do simply with environment variables or encrypted secrets.
In any case make sure you audit and rewrite your Git history if necessary to make sure you won't be leaking any sensitive config when you make the repo public.
Environment variables might help you.
You can put export SSH_PROD_USER=secret_user_name; in your preferred shell profile. For example if you use bash then it would be ~.bash_profile. Then use it in production.rb like this:
server 'example.com', user: ENV['SSH_PROD_USER'], roles: %w{web app db}
So basically ruby will have all your environment variables
Use something like Figaro gem: https://github.com/laserlemon/figaro
and don't push to your repo your application.yml
Or use an encrypted repo with credentials.

Rails config/local_env.yml vs .env vs bashrc environment variables

I was reading from this article that you can create a config/local_env.yml with environment variables and then use config/application.rb to read it to replace/take priority before the environment variables that you export in your .bashrc. What then is the .env used for then? Does it serve the same purpose as the config/local_env.yml?
All of these methods are used to feed environment variables to your rails application. So, from an app's point of view, it serves the same purpose whether you export it from .env or .bashrc or config/local_env.yml files.
The differences in these methods are really a matter of personal choice among the team members involved in maintaining the app's development and deployment environments. However, here are few things to consider while opting for either of these choices.
.bashrc - Use this if you manually configure deployment servers and really comfortable with linux/unix command line system administration. This configuration file is specific to bash shell (Bourne Again Shell). You need to configure different file if your server uses different shell (for example: .zshrc if it uses Z Shell)
.env - Use this if you want to keep your app centric environment variables within the app itself while maintaining different variations of environment variables for different runtime environments of your rails app. For example: .env.development and .env.test files with specific values of the environment variables for your development and test environments respectively. This gives you more control of your app's environment variables and do not have to rely on the platform (terminal shell) you want to deploy your app.
config/local_env.yml - This is similar to .env approach, which is provided out of the box by rails gem that allows you to configure environment variables for your app in yml format. This method also keeps your app's configuration within the app irrespective of the shell you are using to run your app.
In addition to the previous answer, another downside of using .bashrc is that it is specific to only one user, so if you're e.g. starting your app server as a systemd service then I believe it won't see your variables.
Meanwhile, .env's Github readme says that it is not the most recommended thing to use outside of the development environment, although it is OK for that purpose.
Another two options to consider are:
Rails secrets. The benefit of this approach is that you get to commit it to git since it's encrypted, so when working in a team you will all have access to the same file. The downside is that it is coupled to the RAILS_ENV variable, so you can't use it to set that e.g. to production on your production app (but you can manually pass it every time it's invoked). Another downside is that if you have a staging environment, then apparently Heroku discourages using RAILS_ENV=staging, which you sometimes really need, so if you need it then you can either do it anyway, or you'll need to set the differing variables via a different mechanism - for example my app has a variable which points to the URL of another part of my app, this URL needs to point to its staging variant on staging, and to its production variant on production, so it needs to differ between my production and staging environment.
Using an /etc/environment file - note that systemd services don't have access to it by default so you'd have to add the line EnvironmentFile=/etc/environment. Also if you're running a shell script from a non-login shell (which happens sometimes), they it won't load them either, but the solution is to just include in your script this: set -a; source /etc/environment; set +a. You should be careful not to commit this to git. If you're working in a team and you need to manage this file then it gets a little complicated since it's not committed to git, but maybe there's a way to have it encrypted. (systemd services note: you might optionally use LoadCredential= for sensitive variables such as private keys, so that other processes won't have access to them; if you do this then you can also commit to git /etc/environment which should now contain non-sensitive variables only).
In my opinion both of those are valid and it's fine to pick whatever is easier to do in your app.

where to put secure passwords/keys in a rails app?

I have a few web services that require secure tokens/keys/passwords to be passed in. Where should I define these secure values for my rails app to see? I want the development keys in version control, but don't want the production keys in version control. How should I set this up? I'm new to rails.
You see the question properly.
Put your passwords and keys in some yml file excluded from version control.
Then on your production server, create the very same file and symlink your app to it every time you deploy.
EDIT.
Capistrano is almost made to fits these needs:
put your yml files in the shared folder
In your capistrano deploy.rbfile:
after 'deploy' do
run "ln -s #{shared_path}/database.yml #{release_path}/config/database.yml"
end
to work with yml files: http://railscasts.com/episodes/85-yaml-configuration-file
apneadiving is right, symlinking the files is a good idea. Another approach is to put the keys in the shell variables, accessible only to the user that runs the app. Then, in your rails app you'll have
login = ENV['SERVICE_LOGIN']
password = ENV['SERVICE_PASSWORD']
As of Rails 4.1.0, check out secrets.yml.

Managing security for an open source rails 3 application stored at github

New to rails, open source and soon ready for deploying to a production environment, I have some security considerations.
How to handle the database.yml is covered pretty good by, how-to-manage-rails-database-yml
But from my point of view there are more configuration settings in a normal rails application that shouldn't be hosted in a public github repository and deployed to production e.g.
devise.rb -> config.pepper
secret_token.rb -> Application.config.secret_token
capistrano -> deploy.rb
...
Adding config/****/* to .gitignore would not only prevent new developers from bundle install, db:create, db:migrate, rails server but also to keep the production config up to date if a new gem with an initializer is installed.
Another possibility would be add an environment.yml with sensitive config, like database.yml where sensitive configuration in the initializers will be overridden?
This will make it easy to get up and running after a clean checkout and the production environment will be easy to maintain.
Any ideas how to approach my problems above?
I usually put "safe" data in these files, which will usually work for development purposes. But in production I symlink the files to another location with capistrano, like this:
invoke_command "ln -sf #{shared_path}/database.yml #{release_path}/config/database.yml"
So in the production server I have a bunch of files that override the files in source control. I don't even work with a database.yml.example, just some sane default database.yml that the developers agree upon to use in development and test.
For individual settings, like API keys, I usually create a config/settings.yml and read them from inside the initializer:
SETTINGS = YAML.load(IO.read(Rails.root.join("config", "settings.yml")))
YourApp::Application.config.secret_token = SETTINGS["secret_token"]

Deploying a rails app to multiple locations

I'm trying to deploy the same rails app to two different locations with different app names, different logos, different stylesheets, etc.
I've got the code working based on an APP_NAME and a HOST_NAME variable I store in environments/production.rb. Now I need to actually deploy it, and I need a better solution than manually editing the environment file on the production machine.
The only way I can see to do it is to create a new production environment - e.g. production_app2 - and define APP_NAME and HOST_NAME differently in them. Is there a better way?
No no no! Don't edit the environment files. I mean, edit them as you need to for things that need to be configured the same for every deployment, but not for things that should be configurable between deployments.
For that, use configuration.
Throw a YAML file in config that looks something like this:
development:
:app_name: App 1
:host_name: something.com
test:
:app_name: App 1
:host_name: something.com
production:
:app_name: App 1
:host_name: something.com
Call it whatever makes sense. Let's say settings.yml.
Now load it with an initializer in config/initializers/settings.rb that looks like this:
SETTINGS = YAML.load_file("#{RAILS_ROOT}/config/settings.yml")[RAILS_ENV]
Now access your configuration like this:
SETTINGS[:app_name]
(If you don't want to change your existing code at all, inside config/initializers/settings.rb add lines that set your existing names like APP_NAME = SETTINGS[:app_name], etc.)
Note that this is one possible implementation of settings configuration, but even if another approach is taken it should be based on deployment-independent configuration. This can be much more easily and maintainably set up to persist between deployments and upgrades than mucking with environment files.
Again, to recap:
environment files are for configuration that is the same across all deployments
configuration files are for configuration that can change between deployments
Update
For Capistrano based deployments, this is what I use to symlink multiple configuration files in the new current from the shared directory (I think it originally came from an Ezra recipe from EngineYard):
after "deploy:update_code","deploy:symlink_configs"
namespace(:deploy) do
task :symlink_configs, :roles => :app, :except => {:no_symlink => true} do
configs = %w{ database settings }
configs.map! { |file| "ln -nfs #{shared_path}/config/#{file}.yml #{release_path}/config/#{file}.yml" }
run <<-CMD
cd #{release_path} && #{configs.join(' && ')}
CMD
end
end
I think that's a pretty good way.
Where we are we define different environments (e.g. 'staging', 'production', 'production_backup' - giving us a staging.rb, production.rb, production_backup.rb where you can define your specific APP_NAMEs and HOST_NAMEs) and can deploy to each of them using Capistrano. It works just fine.
This is a good link on it: http://www.egtheblog.com/?p=8
Because you are actually deploying to two different environments, it seems best to create two different environment files, each with their own settings. Make sure you pick descriptive names for your environment files, not just production2.
You could also store this information in the database, but I don't know if you're willing to accept such a dependency. I guess using a database would only make sense if the number of deployments is too large to manage easily with a few environment files.

Resources