16

I have a website that runs HTTPS correctly in my local environment. When I upload it to AWS it just times out or redirects forever.

My setup in AWS is an Elastic Beanstalk application, an RDS database running MS SQL, I added a Load Balancer to forward the HTTPS requests, and I have a SSL certificate properly assigned to the Load Balancer. From all I can tell my app is running, in fact, Entity Framework fired off and correctly built my database in my RDS instance. I just can't reach the website through the internet.

I've tried setting the Listeners different ways. If I set them like this, it just redirects forever: Redirect Forever

If I set them like this, it just times out: enter image description here

I have the default HTTP/HTTPS port forwarding code in my Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // Sets all calls to require HTTPS: https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl
    services.Configure<MvcOptions>(options =>
    {
        options.Filters.Add(new RequireHttpsAttribute());
    });
    ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // Force all HTTP requests to redirect to HTTPS: https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl
    var options = new RewriteOptions().AddRedirectToHttps();
    app.UseRewriter(options);

    ...

    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                            ForwardedHeaders.XForwardedProto
    });
    ...
}

I've spent days on this and I can't get it to work. I've tried taking all of my HTTPS code out and that doesn't work. I've tried code solutions from blogs like this and this and that doesn't work either. From what I've read, the Load Balancer ends up handling the HTTPS request and then forwards an HTTP request to my app. But I don't know how to properly handle that, still enforce HTTPS, and redirect HTTP to HTTPS.

This seems like it would be something that would just work out of the box without a bunch of setup from me. If it's not, I would think a lot of other people would have run into this problem by now and there'd be info about it on the internet. Am I missing something small? Because I'm totally at my wit's end about it.

If you can answer this, you'll be my new hero.

Lauren Yim
  • 12,700
  • 2
  • 32
  • 59
Pinski
  • 2,607
  • 2
  • 24
  • 25

5 Answers5

19

So I finally got this fixed. First, the Load Balancer has to be set to forward HTTPS 443 to HTTP 80 like this: enter image description here

Then, ALL the code I've outlined in my question needs to be deleted (or not run in the AWS environment). I forgot to remove the services.Configure<MvcOptions>(options){} lines of code initially and I believe that was what was causing the error.

Then I followed this blog to handle the X-Forwarded-Proto header. I put all the code in one extension file:

public static class RedirectToProxiedHttpsExtensions
{
    public static RewriteOptions AddRedirectToProxiedHttps(this RewriteOptions options)
    {
        options.Rules.Add(new RedirectToProxiedHttpsRule());
        return options;
    }
}

public class RedirectToProxiedHttpsRule : IRule
{
    public virtual void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        // #1) Did this request start off as HTTP?
        string reqProtocol;
        if (request.Headers.ContainsKey("X-Forwarded-Proto"))
        {
            reqProtocol = request.Headers["X-Forwarded-Proto"][0];
        }
        else
        {
            reqProtocol = (request.IsHttps ? "https" : "http");
        }


        // #2) If so, redirect to HTTPS equivalent
        if (reqProtocol != "https")
        {
            var newUrl = new StringBuilder()
                .Append("https://").Append(request.Host)
                .Append(request.PathBase).Append(request.Path)
                .Append(request.QueryString);

            context.HttpContext.Response.Redirect(newUrl.ToString(), true);
        }
    }
}

Finally, I call this code in Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    ...
    var options = new RewriteOptions()
        .AddRedirectToProxiedHttps()
        .AddRedirect("(.*)/$", "$1");  // remove trailing slash
    app.UseRewriter(options);
    ... 
}

After all that it finally worked!

Pinski
  • 2,607
  • 2
  • 24
  • 25
  • Thanks. I finally got this to work. I had modify the HealthCheck page from the default / to an html file (ie. HealthCheck.html). – Ásgeir Gunnar Stefánsson Dec 23 '17 at 15:27
  • This works great. But another problem, if the Azure Authentication is used, all request are sent through http and rewritten afterwarts. I have to add a reply url with http to the list of the app in azure. Any idea? – Mathias Müller Aug 08 '18 at 12:58
  • No, sorry, I'm not familiar with Azure's infrastructure. Good luck. – Pinski Aug 09 '18 at 03:50
