Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NancyFX + SSL - how to make "this.RequireHttps()" work on Linux?

Tags:

c#

linux

mono

nancy

My self-hosted* NancyFX application use SSL, and I use "this.RequiresHttps()" to mark certain modules "SSL only". On Windows I followed this tutorial:

https://github.com/NancyFx/Nancy/wiki/Accessing-the-client-certificate-when-using-SSL

after:

netsh http add sslcert ipport=0.0.0.0:1234 certhash=303b4adb5aeb17eeac00d8576693a908c01e0b71 appid={00112233-4455-6677-8899-AABBCCDDEEFF} clientcertnegotiation=enable

I used the following code:

public static void Main(string[] args)
{
    List<Uri> uri2 = new List<Uri>();
    uri2.Add(new Uri("http://localhost:80"));
    uri2.Add(new Uri("https://localhost:1234"));

    HostConfiguration hc = new HostConfiguration()
    {
        EnableClientCertificates = true
    };

    using (var host = new NancyHost(hc,uri2.ToArray()))
    {
        host.Start();

        string runningOn = "\n\n";
        foreach(var item in uri2)
        {
            runningOn += item+"\n";
        }

        Console.WriteLine("Your application is running on " + runningOn/*uri2.First()*/);
        Console.WriteLine("Press any [Enter] to close the host.");
        Console.ReadLine();
    }
}

and it works great - unencrypted data can be accessed on port 80 and SSL works on port 1234.

Problem is - I would like to do the same on my Linux host, but I can't seem to find command equivalent to Windows "netsh".

Right now I ended up using nginx to provide SSL, by following this tutorial:

https://github.com/NancyFx/Nancy/wiki/Hosting-Nancy-with-Nginx-on-Ubuntu

then modifying nginx config to the following (don't mind the paths, this is just my development virtual machine):

server {
    listen       80;
    listen       443 ssl;
    ssl_certificate    /home/james/sslCert2/server.crt;
    ssl_certificate_key /home/james/sslCert2/server.key;


    server_name  localhost;
    root /home/james/nancywebpageroot/NancyWebPage.DPL.Services/bin/Debug/;

    location /Content/ {
        alias /home/james/nancywebpageroot/NancyWebPage.DPL.Services/bin/Debug/Content/;
        location ~*  \.(jpg|jpeg|png|gif|ico|css|js|ttf)$ {
            expires 365d;
        }
    }

    location / {
            proxy_pass http://127.0.0.1:8080;
    }       
}

and then modifying Nancy code to listen only on port 8080.

While the above works (nginx is managing SSL connections and redirecting requests to Nancy at port 8080), it makes "this.RequiresHttps()" worthless - when I use it like this:

this.RequiresHttps(true, 443)

Chrome reports ERR_TOO_MANY_REDIRECTS.

So my question is - how to/is it possible to configure Nancy on Linux to make "this.RequiresHttps()" ?

Also could someone from Nancy team, explain a bit more what does "EnableClientCertificates" host configuration option does? Is it require to enable SSL? Documentation is rather scarce...

Thanks in advance.

*while I started project as self-hosted, it can be modified to use nginx or any other hosting form if necessary.

like image 436
user2384366 Avatar asked Sep 29 '22 07:09

user2384366


1 Answers

Combining: help from Nancy team, some other resources and my empty head I finally managed to solve this.

First - the error ERR_TOO_MANY_REDIRECTS was actually logical consequence of nginx configuration from my first post - when user tried to access SSL protected resource, Nancy redirected him to port 443 which is correct behavior, then nginx got request on that port, established SSL connection... and send it back to Nancy at port 8080. Since nginx was always talking to Nancy on unsecured connection (http://127.0.0.1:8080) Nancy had no way of knowing that SSL was being used, so it again redirected the request to port 443, nginx again picked it up, established SSL and send it to http://127.0.0.1:8080 - here is our endless loop. On Windos it worked, because there the application had direct access to both http and https endpoints, and was managing them both.

The fix is rather simple (although it took me some time to find it) and involves two steps:

  1. Add the following line to RequestStartup method in Nancy bootstrapper:

    SSLProxy.RewriteSchemeUsingForwardedHeaders (pipelines);

This will make Nancy listen for X-Forwarded-Proto header - when it's there - this method will override request url scheme to https - so now the:

this.RequireHttps()

will detect request as SSL enabled.

  1. Configure nginx with something like this (still - don't mind the paths - this is just development machine):

    server { listen 80;

    server_name  localhost;
    root /home/james/nancywebpageroot/NancyWebPage.DPL.Services/bin/Debug/;
    
    location /Content/ {
        alias /home/james/nancywebpageroot/NancyWebPage.DPL.Services/bin/Debug/Content/;
        location ~*  \.(jpg|jpeg|png|gif|ico|css|js|ttf)$ {
            expires 365d;
        }
    }
    
    location / {
    proxy_pass http://127.0.0.1:8080;
    }    
    

    }

    server { listen 443 ssl; ssl_certificate /home/james/sslCert2/server.crt; ssl_certificate_key /home/james/sslCert2/server.key;

    server_name  localhost;
    root /home/james/nancywebpageroot/NancyWebPage.DPL.Services/bin/Debug/;
    
    location /Content/ {
        alias /home/james/nancywebpageroot/NancyWebPage.DPL.Services/bin/Debug/Content/;
        location ~*  \.(jpg|jpeg|png|gif|ico|css|js|ttf)$ {
            expires 365d;
        }
    }
    
    location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }    
    

    }

NOTE: I'm not experienced with nginx - while the above seem to work, there may be errors (if anyone sees any - please point them out) or a better way to do this - you have been warned :)

So what is happening here? "Normal" request will hit port 80 and will be simply redirected to Nancy standard path, when nginx get some request on port 443, it will include X-Forwarded-For header, Nancy will detect it and it won't redirect anymore - as it should be.

I hope this helps someone.

Best regards.

like image 132
user2384366 Avatar answered Oct 07 '22 20:10

user2384366