Securing an ASP.NET Core app on Ubuntu using Nginx and Docker – Part III


 In Part I of this tutorial, we created a self-contained ASP.NET Core web application using the new dotnet CLI tools, configured PuTTY and PSCP to SSH and transfer files, and then finally transfer the self-contained app from a Windows environment to the Ubuntu VM. Part II discussed setting up Docker, creating a Docker Image, and running your application from a Docker container.

Now, to properly secure our application we shall issue a self-signed certificate using Windows Powershell and configure Nginx to use that certificate to do SSL Termination. SSL Termination is a simple concept, the client connection to the Nginx load balancer is over HTTPS, and the Nginx load balancer retrieves the data from a private network over HTTP. Load balancing is when you distribute the web requests among many servers to improve performance. The following diagram illustrates Nginx SSL Termination:

nginx_ssl_term.png

Let’s install and configure Nginx to do SSL Termination!


 

Step 1: Create an entry in your host file and Install Nginx

Add an entry in your host file to make life easier.

cd /etc/
sudo nano hosts

Add the following to the bottom (replace with your Ubuntu host IP!)

192.168.xxx.xxx MyNetCoreApp

Install Nginx

sudo apt-get update
sudo apt-get install nginx

Adjust Nginx firewall settings

View firewall registered applications:

sudo ufw app list

Register Nginx as a firewall app if it is not displayed

sudo ufw allow ‘Nginx Full’

Check the status of the Web Server
systemctl status nginx

Step 2: Create and export a self-signed SSL certificate

Before creating the certificate, let’s create a temporary folder to hold the SSL files.

mkdir ~/temp-ssl/

 

Now launch powershell with administrative priveledges on your Windows machine.

Enter the following command:

New-SelfSigned-Certificate -DnsName <AD FS Machine IP> -CertStoreLocation Cert:LocalMachinemy -FriendlyName “UbuntuCert”

Export the .pfx certificate via running mmc.exe.

  • File -> Add or Remove Snap-ins
  • Select “Certificates” and hit “Add >”
  • Select the “Computer Account” radio button and hit “Next”
  • Select “Local Computer” and hit “Finish”
  • Browse to the Personal -> Certificates directory, locate the Machine IP named certificate
  • Right click -> All Tasks… -> Export. IMPORTANT: Select “Yes, export the private key”
  • Add a password in the security window and hit next

Use PSCP to copy the certificate from your windows machine to your Ubuntu temp-ssl folder 

pscp C:<path to cert><cert>.pfx <username>@192.168.xxx.xxx:/home/<username>/temp-ssl

Step 3: Use OpenSSL to extract the public and private key

Install OpenSSL on your Ubuntu Machine

sudo apt-get install openssl

Extract the public key

openssl pkcs12 -in [yourfile.pfx] -clcerts -nokeys -out [cert.crt]

Extract the private key

openssl pkcs12 -in [yourfile.pfx] -nocerts -out [keyfile-encrypted.key]

Get rid of the password protection on the private key

openssl rsa -in [keyfile.key] -out [keyfile-decrypted.key]

Create the folder to hold the SSL certs: /etc/nginx/ssl

sudo mkdir /etc/nginx ssl

Copy the decrypted private key as well as the public key to /etc/nginx/ssl

sudo cp ~/temp-ssl/<filename> /etc/nginx/ssl

Now that we have a public and private key ready, we may configure Nginx for SSL Termination!

Step 4: Create a server block to configure SSL Termination

Nginx server blocks are the equivalent to virtual hosts in Apache. They contain configuration details and enable us to do things such as host more than one domain off a single server, do load balancing, as well as redirect HTTP to HTTPS traffic (enable SSL).

Let’s create the server block

cd /etc/nginx/sites-available

sudo nano myTestServer

Begin inputting the contents of the server block as follows

