aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/commands/credentials
diff options
context:
space:
mode:
Diffstat (limited to 'railties/lib/rails/commands/credentials')
-rw-r--r--railties/lib/rails/commands/credentials/USAGE58
-rw-r--r--railties/lib/rails/commands/credentials/credentials_command.rb112
2 files changed, 170 insertions, 0 deletions
diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE
new file mode 100644
index 0000000000..c8d3fb9eda
--- /dev/null
+++ b/railties/lib/rails/commands/credentials/USAGE
@@ -0,0 +1,58 @@
+=== Storing Encrypted Credentials in Source Control
+
+The Rails `credentials` commands provide access to encrypted credentials,
+so you can safely store access tokens, database passwords, and the like
+safely inside the app without relying on a mess of ENVs.
+
+This also allows for atomic deploys: no need to coordinate key changes
+to get everything working as the keys are shipped with the code.
+
+=== Setup
+
+Applications after Rails 5.2 automatically have a basic credentials file generated
+that just contains the secret_key_base used by MessageVerifiers/MessageEncryptors, like the ones
+signing and encrypting cookies.
+
+For applications created prior to Rails 5.2, we'll automatically generate a new
+credentials file in `config/credentials.yml.enc` the first time you run `rails credentials:edit`.
+If you didn't have a master key saved in `config/master.key`, that'll be created too.
+
+Don't lose this master key! Put it in a password manager your team can access.
+Should you lose it no one, including you, will be able to access any encrypted
+credentials.
+
+Don't commit the key! Add `config/master.key` to your source control's
+ignore file. If you use Git, Rails handles this for you.
+
+Rails also looks for the master key in `ENV["RAILS_MASTER_KEY"]`, if that's easier to manage.
+
+You could prepend that to your server's start command like this:
+
+ RAILS_MASTER_KEY="very-secret-and-secure" server.start
+
+=== Editing Credentials
+
+This will open a temporary file in `$EDITOR` with the decrypted contents to edit
+the encrypted credentials.
+
+When the temporary file is next saved the contents are encrypted and written to
+`config/credentials.yml.enc` while the file itself is destroyed to prevent credentials
+from leaking.
+
+=== Environment Specific Credentials
+
+The `credentials` command supports passing an `--environment` option to create an
+environment specific override. That override will take precedence over the
+global `config/credentials.yml.enc` file when running in that environment. So:
+
+ rails credentials:edit --environment development
+
+will create `config/credentials/development.yml.enc` with the corresponding
+encryption key in `config/credentials/development.key` if the credentials file
+doesn't exist.
+
+The encryption key can also be put in `ENV["RAILS_MASTER_KEY"]`, which takes
+precedence over the file encryption key.
+
+In addition to that, the default credentials lookup paths can be overridden through
+`config.credentials.content_path` and `config.credentials.key_path`.
diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb
new file mode 100644
index 0000000000..e23a1b3008
--- /dev/null
+++ b/railties/lib/rails/commands/credentials/credentials_command.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require "active_support"
+require "rails/command/helpers/editor"
+require "rails/command/environment_argument"
+
+module Rails
+ module Command
+ class CredentialsCommand < Rails::Command::Base # :nodoc:
+ include Helpers::Editor
+ include EnvironmentArgument
+
+ self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key"
+
+ no_commands do
+ def help
+ say "Usage:\n #{self.class.banner}"
+ say ""
+ say self.class.desc
+ end
+ end
+
+ def edit
+ extract_environment_option_from_argument(default_environment: nil)
+ require_application!
+
+ ensure_editor_available(command: "bin/rails credentials:edit") || (return)
+
+ ensure_encryption_key_has_been_added if credentials.key.nil?
+ ensure_credentials_have_been_added
+
+ catch_editing_exceptions do
+ change_credentials_in_system_editor
+ end
+
+ say "File encrypted and saved."
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
+ say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
+ end
+
+ def show
+ extract_environment_option_from_argument(default_environment: nil)
+ require_application!
+
+ say credentials.read.presence || missing_credentials_message
+ end
+
+ private
+ def credentials
+ Rails.application.encrypted(content_path, key_path: key_path)
+ end
+
+ def ensure_encryption_key_has_been_added
+ encryption_key_file_generator.add_key_file(key_path)
+ encryption_key_file_generator.ignore_key_file(key_path)
+ end
+
+ def ensure_credentials_have_been_added
+ if options[:environment]
+ encrypted_file_generator.add_encrypted_file_silently(content_path, key_path)
+ else
+ credentials_generator.add_credentials_file_silently
+ end
+ end
+
+ def change_credentials_in_system_editor
+ credentials.change do |tmp_path|
+ system("#{ENV["EDITOR"]} #{tmp_path}")
+ end
+ end
+
+ def missing_credentials_message
+ if credentials.key.nil?
+ "Missing '#{key_path}' to decrypt credentials. See `rails credentials:help`"
+ else
+ "File '#{content_path}' does not exist. Use `rails credentials:edit` to change that."
+ end
+ end
+
+
+ def content_path
+ options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc"
+ end
+
+ def key_path
+ options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key"
+ end
+
+
+ def encryption_key_file_generator
+ require "rails/generators"
+ require "rails/generators/rails/encryption_key_file/encryption_key_file_generator"
+
+ Rails::Generators::EncryptionKeyFileGenerator.new
+ end
+
+ def encrypted_file_generator
+ require "rails/generators"
+ require "rails/generators/rails/encrypted_file/encrypted_file_generator"
+
+ Rails::Generators::EncryptedFileGenerator.new
+ end
+
+ def credentials_generator
+ require "rails/generators"
+ require "rails/generators/rails/credentials/credentials_generator"
+
+ Rails::Generators::CredentialsGenerator.new
+ end
+ end
+ end
+end