4

According to this AWS docs you must analyze X-Forwarded-Proto header and response with redirects only when it is http (not https).

Current RedirectToHttpsRule from Microsoft.AspNetCore.Rewrite package does not analyze this. You need to implement your own IRule.

Dmitry
  • 16,110
  • 4
  • 61
  • 73
  • So I tried analyzing the X-Forwarded-Proto header following [this blog](http://blog.treeloop.com/asp-net-core-url-rewrite-module-behind-load-balancer/) as a guide. I still couldn't get it to work. – Pinski Oct 12 '17 at 18:59
  • Got it working with a little more diligence with following the blogs example. Thanks for your input and help. – Pinski Oct 12 '17 at 22:21
4

app.UseForwardedHeaders() seems to have issues with AWS Load Balancers unless you clear the known networks and proxies first.

Don't forget to install the Microsoft.AspNetCore.HttpOverrides NuGet package first otherwise it will fail silently.

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        ...
        app.UseForwardedHeaders(GetForwardedHeadersOptions());
        ...
    }

    private static ForwardedHeadersOptions GetForwardedHeadersOptions()
    {
        ForwardedHeadersOptions forwardedHeadersOptions = new ForwardedHeadersOptions()
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        };

        forwardedHeadersOptions.KnownNetworks.Clear();
        forwardedHeadersOptions.KnownProxies.Clear();

        return forwardedHeadersOptions;
    }
ArcadeRenegade
  • 804
  • 9
  • 14
  • I tried this and it didn't fix the problem. My solution did fix the problem though. I'm using an older version of Microsoft.AspNetCore.HttpOverrides (1.1.2), could that be a problem? – Pinski Oct 13 '17 at 04:19
  • Yeah this handles the correct protocol from the X-Forwarded-For header for most middleware but apparently not for Rewrite.AddRedirectToHttps I ended up having to use your solution myself :) but I had to change it a little bit to not redirect http:// if the X-Forwarded-Proto header was missing. That way my Load Balancer health check didn't get redirected to https. – ArcadeRenegade Oct 13 '17 at 04:26
  • ArcadeRenegade can you please share what you did for the health check? – Wayne Jun 17 '20 at 04:45
  • For redirect_uri issues, make sure that the forwarded headers is set BEFORE the AddAuthentication() call. Additionally, I found that on an AWS EB Linux deploy, that the NGINX proxy wasn't set correctly to actually forward the HTTPS headers received on the load balancer to the HTTP port on my app. I had to [follow this](https://stackoverflow.com/q/67570998) to get nginx to set the `X-Forwarded-Proto` header to HTTPS – StuartLC Apr 05 '23 at 12:58
  • @StuartLC how do you add this before `AddAuthentication()`? `AddAuthentication()` is on the services object, which is done before you build the app to add UseForwardedHeaders. Did you mean before `app.UseAuthentication();`? – odyth Aug 05 '23 at 22:27
  • @odyth ping me stuart@vanestum.com - I did eventually get a net core https listener on eb with a lb going. It also requires nginx config. Can help tomorrow – StuartLC Aug 05 '23 at 22:52
1

You need to accept the XForwardedProto

In Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
  ...
    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders = ForwardedHeaders.XForwardedProto;
    });
   ...  
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
 ...
 app.UseForwardedHeaders();
 ...
}
Dan
  • 12,808
  • 7
  • 45
  • 54
1

I was facing same issue.I finally got this fixed by changing web.config file.

Below Exact code Works for me. I follow this link. If URL rewrite module is not install then you will have to install this on your instance otherwise only this web.config file change will works.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>    
  <system.webServer>        
    <rewrite>            
      <rules>                
        <rule name="HTTPS rewrite behind AWS ELB rule" enabled="true" stopProcessing="true">
          <match url="^(.*)$" ignoreCase="false" />
          <conditions>
            <add input="{HTTP_X_FORWARDED_PROTO}" pattern="^http$" ignoreCase="false" />                    
          </conditions>                    
          <action type="Redirect" url="https://{SERVER_NAME}{URL}" redirectType="Found" />                
        </rule>            
      </rules>        
    </rewrite>    
  </system.webServer>
</configuration>