http {
    upstream http-backend {
         server MyNetCoreApp:8081
     } 
     server {
       listen MyNetCoreApp:80; 
       return 301 https://$host:443$request_uri;
     }
     server {
        listen MyNetCoreApp:443 ssl http2;
        root /etc/netcore/mySecureApp/;
        server_name MyNetCoreApp;

        ssl on; 
        ssl_certificate /etc/nginx/ssl/cert.crt
        ssl_certificate_key /etc/nginx/ssl/keyfile-decrypted.key

        location / {
                 try_files $uri $uri/ =404;
                 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_pass              http://http-backend;
                 proxy_read_timeout      90;
        }
    }
}


Let’s review the contents of the server block

Load balancing

    upstream http-backend {
         server MyNetCoreApp:8080
     }

Here, we may add several other servers if we wish to achieve load balancing. This block is what the nginx web server will serve content from with a label “http-backend” that will always select he most available resource host. Port 8080 shall be mapped to 5001 (HTTPS). You may also map 8080 to 443, but then all the traffic between the Nginx load balancer and private networks becomes secured, resulting in wasted performance and improper SSL Termination. Again, the following diagram illustrates the goal of SSL Termination; use a load balancer to handle HTTPS traffic from the client while accessing content over HTTP from the most available resource in the private network. 

nginx_ssl_term.pngHTTP redirect to HTTPS Server block

     server {
       listen MyNetCoreApp:80; 
       return 301 https://$host:443$request_uri;
     }

This server block listens to all connections on the default HTTP port 80 and 301 redirect them to an HTTPS connection over port 443. The $host and the $request_uri variables are obtained from the header values.

Main server block for serving content

     server {
        listen MyNetCoreApp:443 ssl http2;
        root /etc/netcore/mySecureApp/;
        server_name MyNetCoreApp;

        ssl on; 
        ssl_certificate /etc/nginx/ssl/cert.crt
        ssl_certificate_key /etc/nginx/ssl/keyfile-decrypted.key

        location / {
                 try_files $uri $uri/ =404;
                 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_pass              http://http-backend;
                 proxy_read_timeout      90;
        }

This server block listens to all connections over the 443 HTTPS port on MyNetCoreApp (which we added in our host file)

The root for my content is specified as /etc/netcore/mySecureApp/

The server_name is specified as MyNetCoreApp

The ssl_certificate and ssl_certificate_key are the locations of the ssl certificates that we moved earlier.

The location block here provides a 404 error if the file is not found, sets the$host, $remote_addr, and forwarding data in the header. The proxy_pass is important as it tells nginx to load the data from the load balancer into an http:// connection, this should ring a bell, the SSL Termination is over HTTP when accessing files from the network.

Now that we’ve configured SSL Termination, let’s reload the nginx service.

sudo services nginx reload

To enable the website, create a link in /etc/nginx/sites-enabled/

sudo ln -s /etc/nginx/sites-available/myTestServer /etc/nginx/sites-enabled

If you desire to disable a website, you can simply remove the link in sites-enabled.

Step 5: Build and run the Docker container! 

Navigate to the self-contained app directory
cd /etc/netcore/mySecureApp

Build the docker image
sudo docker build -t mydemos: HelloWorld .

Note: -t adds a tag (mydemos:…) and the trailing period tells docker to look in the current directory for the Dockerfile to build from.

Run the docker image
sudo docker run -p 8080:5001 -t mydemos:mySecureApp

Note: -p specifies what port on the local machine we wish to map onto the container. Port 8080 will be mapped to 5001 because our nginx load balancer is expecting to serve content from 8080, and we wish to load the content over HTTP.

Now you have a working, secure, dockerized ASP.NET Core HelloWorld web application hosted on Ubuntu with Nginx SSL Termination.

It’s important to note that this blog is being posted in the pre-release days of ASP.NET Core, and many of these things are subject to change or break as Microsoft finishes revising ASP.NET Core for its final release. I will make it my duty to update this, but if I fall behind feel free to leave a comment or reach me at my LinkdIn posted above.

Leave a Reply