How to secure a Web Application running on a private network.
Tags: nginx, webdev, certbot, digitalocean
Published: 2020-03-13 18:11:23

##Introduction


This article describes how to secure a web app running on a private network under these constraints:

* The server is not reachable via the public internet.
* The server is only reachable by clients on a LAN using private IP addresses.
* The internet gateway for the server _may_ not have a  Static Public IP.
* We do not want to use a self-signed certificate.
* The certificate generation process should be automated.
* The certificate renewal process should also be automated.

##Prerequisites


* Ubuntu 18.04 LTS running on a server reachable over the local network.
The server should be able to connect to the internet to download the required software.

* A registered domain with any of the popular registrars with a panel where you can update the Name Server entries.

In this article: 
The network segment is **192.168.1.0/24** and the server IP address is **192.168.1.4**
We will use the domain  **private.hamsterdam.me** as an example.

## The components in the stack are:
 **Ubuntu Linux 18.04 LTS** will run the Webserver, Firewall and certificate generation and renewal tool.

 **NGINX web server** will terminate HTTPS traffic. It can proxy traffic to an application running on the same server or another machine on the LAN

 **Let's Encrypt Certbot** will automate TLS/SSL certificate generation and renewal.

 **Digital Ocean** will handle management of the DNS zone. Certbot has a plugin for the Digital Ocean API that uses the dns-01 challenge which involves creating TXT records. The dns-01 challenge allows us to get around the http-01 challenge requirement to have a public IP reachable via the internet.


These steps were performed on a fresh installation of Ubuntu 18.04 server.

## First, we install NGINX

```
sudo add-apt-repository universe
sudo apt update
sudo apt install -y nginx
```

We will use the ufw firewall to manage access to the server. The firewall is disabled by default.

```
sudo ufw status

Status: inactive
```

Let's list ufw default application profiles.

```
sudo ufw app list
Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
```

We will allow HTTP and HTTPS and SSH traffic through the firewall.
HTTP maps to port 80 (normal, unencrypted web traffic) and HTTPS to port 443 (TLS/SSL encrypted traffic).

We will also allow OpenSSH since we need to login to the server over the network.
The _Nginx Full_ profile allows both HTTP and HTTPS traffic.

```
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
```
Now enable the firewall
```
sudo ufw enable
```
Check the status again and confirm the firewall is active and SSH and HTTP(S) are allowed.
```
sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)
```

