如何让 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?

查看:21
本文介绍了如何让 AWS ELB 将实际主机名而不是 ELB 的主机名转发到目标组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个 Ruby/Rails 网站,我们正在从 Heroku 迁移到 AWS.原始开发人员不可用.我现在正在尝试完成迁移.我的背景是在 Windows/.NET 世界.这个 Linux/Ruby/Rails 环境对我来说很陌生...

这是我设置的当前环境:

53 号公路

<头>
记录名称记录类型别名别名路由流量到
foo.example.comA是的cloudfront: xyz.cloudfront.net

云前端

<头>
域名备用域名源域源协议行为协议
xyz.cloudfront.netfoo.example.comfoo.us-west-2.elb.amazonaws.com仅 HTTP将 HTTP 重定向到 HTTPS

CloudFront 发行版:

  • 使用 AWS 颁发的 SSL 证书
  • 处理 http 到 https 的重定向
  • 通过 http(不是 https)将请求转发给 ELB

负载均衡器

<头>
DNS 名称监听规则转发到
foo.us-west-2.elb.amazonaws.comHTTP 80:默认操作目标群体:foo-ec2

目标组:foo-ec2 包含一个运行 nginx/1.18.0 + Phusion Passenger 6.0.10 的 Ubuntu ec2 实例,用于为 Ruby/Rails 站点提供服务.

nginx 配置

服务器{听 80 default_server;听 [::]:80 default_server;# SSL 配置 - 我们不应该接收 443/https 流量;# CloudFront 管理 https 流量 =>AWS ELB =>http 到此服务器#listen 443 ssl default_server;#listen [::]:443 ssl default_server;server_name foo.example.comfoo.us-west-2.elb.amazonaws.com;# 告诉 Nginx 和Passenger 应用程序的'public' 目录在哪里根/var/www/foo_example/public;# 开启乘客乘客启用;passenger_app_env 生产;passenger_ruby/home/ubuntu/.rbenv/versions/2.6.8/bin/ruby;}

问题

rails 应用程序启动时没有错误,并通过 https 提供服务.但是,当用户尝试登录/验证时,

一旦创建了新的源请求策略:

  • 回到 CloudFront 分发版
  • 点击您的发行版的 ID,以便您可以对其进行编辑
  • 点击行为"标签
  • 选择您的行为并进行编辑
  • 向下滚动到缓存密钥和源请求"
  • 确保缓存策略和源请求策略(推荐)"被选中
  • 在来源请求政策 - 可选"下,选择您的新政策,即我的来源请求政策"
  • 保存更改

行为看起来像这样(我现在不使用缓存来验证 ec2 实例是否正在获取所有请求):

就是这样.主机标头现在已正确传递到 ELB 和 ec2 实例.ELB 无需执行任何其他操作.

我通过修改 nginx 日志记录选项以在日志文件中包含 $host 变量来验证所有请求中都使用了主机标头(并对 OOB 格式进行了更多自定义):

# 以'[my-log]' 为前缀的日志,但它不是必需的;消除.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';服务器 {听 80 default_server;听 [::]:80 default_server;# SSL 配置 - 我们不应该接收 443/https 流量;# CloudFront 管理 https 流量 =>AWS ELB =>http 到此服务器#listen 443 ssl default_server;#listen [::]:443 ssl default_server;server_name foo.example.comfoo.us-west-2.elb.amazonaws.com;# 创建我们的日志文件access_log/var/log/nginx/my-log.access.log my-log;# 告诉 Nginx 和Passenger 应用程序的'public' 目录在哪里根/var/www/foo_example/public;# 开启乘客乘客启用;passenger_app_env 生产;passenger_ruby/home/ubuntu/.rbenv/versions/2.6.8/bin/ruby;}

这肯定会对我和其他人的未来有所帮助.

We have a Ruby/Rails website we're migrating from Heroku to AWS. The original dev is not available. I'm now trying to complete the migration. My background is in the Windows / .NET world. This Linux / Ruby/Rails environment is quite foreign to me...

Here's the current environment I've set-up:

Route 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

The CloudFront distribution:

  • uses an AWS issued SSL cert
  • handles the http to https redirect
  • forwards the request to the ELB over http (not https)

Load Balancer

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 contains a single Ubuntu ec2 instance running nginx/1.18.0 + Phusion Passenger 6.0.10 to serve up the Ruby/Rails site.

nginx config

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;
}

Issue

The rails app starts up without error and is served over https. However, when a user attempts to log in / authenticate, the Devise gem sends back a redirect using http and the ELB's DNS name.

Example

sign_in request

Request URL: https://foo.example.com/users/sign_in
Request Method: POST
Status Code: 302 

sign_in response

location: http://foo.us-west-2.elb.amazonaws.com/users
server: nginx/1.18.0 + Phusion Passenger(R) 6.0.10
status: 302 Found

Notice the request was over https and our domain:

  • https://foo.example.com

But now we're over http and the ELB's domain:

  • http://foo.us-west-2.elb.amazonaws.com

My assumption

The devise gem is seeing the host from the ELB and then generates the URL from the ELB host, creating two issues:

  • we are now on http since the ELB communicates with the ec2 instance over http
  • we are now on the ELB's host name, foo.us-west-2.elb.amazonaws.com, instead of our name, foo.example.com

I've looked into the devise documentation to see if we can just pass in the http protocol and domain to use when creating the post back, but my ruby knowledge is limited. Plus, I think this would be the "bad" path; where the "good" path would be to have the AWS ELB forward the actual domain name, instead of it's own.

I've reviewed several SO and related stack sites with similar questions, but I've either ended up with an infinite loop redirect, or the various config changes have resulted in the same behavior of the devise gem creating the wrong URL post back.

These two questions seem to be the closest, but I'm not quite able to make the "connection" between the answers and my limited knowledge of this ecosystem.

Question

How can I get the AWS ELB to forward our domain, foo.example.com, to the ec2 target group and not the ELB's domain?

解决方案

After more experimentation with AWS settings, the solution is actually rather simple. The other answers I posted in the question were vague in the actual settings, so here's the concrete solution.

In CloudFront, you need to create a new origin request policy, not a cache policy:

  • Open up CloudFront
  • go to Policies (left nav)
  • click the "Origin Request" tab
  • click the "create origin request policy" button
  • name the policy whatever you want, i.e., "my origin request policy"
  • under "Origin request settings" > Headers: select "Include the following headers"
  • under "Add header": check the "Host" option
  • click the "Create" button

The policy will look like this:

Once the new origin request policy has been created:

  • head back to the CloudFront distributions
  • click your distribution's Id so you can edit it
  • click the "Behaviors" tab
  • select your behavior and edit
  • scroll down to "Cache key and origin requests"
  • make sure the "Cache policy and origin request policy (recommended)" is selected
  • under the "Origin request policy - optional", select your new policy, i.e., "my origin request policy"
  • save changes

The behavior will look like this (I'm using no caching for now to verify the ec2 instance is getting all the requests):

That's it. The host header is now correctly passed through to the ELB and ec2 instance. Nothing else needs to be done with the ELB.

I verified the host header was being used in all requests by modifying the nginx logging option to include the $host variable in the log file (and did a bit more customization to the OOB format):

# 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;
}

Surely this will help future me as well as others.

这篇关于如何让 AWS ELB 将实际主机名而不是 ELB 的主机名转发到目标组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