3

Setup

Hi. I'm deploying an ASP.Net Core application to AWS Elastic Beanstalk. The platform I'm running on is 64bit Amazon Linux 2/2.1.5 using Nginx as the proxy server software. I've got a pair of listeners for my load balancer set up in the environment configuration. They are set up as follows:

  • Port=443 Protocol=HTTPS SSL=certificate Process=default
  • Port=80 Protocal=HTTP Process=default

And I've got a single process:

Name=default Port=80 Protocol=HTTPS

Problem

On my ASP.Net Core server, I'm trying to check if the original client to the server is communicating over HTTPS or HTTP. As I understand, the X-Forwarded-Proto header for requests should carry this information. However, the value of X-Forwarded-Proto is always http regardless of how a client connects to the server. Why is the X-Forwarded-Proto not ever set to https even when connected as so from my web browser?

Thanks in advance for any help!

Matthew Miller
  • 555
  • 3
  • 15
  • 1
    Are you sure Nginx isn't overriding that header when it forwards requests to your .NET service? – Mark B May 17 '21 at 13:54
  • @MarkB From what I understand, it's not. Here's the relevant setting in the Nginx config file: `proxy_set_header X-Forwarded-Proto $scheme;` – Matthew Miller May 17 '21 at 15:37
  • 1
    That line in your Nginx config file is literally doing the exact thing you say it is not. It is taking the scheme that was used to connect to Nginx (your load balancer connects to Nginx using HTTP) and setting that as the `X-Forwarded-Proto`, overwriting anything that the load balancer may have set in that header. – Mark B May 17 '21 at 16:36
  • @MarkB Ok. So that implies that `$scheme` being set based solely on the communication between load balancer and Nginx. The load balancer should be sending a `X-Forwarded-Proto` header though. Does `$scheme` not consider that header? – Matthew Miller May 17 '21 at 16:58
  • 1
    No, `$sheme` is literally just the protocol that was used to connect to Nginx. You should delete that entire line from your Nginx config to allow the header to pass through, or else do something like what is documented [here](https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/) – Mark B May 17 '21 at 17:08
  • @MarkB Yep, you are completely right. It took a little while to actually replace the config on the AWS server. I wouldn't never thought it was the problem so thanks for pointing it out. It's weird that this very basic AWS setup doesn't automatically forward the correct header in its default Nginx config. – Matthew Miller May 18 '21 at 13:13
  • 1
    I don't think it's weird at all. You shouldn't make assumptions that Nginx will somehow detect it's running on AWS and "do the right thing". You simply need to be aware of how the settings you configure Nginx with are actually going to work. – Mark B May 18 '21 at 13:24
  • @MarkB. Right, I get that. It's just that this particular configuration was not my own. It was a defaults set up by AWS specifically for Elastic Beanstalk which is always run behind a load balancer. That's why I didn't expect it to be the problem. – Matthew Miller May 18 '21 at 13:27
  • 1
    I see what you are saying now, and the default Nginx config for Elastic Beanstalk really should handle that better. However, Elastic Beanstalk does support single-instance configurations without a load balancer. – Mark B May 18 '21 at 14:24

1 Answers1

9

The problem was in the Nginx configuration as pointed out by @MarkB. AWS Elastic Beanstalk has a default configuration file 00_application.conf in /etc/nginx/conf.d/elasticbeanstalk that is the culprit. It has a declaration:

proxy_set_header    X-Forwarded-Proto     $scheme;

that needed to be changed to:

proxy_set_header    X-Forwarded-Proto     $http_x_forwarded_proto;

To overwrite this file, I used the method detailed here: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/platforms-linux-extend.html.

I added a file .platform/nginx/conf.d/elasticbeanstalk to the root of my deployed project. It contains:

location / {
    proxy_pass          http://127.0.0.1:5000;
    proxy_http_version  1.1;
    proxy_cache_bypass  $http_upgrade;
    proxy_set_header    Upgrade               $http_upgrade;
    proxy_set_header    Connection            $http_connection;
    proxy_set_header    Host                  $host;
    proxy_set_header    X-Forwarded-For       $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto     $http_x_forwarded_proto;
}

I also had to add a middleware to my ASP.Net Core application to use the forwarded headers as noted in this answer: Redirect URI sent as HTTP and not HTTPS in app running HTTPS.

I added the following to my Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders =
            ForwardedHeaders.XForwardedFor |
            ForwardedHeaders.XForwardedProto;
        options.KnownNetworks.Clear();
        options.KnownProxies.Clear();
    });
    //...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //...
    app.UseForwardedHeaders();
    //...
}

I hope this helps others!

Matthew Miller
  • 555
  • 3
  • 15
  • 1
    For what it's worth, directly using that file content for `.platform/nginx/conf.d/elasticbeanstalk` seems to fail with an error: nginx: [emerg] "location" directive is not allowed here – qid Nov 09 '21 at 20:12
  • 1
    The file needs to be name 00_application.conf and placed into the folder .platform/nginx/conf.d/elasticbeanstalk – Snickers May 18 '22 at 03:55
  • @Snickers is correct - the file is `/etc/nginx/conf.d/elasticbeanstalk/00_application.conf` - confirmed that after AWS Toolkit right mouse publish that the `.platform/nginx/conf.d/elasticbeanstalk/00_application.conf` file in your .Net proj overwrites the /etc/nginx file of the same name. Checked via SSH into the EC2 instance. – StuartLC Apr 05 '23 at 12:45