解答:这两个环境变量在 Heroku 中被混淆了。CERT 指向私钥,PRIVATE_KEY 指向证书。解决了这个问题。授权现在有效。
我会在可能的时候标记这个问题的答案。
我正在使用我的 Rails 4 应用程序中的服务帐户连接到 Google Analytics Reporting API。我已经建立了授权过程并让它在开发中工作。部署后,我意识到使用 .p12 密钥文件是一个问题——我要么必须将其提交到我的存储库,要么找到解决方法。所以我找到了一种解决方法,将密钥文件分成两个 .PEM 文件并将这些文件的内容存储为环境变量。
在开发中效果很好(我使用dotenv-rails在那里存储环境变量)。但是 Heroku 上的变量肯定发生了一些事情,因为当我尝试在生产中使用我的授权方法时,我得到了错误ArgumentError: Could not parse PKey: no start line
。
我四处寻找有关这里发生的事情的一些线索 - 我已经看到由于版本问题可能会发生相关错误(有人从 Ruby 2.2 降级到 1.9.8 并修复了类似的问题,尽管他们只是完全没有'不能在任何地方工作,而我的工作正在开发中)。降级我的 Ruby 版本并不是一个真正的选择。:/
以下是相关代码:
def authorize_with_services
begin
self.client = Google::APIClient.new(application_name: 'Playground', application_version: '1')
p12 = OpenSSL::PKCS12.create('notasecret', 'descriptor',
problem spot --> OpenSSL::PKey.read(ENV['PRIVATE_KEY']),
OpenSSL::X509::Certificate.new(ENV['CERT']))
p12_binary = p12.to_der
self.client.authorization = Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
audience: 'https://accounts.google.com/o/oauth2/token',
scope: 'https://www.googleapis.com/auth/analytics.readonly',
issuer: GA_EMAIL,
signing_key: Google::APIClient::PKCS12.load_key(p12_binary, 'notasecret')
).tap { |auth| auth.fetch_access_token! }
rescue Signet::AuthorizationError
self.client = nil
end
self.client
end
然后是存储在 .env 中的 ENV 变量:
PRIVATE_KEY: "Bag Attributes\n friendlyName: privatekey\n localKeyID: 54 69 6D 65 20 31 34 33 35 36 38 34 30 33 31 30 32 39\nKey Attributes: <No Attributes>\n-----BEGIN RSA PRIVATE KEY-----\nKEY\n-----END RSA PRIVATE KEY-----\n"
CERT: "Bag Attributes\nfriendlyName: privatekey\nlocalKeyID: 54 69 6D 65 20 31 34 33 35 36 38 34 30 33 31 30 32 39\nsubject=/CN=636085506886-096q9j1uotf9kp1tv4evhn2crip6dec8.apps.googleusercontent.com\nissuer=/CN=636085506886-096q9j1uotf9kp1tv4evhn2crip6dec8.apps.googleusercontent.com\n-----BEGIN CERTIFICATE-----\nCERT\n-----END CERTIFICATE-----\n"
heroku config:set PRIVATE_KEY
而且我在和中将它们设置为完全相同heroku config:set CERT
。
编辑
这是 Heroku 控制台中导致错误的会话:
# 11:15:36 (ruby-2.1.5@yt-ga-playground) ~/projects/confreaks/yt-ga-playground (master)$ heroku run console
Running `console` attached to terminal... up, run.8357
Loading production environment (Rails 4.2.0)
irb(main):001:0> querier = AnalyticsQuerier.new
=> #<AnalyticsQuerier:0x007f81fc6f5358 @options={"ids"=>"ga:104082366", "start-date"=>"2015-01-01", "end-date"=>"2015-07-04", "metrics"=>"ga:totalEvents"}, @ids="ga:104082366", @start_date="2015-01-01", @end_date="2015-07-04", @metrics="ga:totalEvents">
irb(main):002:0> querier.authorize_with_services
ArgumentError: Could not parse PKey: no start line
from /app/app/services/analytics_querier.rb:56:in `read'
from /app/app/services/analytics_querier.rb:56:in `authorize_with_services'
from (irb):2
from /app/vendor/bundle/ruby/2.1.0/gems/railties-4.2.0/lib/rails/commands/console.rb:110:in `start'
from /app/vendor/bundle/ruby/2.1.0/gems/railties-4.2.0/lib/rails/commands/console.rb:9:in `start'
from /app/vendor/bundle/ruby/2.1.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:68:in `console'
from /app/vendor/bundle/ruby/2.1.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /app/vendor/bundle/ruby/2.1.0/gems/railties-4.2.0/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:8:in `require'
from bin/rails:8:in `<main>'
以及我的开发控制台中的等效会话:
# 11:17:47 (ruby-2.1.5@yt-ga-playground) ~/projects/confreaks/yt-ga-playground (master)$ be rails c
Loading development environment (Rails 4.2.0)
2.1.5 :001 > querier = AnalyticsQuerier.new
=> #<AnalyticsQuerier:0x007fe97d380928 @options={"ids"=>"ga:104082366", "start-date"=>"2015-01-01", "end-date"=>"2015-07-04", "metrics"=>"ga:totalEvents"}, @ids="ga:104082366", @start_date="2015-01-01", @end_date="2015-07-04", @metrics="ga:totalEvents">
2.1.5 :002 > querier.authorize_with_services
=> #<Google::APIClient:0x007fe97d36bc80 @host="www.googleapis.com", @port=443, @discovery_path="/discovery/v1", @user_agent="Playground/1 google-api-ruby-client/0.8.2 Mac OS X/10.10.3\n (gzip)", etc.>
我有一个控制器方法,它使用AnalyticsQuerier
. 当我认为它会起作用时,我将它投入生产。以下是日志中的结果:
[...] app[web.1]: Started POST "/events" for 104.231.12.227 at 2015-07-04 15:14:11 +0000
[...] app[web.1]: Completed 500 Internal Server Error in 16ms
[...] app[web.1]: Processing by EventsController#create as HTML
[...] app[web.1]: Parameters: {"authenticity_token"=>"1fvzDI1n+AlTssDybGFHAyiG4M+kir2UDLsA4dZGiAJvrWXdK6OnlAf1wK0V+/dI4QXz3lxonmRw15XbFjpOAQ=="}
[...] app[web.1]: (0.8ms) SELECT COUNT(*) FROM "analytics_calls"
[...] app[web.1]:
[...] app[web.1]: ArgumentError (Could not parse PKey: no start line):
[...] app[web.1]: app/services/analytics_querier.rb:56:in `read'
[...] app[web.1]: app/services/analytics_querier.rb:56:in `authorize_with_services'
[...] app[web.1]: app/controllers/events_controller.rb:10:in `create'
2015-07-04T15:14:11.542398+00:00 app[web.1]:
[...] app[web.1]: app/models/analytics_call.rb:19:in `get_batch'
[...] app[web.1]:
[...] heroku[router]: at=info method=POST path="/events" host=desolate-fortress-9280.herokuapp.com request_id=3822f668-6b8c-4cb2-9764-c171fc0dc67b fwd="104.231.12.227" dyno=web.1 connect=3ms service=33ms status=500 bytes=1754
编辑 2 - 可能导致正确的方向?
因此,正如 Val Asensio 所指出的,Heroku 提供的 SSH 设置存在问题(这对我来说或多或少是陌生的领域,但鉴于我的理解需要一些充实,我会尽量清楚地表述事情这里)。
我发现了一个名为heroku-buildback-ssh的 gem ,它肯定表明您需要做的不仅仅是将私钥加载到 env 变量中才能正确使用它。但是,自述文件指定私人不能有密码才能被访问。它似乎还假设您正在使用一个名为 something 的文件rsa
,但没有迹象表明这适用于 .pem 文件?
我在这里找到了另一个关于Heroku 的 ssh 隧道的讨论。我认为这是要走的路。我的理解是,我需要能够在幕后使用环境变量 PRIVATE_KEY 来“重建”私钥文件,并且我可以在 Heroku 启动时编写一个脚本来执行此操作。
但是,我在这里的理解是如此不稳定,并且答案脚本中描述的用例非常不同,以至于我不确定如何推断如何使用这种方法构建解决方案。
我现在想知道的是,我可以使用 Heroku 在幕后运行的 bash 脚本构建 .pem 密钥文件吗?如果可以,我如何确保该文件是我的脚本运行时正在读取的文件OpenSSL::PKey.read
?我想我也可以尝试自己构建原始的 .pk12 文件?
ALSO - 一个大问题:我是否会搞砸任何事情,以至于我陷入混乱的代码中,不得不丢弃整个应用程序(这是一个代码尖峰应用程序,但我仍然想知道是否有人可以提供这里有一个答案)如果我开始编写脚本来创建文件并在我的生产环境中管理这些文件的权限?