如何解决错误“在生产环境下缺少'secret_key_base'”(Rails 4.1)?

189

我使用Rails 4.1从头开始创建了一个Rails应用程序,但我遇到了一个奇怪的问题,我无法解决。

每次我尝试在Heroku上部署我的应用程序时,都会出现500错误:

Missing `secret_key_base` for 'production' environment, set this value in `config/secrets.yml`

secret.yml文件包含以下配置:

secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
在Heroku上,我使用rake secret命令的结果配置了"SECRET_KEY_BASE"环境变量。如果我启动heroku config,我可以看到正确名称和值的变量。为什么我仍然会收到这个错误?
答案:
在Heroku上配置了"SECRET_KEY_BASE"环境变量并使其具有正确的名称和值,但仍然出现错误。

1
我遇到了完全相同的问题,也很想知道为什么会发生这种情况。如果我找到原因,我会回来发布我的解决方案。 - danielricecodes
你的配置文件是叫做 secret.yml 还是 secrets.yml - James
2
我使用Rails生成的.gitignore文件重新配置了一遍,现在一切都正常工作了。 - Paolo Laurenti
当我们升级到Rails 4时,我们也遇到了这个问题。在我们的情况下,是因为我们有一个自定义的环境名称,而这并没有反映在secrets.yml中。我只需要在非标准名称的文件中添加一行,提交并重新部署即可解决问题。 - whognu
给未来的读者:这个答案可能是最简单和最准确的:stackoverflow.com/a/26541742/4880924 - BenKoshy
15个回答

232

我曾经遇到过同样的问题,并通过创建一个环境变量来解决它,该环境变量每次登录到生产服务器时都会被加载,并制作了一个步骤配置的迷你指南:

我当时使用的是Rails 4.1和Unicorn v4.8.2,当我尝试部署我的应用程序时,它没有正确启动,在unicorn.log文件中,我发现了这个错误消息:

app error: Missing `secret_key_base` for 'production' environment, set this value in `config/secrets.yml` (RuntimeError)

经过一些调查,我发现Rails 4.1改变了管理secret_key的方式,所以如果您阅读exampleRailsProject/config/secrets.yml中的secrets.yml文件,您会发现类似于这样的内容:

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

这意味着Rails建议在生产服务器上使用环境变量来存储secret_key_base。为了解决这个错误,您应该按照以下步骤为Linux(例如Ubuntu)在生产服务器上创建一个环境变量:

  1. 在您的生产服务器终端中执行:

  2. $ RAILS_ENV=production rake secret
    

    这将返回一个包含字母和数字的大字符串。请复制它,我们将把这段代码称为GENERATED_CODE

  3. 登录到您的服务器

    • 如果您以root用户身份登录,请找到此文件并进行编辑:

    • $ vi /etc/profile
      

      在vi中使用 Shift+G (大写字母“G”) 跳转到文件底部。

      在vi中按下 i 插入环境变量 GENERATED_CODE,确保在文件末尾的新行中进行:

      $ export SECRET_KEY_BASE=GENERATED_CODE
      

      保存更改并使用 Esc 关闭文件,然后在vi中键入":x"和 Enter 以保存并退出。

    • 但是,如果您以普通用户登录,让我们把它称为此主题的 "example_user",则需要找到其他文件之一:

    • $ vi ~/.bash_profile
      $ vi ~/.bash_login
      $ vi ~/.profile
      
      这些文件按重要性顺序排列,这意味着如果您拥有第一个文件,则不需要编辑其他文件。如果您在目录~/.bash_profile~/.profile中找到了这两个文件,则只需要在第一个文件~/.bash_profile中编写,因为Linux只会读取这一个文件,而忽略另一个文件。
      然后我们再次使用Shift+G到文件底部,并再次使用i将环境变量与我们的GENERATED_CODE写入,并确保在文件末尾添加一个新行:
      $ export SECRET_KEY_BASE=GENERATED_CODE
      

      完成代码编写后,按下Esc,然后输入":x"并按下Enter保存并退出文件。

  4. 您可以使用以下命令验证Linux中是否正确设置了环境变量:

  5. $ printenv | grep SECRET_KEY_BASE
    

    或使用:

    $ echo $SECRET_KEY_BASE
    

    当您执行此命令时,如果一切顺利,它将显示之前生成的GENERATED_CODE。最后,在完成所有配置后,您应该能够使用Unicorn或其他工具无问题地部署Rails应用程序。

当您关闭shell并再次登录到生产服务器时,您将拥有此环境变量,并准备好使用它。

就是这样!我希望这个迷你指南能够帮助您解决此错误。

免责声明:我不是Linux或Rails大师,如果您发现任何错误,我很乐意进行修正。


