Met*_*urf 6 ruby-on-rails nginx devise amazon-cloudfront amazon-elb
我们有一个 Ruby/Rails 网站,正在从 Heroku 迁移到 AWS。原始开发不可用。我现在正在尝试完成迁移。我的背景是 Windows / .NET 世界。这个 Linux / Ruby/Rails 环境对我来说很陌生......
这是我设置的当前环境:
| 记录名称 | 记录类型 | 别名 | 别名将流量路由至 |
|---|---|---|---|
| foo.example.com | A | 是的 | 云前:xyz.cloudfront.net |
| 域名 | 备用域名 | 源域 | 起源协议 | 行为协议 |
|---|---|---|---|---|
| xyz.cloudfront.net | foo.example.com | foo.us-west-2.elb.amazonaws.com | 仅 HTTP | 将 HTTP 重定向到 HTTPS |
CloudFront 分布:
| DNS 名称 | 监听者规则 | 转发 |
|---|---|---|
| foo.us-west-2.elb.amazonaws.com | HTTP 80:默认操作 | 目标组:foo-ec2 |
Target Group: foo-ec2包含一个运行 nginx/1.18.0 + Phusion Passenger 6.0.10 的 Ubuntu ec2 实例来为 Ruby/Rails 站点提供服务。
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL Config - we should NEVER receive 443/https traffic;
# CloudFront manages https traffic => AWS ELB => http to this server
#listen 443 ssl default_server;
#listen [::]:443 ssl default_server;
server_name foo.example.com
foo.us-west-2.elb.amazonaws.com;
# Tell Nginx and Passenger where the app's 'public' directory is
root /var/www/foo_example/public;
# Turn on Passenger
passenger_enabled on;
passenger_app_env production;
passenger_ruby /home/ubuntu/.rbenv/versions/2.6.8/bin/ruby;
}
Run Code Online (Sandbox Code Playgroud)
Rails 应用程序启动时没有错误,并通过 https 提供服务。但是,当用户尝试登录/进行身份验证时,Devise gem会使用 http 和 ELB 的 DNS 名称发回重定向。
Request URL: https://foo.example.com/users/sign_in
Request Method: POST
Status Code: 302
Run Code Online (Sandbox Code Playgroud)
location: http://foo.us-west-2.elb.amazonaws.com/users
server: nginx/1.18.0 + Phusion Passenger(R) 6.0.10
status: 302 Found
Run Code Online (Sandbox Code Playgroud)
请注意,请求是通过 https 和我们的域进行的:
https://foo.example.com但现在我们通过 http 和 ELB 的域:
http://foo.us-west-2.elb.amazonaws.comgemdevise从 ELB 中查看主机,然后从 ELB 主机生成 URL,从而产生两个问题:
我已经调查过devise文档,看看我们是否可以传入 http 协议和域以在创建回发时使用,但我的 Ruby 知识有限。另外,我认为这将是一条“糟糕”的道路;其中“好的”路径是让 AWS ELB 转发实际域名,而不是它自己的域名。
我已经审查了几个具有类似问题的 SO 和相关堆栈站点,但我要么以无限循环重定向结束,要么各种配置更改导致 gemdevise创建错误 URL 回发的相同行为。
这两个问题似乎是最接近的,但我不太能够在答案和我对这个生态系统的有限知识之间建立“联系”。
如何让 AWS ELB 将我们的域 foo.example.com 转发到 ec2 目标组而不是 ELB 的域?
经过对 AWS 设置的更多实验后,解决方案实际上相当简单。我在问题中发布的其他答案在实际设置中含糊不清,所以这里是具体的解决方案。
在 CloudFront 中,您需要创建一个新的origin request policy,而不是cache policy:
创建新的源请求策略后:
行为将如下所示(我现在不使用缓存来验证 ec2 实例是否收到所有请求):

就是这样。主机标头现在已正确传递到 ELB 和 ec2 实例。无需对 ELB 执行任何其他操作。
我通过修改 nginx 日志记录选项以将变量包含$host在日志文件中(并对 OOB 格式进行了更多自定义),验证了所有请求中都使用了主机标头:
# prefixed log with '[my-log]', but it's not needed; remove.
log_format my-log '[my-log] $http_x_forwarded_for - $remote_user [$time_local] '
'"$request_method $scheme://$host$request_uri $server_protocol" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time';
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL Config - we should NEVER receive 443/https traffic;
# CloudFront manages https traffic => AWS ELB => http to this server
#listen 443 ssl default_server;
#listen [::]:443 ssl default_server;
server_name foo.example.com
foo.us-west-2.elb.amazonaws.com;
# create the our log file
access_log /var/log/nginx/my-log.access.log my-log;
# Tell Nginx and Passenger where the app's 'public' directory is
root /var/www/foo_example/public;
# Turn on Passenger
passenger_enabled on;
passenger_app_env production;
passenger_ruby /home/ubuntu/.rbenv/versions/2.6.8/bin/ruby;
}
Run Code Online (Sandbox Code Playgroud)
当然,这对我和其他人的未来都有帮助。