We can confirm that that web server is reachable by navigating to http://192.168.1.4 from another machine on the local network.
We should see the NGINX welcome page.
![nginx-welcome.PNG](https://l2earn.nyc3.digitaloceanspaces.com/l2earn/images/uploads/2020/02/12/665fbab876-nginx-welcome.PNG) 

The next step is to set up the NGINX web root and server block for our domain.
First create the web root folder and apply the necessary permissions. 
```
sudo mkdir -p /var/www/private.hamsterdam.me/html
sudo chown -R $USER:$USER /var/www/private.hamsterdam.me/html/
sudo chmod -R 755 /var/www/private.hamsterdam.me/
```
Add an index file using your favorite editor.
```
vi /var/www/private.hamsterdam.me/html/index.html
```
Paste this content into the file and save.
```
<h1>Welcome to private.hamsterdam.me</h1>
```

Set up server block.
The ubuntu NGINX convention is to add configuration files to the _sites-available_ folder
and then enable them by creating symbolic links in the _sites-enabled_ folder.

```
sudo vi /etc/nginx/sites-available/private.hamsterdam.me
```

Add the following content to the file.

```
server {
        listen 80;
        listen [::]:80;

        root /var/www/private.hamsterdam.me/html;
        index index.html index.htm index.nginx-debian.html;

        server_name private.hamsterdam.me;

        location / {
                try_files $uri $uri/ =404;
        }
}
```
Now create a symbolic link.
```
sudo ln -s /etc/nginx/sites-available/private.hamsterdam.me /etc/nginx/sites-enabled/
```
Verify that the NGINX settings are ok and reload the configuration.
```
nginx -t
sudo nginx -s reload
```

## Setup DNS


This step requires you to update the DNS NS records at your registrar to point to the Digital Ocean servers.

Digital Ocean provides detailed instructions at  [this link](https://www.digitalocean.com/community/tutorials/how-to-point-to-digitalocean-nameservers-from-common-domain-registrars).

To confirm the DNS settings for our domain lets install whois.
```
sudo apt install -y whois

whois hamsterdam.me
```
The response will have multiple line lines. We are interested in lines that start with _Name Server_ to confirm that NS records are set up correctly

```
Name Server: NS1.DIGITALOCEAN.COM
Name Server: NS2.DIGITALOCEAN.COM
Name Server: NS3.DIGITALOCEAN.COM
```

In the Digital Ocean control panel for your domain add an A record for the domain that will serve the web app.
Normally we would use the public IP of the server but here we use the private IP of the server.
![dns.PNG](https://l2earn.nyc3.digitaloceanspaces.com/l2earn/images/uploads/2020/02/12/707beebf4f-dns.PNG) 

After adding the record ping private.hamsterdam.me from a machine on the Local network until it resolves successfully.
```
ping private.hamsterdam.me

Pinging private.hamsterdam.me [192.168.1.4] with 32 bytes of data:
Reply from 192.168.1.4: bytes=32 time<1ms TTL=64
Reply from 192.168.1.4: bytes=32 time<1ms TTL=64
Reply from 192.168.1.4: bytes=32 time<1ms TTL=64
Reply from 192.168.1.4: bytes=32 time<1ms TTL=64

Ping statistics for 192.168.1.4:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
```

Now confirm that the NGINX block is correctly set up  by navigatiing to http://private.hamsterdam.me
You should see the welcome page we added earlier.
![private_dns.PNG](https://l2earn.nyc3.digitaloceanspaces.com/l2earn/images/uploads/2020/02/12/a353ffd7d7-private_dns.PNG) 


## Let's Encrypt Certbot

We will now install the certificate for private.hamsterdam.me using the certbot client.

Add the Certbot repository for Ubuntu.
```
sudo add-apt-repository ppa:certbot/certbot
```
Press ENTER at the prompt.

Then install Certbot’s Nginx package.
```
sudo apt install -y python-certbot-nginx
```
We will use the Digital Ocean certbot plugin to automate certificates generation and renewal.

Install the plugin.
```
sudo apt install -y python3-certbot-dns-digitalocean
```

The plugin authenticates to the Digital Ocean API using a token obtained from the [Digital Ocean account’s Applications & API Tokens page.](https://cloud.digitalocean.com/settings/api/tokens)



![tokens.PNG](https://l2earn.nyc3.digitaloceanspaces.com/l2earn/images/uploads/2020/02/12/69c1e963f4-tokens.PNG) 
Click _Generate New Token_.
![newtoken.PNG](https://l2earn.nyc3.digitaloceanspaces.com/l2earn/images/uploads/2020/02/12/6f1388851a-newtoken.PNG) 

Then enter a name for the token and click  _Generate Token_.

The token is displayed in the list as a long hexadecimal value.
_Make sure you copy this value as it is only displayed once_.

Next, create a folder for the credentials file.
```
mkdir -p .secrets/certbot/
```

Use your favorite editor to create the credentials file.
```
vi .secrets/certbot/digitalocean.ini

```

Then add an entry to the credentials file that looks like this
```
dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff
```
The value after the equals sign should be the token you copied from the Digital Ocean panel.

Update the file permissions to prevent the file from being read by other users.
```
chmod 600 ~/.secrets/certbot/digitalocean.ini
```

Generate the certificate.
```
sudo certbot --authenticator dns-digitalocean --installer nginx --dns-digitalocean-credentials ~/.secrets/certbot/digitalocean.ini
```
When prompted, Enter you email address, Agree to the terms of service and Choose whether to share your email or not.

You will then be prompted to select the domain.
```
Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: private.hamsterdam.me
```
In this case there is only one entry so select 1 and press ENTER.
```
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
```
We choose option 2 so that unencrypted requests are redirected to the secure port.

If all goes well you will see a congratulatory message and further NOTES.
```
Congratulations! You have successfully enabled https://private.hamsterdam.me
```

If we inspect our NGINX conf we will see that certbot has made various changes to
enable HTTPS and redirect HTTP traffic to the secure port.
```
less /etc/nginx/sites-enabled/private.hamsterdam.me

server {

        root /var/www/private.hamsterdam.me/html;
        index index.html index.htm index.nginx-debian.html;

        server_name private.hamsterdam.me;

        location / {
                try_files $uri $uri/ =404;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/private.hamsterdam.me/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/private.hamsterdam.me/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = private.hamsterdam.me) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;

        server_name private.hamsterdam.me;
    return 404; # managed by Certbot


}
```

Now reload the NGINX conf.
```
sudo nginx -s reload
```

Now for the moment of truth.

Navigate to http://private.hamsterdam.me
The browser will be redirected to https://private.hamsterdam.me and you should see the lock icon displayed

![https_redirect.PNG](https://l2earn.nyc3.digitaloceanspaces.com/l2earn/images/uploads/2020/02/12/8d5aca4188-https_redirect.PNG) 

Certbot also adds a cron entry for renewing the certificate which you can inspect.
```
less /etc/cron.d/certbot
```