12
看起来Rails无法识别环境变量SECRET_KEY_BASE。使用printenv可以显示它,如果我检查ENV,rails c production也可以显示它。但是,在重新启动Unicorn时没有影响。现在唯一有效的方法是直接将其粘贴到secrets.yml中。 - AntonAL
1
这对我有用。感谢您的详细解释。我刚刚了解到存在用于管理应用程序环境变量的 gem。'Dotenv' 是其中之一,'foreman' 适用于 heroku。虽然手动修复错误的方式很有教育意义,但使用其中一个 gem 可能会简化流程。 - Nick Res
很高兴我的回答有所帮助,感谢@ninja08提供的宝石选项,它们确实使过程更容易,特别是对于使用capistrano或其他增量工具来管理服务器的人来说 :) - Demi Magus
很好的答案!!我不知道为什么这对我没有解决,我创建了一个问题https://dev59.com/0pDea4cB1Zd3GeqPWhGX - Adriano Resende
你可以使用Figaro gem https://github.com/laserlemon/figaro来处理Rails应用程序中的环境变量。 - Jude Calimbas
显示剩余8条评论

96

我假设你没有将你的secrets.yml上传到源代码控制中(即在.gitignore文件中)。 即使这不是您的情况,但因为许多其他人在GitHub上公开了他们的代码并且不想让秘钥漂浮在外,所以会这样做。

如果没有上传到源代码控制中,Heroku就不知道它。因此Rails正在查找Rails.application.secrets.secret_key_base,但它还没有被设置,因为Rails通过检查不存在的secrets.yml文件来设置它。简单的解决方法是进入您的config/environments/production.rb文件并添加以下代码行:

Rails.application.configure do
    ...
    config.secret_key_base = ENV["SECRET_KEY_BASE"]
    ...
end

这会告诉你的应用程序使用环境变量设置密钥,而不是在secrets.yml中查找它。提前知道这一点可以节省大量时间。


16
这是最好的答案。 Figaroheroku_secrets除非Rails知道SECRET_KEY_BASE存储在ENV中,否则不起作用。我一直在苦苦思考,认为如果配置变量存在于Heroku上,Rails就会通过它的存在而自动读取它,但现在似乎显而易见,Rails需要知道在哪里查找。我一直在想如何在Github上有代码而不必担心密钥问题;现在我知道了。 - flanger001
1
同意,我认为有了像Figaro这样的好宝石,secrets.yml就是多余的。 - Joe
2
如果你的项目使用Github和Heroku,似乎是最好的选择。 - flexus
1
将secrets.yml与production: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>一起提交有什么问题?这是否意味着实际的密钥也不会被暴露。如果所有内容都只是种子数据和测试数据,那么在提交secrets.yml时暴露开发和测试密钥是否存在风险? - Jay Killeen
即使在Rails 6.0.2中不再有secrets.yml,这仍然有效。 - Ken Tsoi

51
config/secrets.yml添加到版本控制并重新部署。 您可能需要从.gitignore中删除一行,以便可以提交该文件。
我遇到了完全相同的问题,结果发现Github为我的Rails应用程序创建的样板.gitignore包括config/secrets.yml

145
config/secrets.yml 绝不能放在代码库中。你可以使用 do.yml.sample 文件并填入虚假数据,但为了安全起见,从不要在代码库中放置 .yml 文件。 - user3379926
9
在Heroku上的Rails应用中,您不能选择哪些文件包括在版本控制中,哪些不包括。Rails 4.1期望秘密配置存在,否则应用程序将无法运行。如果您有一种方法可以解决上面问题而不必将secrets.yml文件提交到Git,请提供该建议以改进此线程。 - danielricecodes
10
你可以在初始化器中手动设置值。像这样 Rails.application.config.secret_key_base = ENV["SECRET_KEY_BASE"] 可以解决错误,而无需将 secrets.yml 添加到源代码中。 - joshhepworth
11
当我使用rails new命令生成一个新的 Rails 应用程序(在这种情况下,生成的 Gemfile 中 rails gem 的版本是 4.2.4),会自动生成 config/secrets.yml 文件。它包含了预先生成的开发和测试环境的秘密密钥,并从环境变量中读取生产环境的 secret_key:secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>。在我的看来,只要不实际在secrets.yml文件中定义密钥,将此文件保存在版本控制中应该是完全安全且非常有用的。 - Teemu Leisti
3
为什么?如果你已经从环境变量中读取了秘钥,有什么大不了的呢?没有任何秘密被暴露出来。 - horseyguy
显示剩余4条评论

13

这对我有用。

通过SSH登录到你的生产服务器并cd到你当前的目录,运行bundle exec rake secret或者rake secret,你会得到一个长字符串作为输出,复制那个字符串。

现在运行sudo nano /etc/environment

将该字符串粘贴到文件底部。

export SECRET_KEY_BASE=rake secret
ruby -e 'p ENV["SECRET_KEY_BASE"]'

将刚刚复制的字符串粘贴到rake secret的位置,其中rake secret是一个命令。

重新启动服务器并通过运行echo $SECRET_KEY_BASE进行测试。


