Skip to content

Commit 8c5978c

Browse files
p8rafaelfranca
authored andcommitted
Use secret_key_base from ENV or credentials when present locally
Since 0c76f17 a `tmp/local_secret.txt` is always used for `Rails.config.secret_key_base` in the test and development environments, where previously `ENV["SECRET_KEY_BASE"]` or `Rails.application.credentials.secret_key_base` were used if present. The race condition in generating the local secret file, as reported in rails#53661, should be avoided by setting `ENV["SECRET_KEY_BASE"]` or `Rails.application.credentials.secret_key_base`, but this currently doesn't work. When ENV["SECRET_KEY_BASE"] or `Rails.application.credentials.secret_key_base` is set for test or development, it should be used for the `Rails.config.secret_key_base`, instead of generating a `tmp/local_secret.txt`.
1 parent 916f984 commit 8c5978c

File tree

4 files changed

+72
-17
lines changed

4 files changed

+72
-17
lines changed

railties/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
* Use `secret_key_base` from ENV or credentials when present locally.
2+
3+
When ENV["SECRET_KEY_BASE"] or
4+
`Rails.application.credentials.secret_key_base` is set for test or
5+
development, it is used for the `Rails.config.secret_key_base`,
6+
instead of generating a `tmp/local_secret.txt` file.
7+
8+
*Petrik de Heus*
9+
110
* The authentication generator's `SessionsController` sets the `Clear-Site-Data` header on logout.
211

312
By default the header will be set to `"cache","storage"` to help prevent data leakage after

railties/lib/rails/application.rb

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -457,18 +457,21 @@ def config # :nodoc:
457457
# is used to create all ActiveSupport::MessageVerifier and ActiveSupport::MessageEncryptor instances,
458458
# including the ones that sign and encrypt cookies.
459459
#
460-
# In development and test, this is randomly generated and stored in a
461-
# temporary file in <tt>tmp/local_secret.txt</tt>.
460+
# We look for it first in <tt>ENV["SECRET_KEY_BASE"]</tt>, then in
461+
# +credentials.secret_key_base+. For most applications, the correct place
462+
# to store it is in the encrypted credentials file.
462463
#
463-
# You can also set <tt>ENV["SECRET_KEY_BASE_DUMMY"]</tt> to trigger the use of a randomly generated
464-
# secret_key_base that's stored in a temporary file. This is useful when precompiling assets for
465-
# production as part of a build step that otherwise does not need access to the production secrets.
464+
# In development and test, if the secret_key_base is still empty, it is
465+
# randomly generated and stored in a temporary file in
466+
# <tt>tmp/local_secret.txt</tt>.
466467
#
467-
# Dockerfile example: <tt>RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile</tt>.
468+
# Generating a random secret_key_base and storing it in
469+
# <tt>tmp/local_secret.txt</tt> can also be triggered by setting
470+
# <tt>ENV["SECRET_KEY_BASE_DUMMY"]</tt>. This is useful when precompiling
471+
# assets for production as part of a build step that otherwise does not
472+
# need access to the production secrets.
468473
#
469-
# In all other environments, we look for it first in <tt>ENV["SECRET_KEY_BASE"]</tt>,
470-
# then +credentials.secret_key_base+. For most applications, the correct place to store it is in the
471-
# encrypted credentials file.
474+
# Dockerfile example: <tt>RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile</tt>.
472475
def secret_key_base
473476
config.secret_key_base
474477
end

railties/lib/rails/application/configuration.rb

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -510,16 +510,18 @@ def colorize_logging=(val)
510510

511511
def secret_key_base
512512
@secret_key_base || begin
513-
self.secret_key_base = if generate_local_secret?
513+
self.secret_key_base = if ENV["SECRET_KEY_BASE_DUMMY"]
514514
generate_local_secret
515515
else
516-
ENV["SECRET_KEY_BASE"] || Rails.application.credentials.secret_key_base
516+
ENV["SECRET_KEY_BASE"] ||
517+
Rails.application.credentials.secret_key_base ||
518+
(Rails.env.local? && generate_local_secret)
517519
end
518520
end
519521
end
520522

521523
def secret_key_base=(new_secret_key_base)
522-
if new_secret_key_base.nil? && generate_local_secret?
524+
if new_secret_key_base.nil? && Rails.env.local?
523525
@secret_key_base = generate_local_secret
524526
elsif new_secret_key_base.is_a?(String) && new_secret_key_base.present?
525527
@secret_key_base = new_secret_key_base
@@ -647,10 +649,6 @@ def generate_local_secret
647649

648650
File.binread(key_file)
649651
end
650-
651-
def generate_local_secret?
652-
Rails.env.local? || ENV["SECRET_KEY_BASE_DUMMY"]
653-
end
654652
end
655653
end
656654
end

railties/test/application/configuration_test.rb

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,28 @@ def index
815815
assert File.exist?(app_path("tmp/local_secret.txt"))
816816
end
817817

818+
test "application will use ENV['SECRET_KEY_BASE'] if present in local env" do
819+
env_var_secret = "env_var_secret"
820+
ENV["SECRET_KEY_BASE"] = env_var_secret
821+
822+
app "development"
823+
824+
assert_equal env_var_secret, app.secret_key_base
825+
ensure
826+
ENV.delete "SECRET_KEY_BASE"
827+
end
828+
829+
test "application will use secret_key_base from credentials if present in local env" do
830+
credentials_secret = "credentials_secret"
831+
add_to_config <<-RUBY
832+
Rails.application.credentials.secret_key_base = "#{credentials_secret}"
833+
RUBY
834+
835+
app "development"
836+
837+
assert_equal credentials_secret, app.secret_key_base
838+
end
839+
818840
test "application will not generate secret_key_base in tmp file if blank in production" do
819841
app_file "config/initializers/secret_token.rb", <<-RUBY
820842
Rails.application.credentials.secret_key_base = nil
@@ -846,8 +868,31 @@ def index
846868
assert_nothing_raised do
847869
app "production"
848870
end
871+
872+
assert_not_nil app.secret_key_base
873+
assert File.exist?(app_path("tmp/local_secret.txt"))
874+
ensure
875+
ENV.delete "SECRET_KEY_BASE_DUMMY"
876+
end
877+
878+
test "always use tmp file secret when dummy secret_key_base is used in production" do
879+
secret = "tmp_file_secret"
880+
ENV["SECRET_KEY_BASE_DUMMY"] = "1"
881+
ENV["SECRET_KEY_BASE"] = "env_secret"
882+
883+
app_file "config/initializers/secret_token.rb", <<-RUBY
884+
Rails.application.credentials.secret_key_base = "credentials_secret"
885+
RUBY
886+
887+
app_dir("tmp")
888+
File.binwrite(app_path("tmp/local_secret.txt"), secret)
889+
890+
app "production"
891+
892+
assert_equal secret, app.secret_key_base
849893
ensure
850-
ENV["SECRET_KEY_BASE_DUMMY"] = nil
894+
ENV.delete "SECRET_KEY_BASE_DUMMY"
895+
ENV.delete "SECRET_KEY_BASE"
851896
end
852897

853898
test "raise when secret_key_base is not a type of string" do

0 commit comments

Comments
 (0)