Rails 5.2 credentials:edit doesn't like secret_key_base - ruby-on-rails

I've been trying to debug my credentials file in my staging server. Whenever I try to edit the credentials on my staging server, I get the following error:
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:583:in `validate_secret_key_base': `secret_key_base` for staging environment must be a type of String`
My database.yml file looks like the following:
---
default: &default
adapter: postgresql
development:
<<: *default
database: dev_db
host: <%= Rails.application.credentials.database.fetch(:development).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:development).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:development).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:development) %>
test:
<<: *default
database: test_db
host: <%= Rails.application.credentials.database.fetch(:development).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:development).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:development).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:development) %>
staging:
<<: *default
database: <%= Rails.application.credentials.database.fetch(:staging).fetch(:name) %>
host: <%= Rails.application.credentials.database.fetch(:staging).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:staging).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:staging).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:staging) %>
production:
<<: *default
database: <%= Rails.application.credentials.database.fetch(:production).fetch(:name) %>
host: <%= Rails.application.credentials.database.fetch(:production).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:production).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:production).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:production) %>
I think my staging's secret_key_base is of type String. I generated my secret_key_base using rails secret. Locally, when I bring up the rails console, I can view the secret_key_bases for my staging environment:
[1] pry(main)> Rails.application.credentials.secret_key_base.fetch(:staging)
\=> "generated_using_rails_secret"
It returns a string but I still get the error message above whenever I try to access credentials in my staging environment.