3
虽然您可以像其他答案一样使用初始化程序,但常规的Rails 4.1+方法是使用config/secrets.yml。Rails团队引入这个的原因超出了本答案的范围,但简而言之是因为secret_token.rb混淆了配置和代码,并且存在安全风险,因为令牌被检入源代码控制历史记录中,唯一需要知道生产秘密令牌的系统是生产基础架构。

您应该像不将config/database.yml添加到源代码控制中一样,将此文件添加到.gitignore中。

参考Heroku自己的代码来设置config/database.ymlDATABASE_URL在他们的Buildpack for Ruby中,我最终forking their repo并修改它以从SECRETS_KEY_BASE环境变量创建config/secrets.yml

自从Rails 4.1引入了这个功能,我觉得编辑./lib/language_pack/rails41.rb并添加这个功能是合适的。
以下是我在公司创建的修改后的buildpack的片段
class LanguagePack::Rails41 < LanguagePack::Rails4

  # ...

  def compile
    instrument "rails41.compile" do
      super
      allow_git do
        create_secrets_yml
      end
    end
  end

  # ...

  # writes ERB based secrets.yml for Rails 4.1+
  def create_secrets_yml
    instrument 'ruby.create_secrets_yml' do
      log("create_secrets_yml") do
        return unless File.directory?("config")
        topic("Writing config/secrets.yml to read from SECRET_KEY_BASE")
        File.open("config/secrets.yml", "w") do |file|
          file.puts <<-SECRETS_YML
<%
raise "No RACK_ENV or RAILS_ENV found" unless ENV["RAILS_ENV"] || ENV["RACK_ENV"]
%>

<%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
          SECRETS_YML
        end
      end
    end
  end

  # ...

end

当然,您可以扩展此代码以添加其他机密(例如第三方API密钥等)以从环境变量中读取:
...
<%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  third_party_api_key: <%= ENV["THIRD_PARTY_API"] %>

这样,您可以以非常标准的方式访问此秘密:
Rails.application.secrets.third_party_api_key

在重新部署应用程序之前,请确保先设置您的环境变量: 在Heroku仪表板中设置SECRET_KEY_BASE 然后将修改过的构建包(或者您可以链接到我的构建包)添加到您的Heroku应用程序中(请参阅Heroku的文档),并重新部署您的应用程序。
构建包将在每次向Heroku进行git push时作为动态构建过程的一部分自动从您的环境变量创建config/secrets.yml
编辑:Heroku自己的文档建议创建config/secrets.yml以从环境变量中读取,但这意味着您应该将此文件检入源代码控制。在我的情况下,这不起作用,因为我有硬编码的秘密用于开发和测试环境,我宁愿不要检查它们。

虽然dotenv和foreman宝石是一个很好的解决方案,可以解决“我在开发和测试环境中有硬编码的秘密”这个问题 - 因此使用这些宝石意味着您不需要构建包,因为您可以在秘密文件中使用ENV_VAR进行开发和测试。 - rmcsharry
请注意,大多数基础设施都会记录环境变量,这意味着未加密的环境变量将以明文形式出现在日志中。我不使用Heroku来运行我的Rails应用程序,因此对此没有建议,但是在AWS中,我们从构建容器内的参数存储中提取加密值,并解密它们以填充此类安全资产。 - Daniel Nalbach

2
你可以将密钥导出为环境变量存储在服务器的~/.bashrc~/.bash_profile文件中:
export SECRET_KEY_BASE = "YOUR_SECRET_KEY"

然后,您可以源化您的.bashrc.bash_profile

source ~/.bashrc 
source ~/.bash_profile

永远不要提交你的 secrets.yml 文件


2

对于rails6,我遇到了同样的问题,因为我缺少了以下文件。一旦我添加了它们,问题就解决了:

1. config/master.key
2. config/credentials.yml.enc

请确保您拥有这些文件!

1
你不应该对你的 master.key 文件进行版本控制,因为它包含了解密你凭据加密文件的密钥。 - damuz91

0
我做了什么: 在我的生产服务器上,我为Thin(我正在使用它)创建了一个配置文件(confthin.yml),并添加了以下信息:
environment: production
user: www-data
group: www-data
SECRET_KEY_BASE: mysecretkeyproduction

然后我使用以下命令启动应用:

thin start -C /whereeveristhefieonprod/configthin.yml

工作得很好,然后就不需要在版本控制中放置秘密密钥

希望它能有所帮助,但我相信Unicorn和其他工具也可以做同样的事情。


1
你能解释一下这是如何工作的吗?这个问题是关于Heroku的。Thin是一个替代方案,还是与Heroku兼容的? - ahnbizcad

-1
我创建了config/initializers/secret_key.rb文件,并且只写了以下一行代码:
Rails.application.config.secret_key_base = ENV["SECRET_KEY_BASE"]

不过我认为@Erik Trautman发布的解决方案更优雅;)

编辑: 哦,最后我在Heroku上找到了这个建议:https://devcenter.heroku.com/changelog-items/426 :)

享受吧!


-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接