Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.net Core - HTTPS with AWS Load Balancer and Elastic Beanstalk doesn't work

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://docs.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://docs.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.

like image 511
Pinski Avatar asked Oct 12 '17 04:10

Pinski


People also ask

How do you activate load balancer in Elastic Beanstalk?

In the Load balancer configuration category, choose Edit. Select the Classic Load Balancer option, if it isn't already selected. Make any Classic Load Balancer configuration changes that your environment requires. Choose Save, and then make any other configuration changes that your environment requires.

Does Network Load Balancer support HTTPS?

Today we are simplifying the process of building secure web applications by giving you the ability to make use of TLS (Transport Layer Security) connections that terminate at a Network Load Balancer (you can think of TLS as providing the “S” in HTTPS).


1 Answers

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!

like image 61
Pinski Avatar answered Oct 15 '22 02:10

Pinski