如何让 AWS ELB 将实际主机名而不是 ELB 的主机名转发给目标组?
How to have an AWS ELB forward the actual host name to the target group instead of the ELB's host name?
我们有一个 Ruby/Rails 网站,我们正在从 Heroku 迁移到 AWS。原始开发人员不可用。我现在正在尝试完成迁移。我的背景是 Windows / .NET 世界。这个Linux / Ruby/Rails环境对我来说很陌生...
这是我设置的当前环境:
53 号公路
Record Name
Record Type
Alias
Alias Route Traffic To
foo.example.com
A
yes
cloudfront: xyz.cloudfront.net
CloudFront
Domain Name
Alternate Domain Names
Origin Domain
Origin Protocol
Behavior Protocol
xyz.cloudfront.net
foo.example.com
foo.us-west-2.elb.amazonaws.com
HTTP only
Redirect HTTP to HTTPS
CloudFront 分布:
- 使用 AWS 颁发的 SSL 证书
- 处理从 http 到 https 的重定向
- 通过 http(不是 https)将请求转发给 ELB
负载均衡器
DNS Name
Listener Rule
Forward To
foo.us-west-2.elb.amazonaws.com
HTTP 80: default action
Target Group: foo-ec2
Target Group: foo-ec2
包含单个 Ubuntu ec2 实例 运行 nginx/1.18.0 + Phusion Passenger 6.0.10 为 Ruby/Rails 站点提供服务。
nginx 配置
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;
}
问题
rails 应用程序启动时没有错误,并通过 https 提供服务。但是,当用户尝试登录/验证时,Devise gem 使用 http 和 ELB 的 DNS 名称发回重定向。
例子
sign_in请求
Request URL: https://foo.example.com/users/sign_in
Request Method: POST
Status Code: 302
sign_in 回应
location: http://foo.us-west-2.elb.amazonaws.com/users
server: nginx/1.18.0 + Phusion Passenger(R) 6.0.10
status: 302 Found
请注意请求是通过 https 和我们的域:
https://foo.example.com
但现在我们已经通过 http 和 ELB 的域:
http://foo.us-west-2.elb.amazonaws.com
我的假设
devise
gem 从 ELB 看到主机,然后从 ELB 主机生成 URL,造成两个问题:
- 我们现在在 http 上,因为 ELB 通过 http
与 ec2 实例通信
- 我们现在使用 ELB 的主机名,foo.us-west-2.elb.amazonaws.com,而不是我们的名字,foo.example.com
我查看了 devise
文档,看看我们是否可以只传入 http 协议和域以在创建 post 返回时使用,但我的 ruby 知识是有限的。另外,我认为这将是“坏”路径; “好的”路径是让 AWS ELB 转发实际域名,而不是它自己的域名。
我查看了几个有类似问题的 SO 和相关堆栈站点,但我要么以无限循环重定向结束,要么各种配置更改导致 devise
的相同行为gem创建错误URLpost回.
这两个问题似乎最接近,但我不太能够在答案和我对这个生态系统的有限知识之间建立“联系”。
问题
如何让 AWS ELB 将我们的域 foo.example.com 转发到 ec2 目标组而不是 ELB 的域?
经过对 AWS 设置的更多实验,解决方案实际上相当简单。我在问题中发布的其他答案在实际设置中是模糊的,所以这里是具体的解决方案。
在 CloudFront 中,您需要创建一个新的 origin request policy
,not a cache policy
:
- 打开 CloudFront
- 转到政策(左侧导航)
- 单击“来源请求”选项卡
- 单击“创建源请求策略”按钮
- 根据需要命名策略,即“我的源请求策略”
- 在“来源请求设置”> Headers 下:select“包括以下 headers”
- 在“添加 header”下:勾选“主机”选项
- 单击“创建”按钮
创建新的源请求策略后:
- 回到 CloudFront 发行版
- 单击您的发行版 ID 以便您可以对其进行编辑
- 单击“行为”选项卡
- select 您的行为并编辑
- 向下滚动到“缓存键和源请求”
- 确保“缓存策略和原始请求策略(推荐)”是selected
- 在“来源请求政策 - 可选”下,select 您的新政策,即“我的来源请求政策”
- 保存更改
行为将如下所示(我现在不使用缓存来验证 ec2 实例正在获取所有请求):
就是这样。主机 header 现在已正确传递到 ELB 和 ec2 实例。无需对 ELB 执行任何其他操作。
我通过修改 nginx 日志记录选项以在日志文件中包含 $host
变量(并对 OOB 格式做了更多自定义)来验证所有请求中都使用了主机 header ):
# 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;
}
当然这对我和其他人的未来都有帮助。
我们有一个 Ruby/Rails 网站,我们正在从 Heroku 迁移到 AWS。原始开发人员不可用。我现在正在尝试完成迁移。我的背景是 Windows / .NET 世界。这个Linux / Ruby/Rails环境对我来说很陌生...
这是我设置的当前环境:
53 号公路
Record Name | Record Type | Alias | Alias Route Traffic To |
---|---|---|---|
foo.example.com | A | yes | cloudfront: xyz.cloudfront.net |
CloudFront
Domain Name | Alternate Domain Names | Origin Domain | Origin Protocol | Behavior Protocol |
---|---|---|---|---|
xyz.cloudfront.net | foo.example.com | foo.us-west-2.elb.amazonaws.com | HTTP only | Redirect HTTP to HTTPS |
CloudFront 分布:
- 使用 AWS 颁发的 SSL 证书
- 处理从 http 到 https 的重定向
- 通过 http(不是 https)将请求转发给 ELB
负载均衡器
DNS Name | Listener Rule | Forward To |
---|---|---|
foo.us-west-2.elb.amazonaws.com | HTTP 80: default action | Target Group: foo-ec2 |
Target Group: foo-ec2
包含单个 Ubuntu ec2 实例 运行 nginx/1.18.0 + Phusion Passenger 6.0.10 为 Ruby/Rails 站点提供服务。
nginx 配置
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;
}
问题
rails 应用程序启动时没有错误,并通过 https 提供服务。但是,当用户尝试登录/验证时,Devise gem 使用 http 和 ELB 的 DNS 名称发回重定向。
例子
sign_in请求
Request URL: https://foo.example.com/users/sign_in
Request Method: POST
Status Code: 302
sign_in 回应
location: http://foo.us-west-2.elb.amazonaws.com/users
server: nginx/1.18.0 + Phusion Passenger(R) 6.0.10
status: 302 Found
请注意请求是通过 https 和我们的域:
https://foo.example.com
但现在我们已经通过 http 和 ELB 的域:
http://foo.us-west-2.elb.amazonaws.com
我的假设
devise
gem 从 ELB 看到主机,然后从 ELB 主机生成 URL,造成两个问题:
- 我们现在在 http 上,因为 ELB 通过 http 与 ec2 实例通信
- 我们现在使用 ELB 的主机名,foo.us-west-2.elb.amazonaws.com,而不是我们的名字,foo.example.com
我查看了 devise
文档,看看我们是否可以只传入 http 协议和域以在创建 post 返回时使用,但我的 ruby 知识是有限的。另外,我认为这将是“坏”路径; “好的”路径是让 AWS ELB 转发实际域名,而不是它自己的域名。
我查看了几个有类似问题的 SO 和相关堆栈站点,但我要么以无限循环重定向结束,要么各种配置更改导致 devise
的相同行为gem创建错误URLpost回.
这两个问题似乎最接近,但我不太能够在答案和我对这个生态系统的有限知识之间建立“联系”。
问题
如何让 AWS ELB 将我们的域 foo.example.com 转发到 ec2 目标组而不是 ELB 的域?
经过对 AWS 设置的更多实验,解决方案实际上相当简单。我在问题中发布的其他答案在实际设置中是模糊的,所以这里是具体的解决方案。
在 CloudFront 中,您需要创建一个新的 origin request policy
,not a cache policy
:
- 打开 CloudFront
- 转到政策(左侧导航)
- 单击“来源请求”选项卡
- 单击“创建源请求策略”按钮
- 根据需要命名策略,即“我的源请求策略”
- 在“来源请求设置”> Headers 下:select“包括以下 headers”
- 在“添加 header”下:勾选“主机”选项
- 单击“创建”按钮
创建新的源请求策略后:
- 回到 CloudFront 发行版
- 单击您的发行版 ID 以便您可以对其进行编辑
- 单击“行为”选项卡
- select 您的行为并编辑
- 向下滚动到“缓存键和源请求”
- 确保“缓存策略和原始请求策略(推荐)”是selected
- 在“来源请求政策 - 可选”下,select 您的新政策,即“我的来源请求政策”
- 保存更改
行为将如下所示(我现在不使用缓存来验证 ec2 实例正在获取所有请求):
就是这样。主机 header 现在已正确传递到 ELB 和 ec2 实例。无需对 ELB 执行任何其他操作。
我通过修改 nginx 日志记录选项以在日志文件中包含 $host
变量(并对 OOB 格式做了更多自定义)来验证所有请求中都使用了主机 header ):
# 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;
}
当然这对我和其他人的未来都有帮助。