I ended up looking at the stack trace and digging into the railties-5.2.0 gem.
Abbreviated stack trace:
ArgumentError: `secret_key_base` for staging environment must be a type of String`
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:583:in `validate_secret_key_base'
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:432:in `secret_key_base'
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:176:in `key_generator'
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:205:in `message_verifier'
I ended up looking in railties-5.2.0/lib/rails/application.rb:432: and seeing the following bit of code:
# The secret_key_base is used as the input secret to the application's key generator, which in turn
# is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
#
# In test and development, this is simply derived as a MD5 hash of the application's name.
#
# In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
# then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
# the correct place to store it is in the encrypted credentials file.
def secret_key_base
if Rails.env.test? || Rails.env.development?
Digest::MD5.hexdigest self.class.name
else
validate_secret_key_base(
ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
)
end
end
I had mistakenly thought I could specify a SECRET_KEY_BASE for an individual environment. Instead, I could only specify one secret key base. The secret key base apparently has nothing to do with database.yml. I need to read up on it and what it actually does.

If you run rails credentials:edit from the command line it will decrypt the config/credentials.yml.enc file.
You can then edit this file to add environment based secret keys like you would have previously added to config/secrets.yml.
When you save this file it will be encrypted again with the new information included.
There is no reason to have the "secret_key_base" in your database.yml file as this will not have any impact.
Nice Article on the new Rails credentials
Additionally just because rails now longer generates a config/secrets.yml file for you, as of rails 5.2, adding one will still work appropriately as it has in previous releases.

Related

make rake db:create setup another database besides development, test or production

I'm using rails 4.2 and trying to configure (in a already established application) the Audited Gem following this second database approach.
My config/database.yml file was as follows:
default: &default
adapter: mysql2
pool: 5
timeout: 5000
development:
<<: *default
host: <%= ENV["MYSQL_HOST"] %>
username: <%= ENV["MYSQL_USER"] %>
password: <%= ENV["MYSQL_PASSWORD"] %>
database: <%= ENV["MYSQL_DATABASE"] %>
test:
<<: *default
host: <%= ENV["MYSQL_HOST"] %>
username: <%= ENV["MYSQL_USER"] %>
password: <%= ENV["MYSQL_PASSWORD"] %>
database: <%= ENV['TEST_ENV_DB'] %>
And I intend to make it work for another db, besides development, test or production. However the task rake db:create only creates my development and test database. Is this possible to accomplish in my rails version?
audition:
<<: *default
host: <%= ENV["MYSQL_HOST"] %>
username: <%= ENV["MYSQL_USER"] %>
password: <%= ENV["MYSQL_PASSWORD"] %>
database: <%= ENV["AUDITION_DATABASE"] %>
Note the new name for audition database
if you want to read/write to a seconds database in rails < 6
create a module
module AuditionConn
def self.included(base)
base.class_eval do
if Rails.env == 'development'
establish_connection "audition-development" # database.yml
else
establish_connection "audition-production" # database.yml
end
end
end
end
then include it in any model you want to read/write from/to auditions database
class AuditionDBModel < ActiveRecord::Base
include AuditionConn
end
migration for second database
def up
AuditionDBModel.connection.create_table ... do |t|
...
AuditionDBModel.connection.change_column ...
end
I think you want to create a new environment call audition, Right?!.
Clone an existing environment file for instance, config/environments/test.rb and rename it config/environments/audition.rb
Add a new configuration block in config/database.yml for your environment.
Update any other configuration file you might have under the config folder with your new environment, some gems need to config it.
Now you can start the server
rails server -e audition
I think this may help you:
create another model for audit:
class AuditModel < ActiveRecord::Base
connects_to database: { writing: :audit_db, reading: :audit_db}
end
or
ActiveRecord::Base.establish_connection(
adapter: "mysql2",
host: "localhost",
username: "myuser",
password: "mypass",
database: "somedatabase"
)
for details:
https://guides.rubyonrails.org/active_record_multiple_databases.html
https://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionHandling.html

How to encrypt environment variables in Rails <= 4

What is the best way to encrypt and use by decrypting ENV variables whenever needed.
Example config/database.yml
development:
adapter: mysql2
encoding: utf8
host: <%= ENV['DB_HOST'] %>
database: <%= ENV['DB'] %>
pool: 5
username: <%= ENV['DB_USERNAME'] %>
password: <%= ENV['DB_PASSWORD'] %>
and .env has
DB_HOST=test.com
DB=testapp_db
DB_USERNAME=test_db_user
DB_PASSWORD=test_password_hard
My question is I want to encrypt DB_PASSWORD=test_password_hard to something like DB_PASSWORD=xadbcxedaxdcda and decrypt ENV['DB_PASSWORD'] while using.
I know Rails 5.2 onwards we can encrypt and use the credentials
Is there a way to achieve this in older rails version <= 4?

Can't connect to PostgreSQL with Rails and Capistrano

I'm trying to deploy my Rails 5 application using Postgres to a VPS via Capistrano. It keeps failing, though - giving me a PG::ConnectionBad: FATAL: password authentication failed for user 'sys_user'.
The relevant settings of my database.yml are:
production:
<<: *default
database: <%= ENV['RDS_DB_NAME'] %>
username: <%= ENV['RDS_USERNAME'] %>
password: <%= ENV['RDS_PASSWORD'] %>
host: <%= ENV['RDS_HOSTNAME'] %>
port: <%= ENV['RDS_PORT'] %>
All these environment variables have been set in /etc/environment - this is definitely the case because it's picking up the RDS_USERNAME as sys_user. The password of the database is the same as the variable RDS_PASSWORD. The port, hostname etc. are all the same also.
I'm stumped. Please help.
Where is the Postgres instance running? I'm guessing that you're trying to migrate an application from Elastic Beanstalk to a non-Amazon host. If that's the case, and your Postgres instance is not actually RDS, you might need to enable password authentication in your config file.
Documentation on Postgres configuration can be found here; I suspect you might need to change ident to md5.

rake not seeing Rails.application.secrets

In short:
seems that rake does not have access to Rails.application.secrets in config/database.yml file
what is the purpose of config/secrets.yml then?
In long:
When I run
RAILS_ENV=production rake db:migrate
I get the error Mysql2::Error: Access denied for user 'root'#'localhost' (using password: NO), though I specified appropriate values in config/database.yml and the user connecting should not be 'root'. This is an excerpt from respective config files:
# config/database.yml
production:
<<: *default
adapter: mysql2
host: localhost
database: <%= Rails.application.secrets[:database][:name] %>
username: <%= Rails.application.secrets[:database][:username] %>
password: <%= Rails.application.secrets[:database][:password] %>
# config/secrets.yml
production:
secret_key_base: very-long-blah-blah-blah
database:
name: app_db_name
username: app_db_user
password: app_db_password
Seems that rake has no access to Rails.application.secrets. Running migration succeeds when I explicitly put necessary values in database.yml, for example, as follows:
production:
<<: *default
adapter: mysql2
host: localhost
database: <%= Rails.application.secrets[:database][:name] || 'app_db_name' %>
username: <%= Rails.application.secrets[:database][:username] || 'app_db_user' %>
password: <%= Rails.application.secrets[:database][:password] || 'app_db_password' %>
The above proves that Rails.application.secrets[:database][:name] resolves to nothing.
How to have access to Rails.application.secrets in rake? Would this be the correct solution?
I know that I can use ENV[VARNAME] to fill in secret sections of config/database.yml. But what the the purpose of config/secrets.yml file then?
Moreover, I am using Passenger, which means that variables in .bashrc will probably not be accessible to the web server (I had this issue with secret_key_base). Therefore I try to avoid using environment variable. Just do not want to have all my secrets spilled all over the server.
rails-4.2.2, Ubuntu LTS 14.04
I haven't seen such nested content for the secrets.yml like you have, also the release notes doesn't have such kind. You should be just fine with the below code
# config/secrets.yml
production:
secret_key_base: very-long-blah-blah-blah
name: app_db_name
username: app_db_user
password: app_db_password
And in the database.yml
# config/database.yml
production:
<<: *default
adapter: mysql2
host: localhost
database: <%= Rails.application.secrets.name %>
username: <%= Rails.application.secrets.username %>
password: <%= Rails.application.secrets.password %>

Provide SSL certificate to PostgreSQL in a Rails app

I have a Rails app on Elastic Beanstalk using an Amazon RDS PostgreSQL instance.
I'd like pg to use SSL to connect to this DB.
Following http://docs.aws.amazon.com/AmazonRDS/[...], I saved rds-combined-ca-bundle.pem at /config/ca/rds.pem and my database.yml looks like this:
production:
adapter: postgresql
database: <%= ENV['DB_NAME'] %>
username: <%= ENV['DB_USERNAME'] %>
password: <%= ENV['DB_PASSWORD'] %>
host: <%= ENV['DB_ADDRESS'] %>
port: <%= ENV['DB_PORT'] %>
sslmode: 'require'
sslrootcert: 'config/ca/rds.pem'
But I have no idea if it's really using SSL: I can change sslrootcert path to anything, and my app is still up. What am I missing?
In your database.yml you have to use sslmode: 'verify-full' instead of sslmode: 'require' in order to verify the instance endpoint against the endpoint in the SSL certificate. This way the certificate is used.

Resources