运行 Cloudfront 后面的 ActionCable

Running ActionCable behind Cloudfront

我们已经在我们的应用程序前面设置了 Cloudfront,但不幸的是它将 ActionCable 所需的 Upgrade header 剥离为 运行。

我们希望有一个不同的子域指向相同的服务器,但绕过 Cloudfront(例如 socket.site.com)。我们已经完成了这项工作,它有点奏效,但似乎无法建立持久连接。 ActionCable 继续每 10 秒重试一次建立连接,但似乎无法保持连接打开:

欢迎任何与 Cloudfront 或 ActionCable 不同域相关的建议。

对于所有关注的人,希望这对您有所帮助。

截至我撰写本文时(2018 年 10 月),您似乎根本无法在 Cloudfront 后面使用 ActionCable。 CF 将放弃升级 header,这将阻止建立安全套接字连接。

我们的设置是 CF -> 应用程序负载均衡器 (ALB) -> EC2。在 AWS 方面,我们首先创建一个直接指向同一个 ALB 并完全绕过 CF 的子域 (socket.example.com)。请注意,Classic Load Balancer 绝对不起作用。您只能使用 ALB。

仅此一项并没有解决问题。在您的 Rails 配置中,您必须将以下行添加到 production.rb:

config.action_cable.url = 'wss://socket.example.com:28080/cable'
config.action_cable.allowed_request_origins = ['https://example.com'] # Not the subdomain

您可能还需要更新您的 CSP 以包含 wss://socket.example.com/cable for connect_src

如果此时您收到有关升级失败的消息,则需要确保您的 NGINX 配置正确。 This answer may help.


您还需要在 cable.js 中反映这一变化。以下代码片段适用于本地开发和生产,但您可能需要对其进行更改。我在编写它时考虑到了 ES6 之前的版本,因为这个文件在我们的配置中从未命中 Babel。

(function() {
  this.App || (this.App = {})

  var wsUrl
  if(location.host.indexOf('localhost') != -1) {
    wsUrl = '/cable'
  } else {
    var host = location.host
    var protocol = location.protocol
    wsUrl = protocol + '//socket.' + host + '/cable'
  }

  App.cable = ActionCable.createConsumer(wsUrl)

}).call(this)

这可能就是您所需要的,具体取决于您的身份验证方案。但是,我使用的是在主应用程序和 ActionCable 之间共享的 cookie,这导致了一个棘手的错误。连接看似正确,但实际上会失败,ActionCable 将每 10 秒重试一次。最后一步是确保设置的身份验证 cookie 可以跨套接字子域工作。我这样更新了我的 cookie:

cookies.signed[:cookie_name] = {
  value: payload,
  domain: ['.socket.example.com', '.example.com']
  # Some people have to specify tld_length, but I was fine without it
}