diff options
Diffstat (limited to 'railties/lib/rails/commands')
4 files changed, 142 insertions, 14 deletions
diff --git a/railties/lib/rails/commands/generate/generate_command.rb b/railties/lib/rails/commands/generate/generate_command.rb index aa8dab71b0..2718b453a8 100644 --- a/railties/lib/rails/commands/generate/generate_command.rb +++ b/railties/lib/rails/commands/generate/generate_command.rb @@ -4,6 +4,9 @@ module Rails module Command class GenerateCommand < Base # :nodoc: def help + require_application_and_environment! + load_generators + Rails::Generators.help self.class.command_name end diff --git a/railties/lib/rails/commands/secrets/USAGE b/railties/lib/rails/commands/secrets/USAGE new file mode 100644 index 0000000000..4b7deb4e2a --- /dev/null +++ b/railties/lib/rails/commands/secrets/USAGE @@ -0,0 +1,52 @@ +=== Storing Encrypted Secrets in Source Control + +The Rails `secrets` commands helps encrypting secrets to slim a production +environment's `ENV` hash. It's also useful for atomic deploys: no need to +coordinate key changes to get everything working as the keys are shipped +with the code. + +=== Setup + +Run `bin/rails secrets:setup` to opt in and generate the `config/secrets.yml.key` +and `config/secrets.yml.enc` files. + +The latter contains all the keys to be encrypted while the former holds the +encryption key. + +Don't lose the 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 +secrets. +Don't commit the key! Add `config/secrets.yml.key` to your source control's +ignore file. If you use Git, Rails handles this for you. + +Rails also looks for the 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="im-the-master-now-hahaha" server.start + + +The `config/secrets.yml.enc` has much the same format as `config/secrets.yml`: + + production: + secret_key_base: so-secret-very-hidden-wow + payment_processing_gateway_key: much-safe-very-gaedwey-wow + +But that's where the similarities between `secrets.yml` and `secrets.yml.enc` +end, e.g. no keys from `secrets.yml` will be moved to `secrets.yml.enc` and +be encrypted. + +A `shared:` top level key is also supported such that any keys there is merged +into the other environments. + +=== Editing Secrets + +After `bin/rails secrets:setup`, run `bin/rails secrets:edit`. + +That command opens a temporary file in `$EDITOR` with the decrypted contents of +`config/secrets.yml.enc` to edit the encrypted secrets. + +When the temporary file is next saved the contents are encrypted and written to +`config/secrets.yml.enc` while the file itself is destroyed to prevent secrets +from leaking. diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb new file mode 100644 index 0000000000..3ba8c0c85b --- /dev/null +++ b/railties/lib/rails/commands/secrets/secrets_command.rb @@ -0,0 +1,36 @@ +require "active_support" +require "rails/secrets" + +module Rails + module Command + class SecretsCommand < Rails::Command::Base # :nodoc: + def help + say "Usage:\n #{self.class.banner}" + say "" + say self.class.desc + end + + def setup + require "rails/generators" + require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" + + Rails::Generators::EncryptedSecretsGenerator.start + end + + def edit + require_application_and_environment! + + Rails::Secrets.read_for_editing do |tmp_path| + puts "Waiting for secrets file to be saved. Abort with Ctrl-C." + system("\$EDITOR #{tmp_path}") + end + + puts "New secrets encrypted and saved." + rescue Interrupt + puts "Aborted changing encrypted secrets: nothing saved." + rescue Rails::Secrets::MissingKeyError => error + say error.message + end + end + end +end diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index d58721f648..1fa27a3155 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -99,8 +99,9 @@ module Rails class_option :port, aliases: "-p", type: :numeric, desc: "Runs Rails on the specified port.", banner: :port, default: 3000 - class_option :binding, aliases: "-b", type: :string, default: "localhost", - desc: "Binds Rails to the specified IP.", banner: :IP + class_option :binding, aliases: "-b", type: :string, + desc: "Binds Rails to the specified IP - defaults to 'localhost' in development and '0.0.0.0' in other environments'.", + banner: :IP class_option :config, aliases: "-c", type: :string, default: "config.ru", desc: "Uses a custom rackup configuration.", banner: :file class_option :daemon, aliases: "-d", type: :boolean, default: false, @@ -133,28 +134,64 @@ module Rails no_commands do def server_options { - server: @server, - log_stdout: @log_stdout, - Port: port, - Host: host, - DoNotReverseLookup: true, - config: options[:config], - environment: environment, - daemonize: options[:daemon], - pid: pid, - caching: options["dev-caching"], - restart_cmd: restart_command + user_supplied_options: user_supplied_options, + server: @server, + log_stdout: @log_stdout, + Port: port, + Host: host, + DoNotReverseLookup: true, + config: options[:config], + environment: environment, + daemonize: options[:daemon], + pid: pid, + caching: options["dev-caching"], + restart_cmd: restart_command } end end private + def user_supplied_options + @user_supplied_options ||= begin + # Convert incoming options array to a hash of flags + # ["-p", "3001", "-c", "foo"] # => {"-p" => true, "-c" => true} + user_flag = {} + @original_options.each_with_index { |command, i| user_flag[command] = true if i.even? } + + # Collect all options that the user has explicitly defined so we can + # differentiate them from defaults + user_supplied_options = [] + self.class.class_options.select do |key, option| + if option.aliases.any? { |name| user_flag[name] } || user_flag["--#{option.name}"] + name = option.name.to_sym + case name + when :port + name = :Port + when :binding + name = :Host + when :"dev-caching" + name = :caching + when :daemonize + name = :daemon + end + user_supplied_options << name + end + end + user_supplied_options << :Host if ENV["Host"] + user_supplied_options << :Port if ENV["PORT"] + user_supplied_options.uniq + end + end + def port ENV.fetch("PORT", options[:port]).to_i end def host - ENV.fetch("HOST", options[:binding]) + unless (default_host = options[:binding]) + default_host = environment == "development" ? "localhost" : "0.0.0.0" + end + ENV.fetch("HOST", default_host) end def environment |