2
votes

I am currently trying to build an API for a client interface-server interaction. I have decided to use ASP.NET Core for the API with Nginx as the hosting platform (On Ubuntu 18.04). Since ASP.NET uses Kestrel, we have set up a reverse proxy to forward requests from Nginx to Kestrel-- what is hosting the API. We have SSL set up on the NGINX server, however it is not set up on the Kestrel Server.

Simply put, I do not know how to set up SSL on the Kestrel Server with another layer of SSL on the NGINX side. How can I do this?

Model: Client --> GET Request over HTTPS --> NGINX with SSL --> HTTP Kestrel Server and vice versa

Output: SSL_PROTOCOL_ERROR

Temporary Solution: Use HTTP with port 5000 in the link.-- No error, however, data is not secure.

Optimal Solution: Use HTTPS without port 5000 in the link. Data is secure.

NGINX Config:

    if ($host = api.OURSITENAME.co) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name api.OURSITENAME.co;
    return 301 https://$server_name$request_uri;


}

server {
    listen 443 ssl http2;
    include /etc/nginx/proxy_params;
    server_name api.OURSITENAME.co;
    access_log /var/log/nginx/api.access.log;
    error_log /var/log/nginx/api.error.log error;
    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/api.OURSITENAME.co/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/api.OURSITENAME.co/privkey.pem; # managed by Certbot
    proxy_http_version 1.1;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_set_header Upgrade $http_upgrade;

    location / {
        proxy_pass         http://172.18.0.2:5000; <-- Docker Container. Can easily be switched out with localhost if we want to run on dotnet directly.
    }
}
2
If nginx and kestrel are on the same server (or in a private network) then you don't need SSL on kestrel. nginx handles the public requests and SSL, your app does not send it's data publicly. Is that your scenario?Rosco
@Rosco Well I understand that I don't think I need SSL on Kestrel, however trying to connect to the website on HTTPS, I get a 504 timed out. Upon inserting the port 5000 in the link, where the Kestrel server is located, I get an SSL error.Velvet

2 Answers

1
votes

As I understand the problem, when you use HTTP to access your application directly on port 5000 you get an SSL error. Even though you don't use HTTPS.

If you have app.UseHsts(); and/or app.UseHttpsRedirection(); in your Startup code then it will use HTTPS.

If you are letting nginx handle the SSL then you can remove code from your app Startup.cs

Typical startup code:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else // Production
    {
        app.UseExceptionHandler("/Error");
        // Remove to use HTTP only
        app.UseHsts(); // HTTPS Strict mode
    }

    // Remove to use HTTP only
    app.UseHttpsRedirection(); // Redirects HTTP to HTTPS
    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseAuthentication();

    app.UseMvc();

}

Documentation on enforcing SSL in dotnet core

0
votes

This simply happens because Kestrel is not configured to handle HTTPS requests. A quick look at MS Docs shows you how.

You can use listenOptions and its various extensions to specify your SSL certificate and other configurations

webBuilder.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(listenOptions =>
    {
        // certificate is an X509Certificate2
        listenOptions.ServerCertificate = certificate;
    });
});

Also, if you used CreateDefaultBuilder from your Program.cs, then you can configure your SSL/HTTPS from appsettings.json because CreateDefaultBuilder calls Configure(context.Configuration.GetSection("Kestrel")) by default to load Kestrel configurations. A sample config file highlighting Kestrel config (from MS Docs) is shown below:

{
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://localhost:5000"
      },
      "HttpsInlineCertFile": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      },
      "HttpsInlineCertStore": {
        "Url": "https://localhost:5002",
        "Certificate": {
          "Subject": "<subject; required>",
          "Store": "<certificate store; required>",
          "Location": "<location; defaults to CurrentUser>",
          "AllowInvalid": "<true or false; defaults to false>"
        }
      },
      "HttpsDefaultCert": {
        "Url": "https://localhost:5003"
      },
      "Https": {
        "Url": "https://*:5004",
        "Certificate": {
          "Path": "<path to .pfx file>",
          "Password": "<certificate password>"
        }
      }
    },
    "Certificates": {
      "Default": {
        "Path": "<path to .pfx file>",
        "Password": "<certificate password>"
      }
    }
  }
}

Make sure to visit the docs if you need more info.

Nevertheless, in my opinion, I don't see much advantage with a double layer of SSL protection. One downside I can immediately notice is some delay as a result of encryption/decryption. A single layer of SSL on your reverse proxy should be more than enough.