DEV Community

Joshua Masiko
Joshua Masiko

Posted on

How to secure a Web Application running on a private network.


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.


  • 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 and the server IP address is
We will use the domain 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

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 from another machine on the local network.
We should see the NGINX welcome page.

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/
sudo chown -R $USER:$USER /var/www/
sudo chmod -R 755 /var/www/

Add an index file using your favorite editor.

vi /var/www/

Paste this content into the file and save.

<h1>Welcome to</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/

Add the following content to the file.

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

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


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

Now create a symbolic link.

sudo ln -s /etc/nginx/sites-available/ /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.

To confirm the DNS settings for our domain lets install whois.

sudo apt install -y whois


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


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.

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


Pinging [] with 32 bytes of data:
Reply from bytes=32 time<1ms TTL=64
Reply from bytes=32 time<1ms TTL=64
Reply from bytes=32 time<1ms TTL=64
Reply from bytes=32 time<1ms TTL=64

Ping statistics for
    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
You should see the welcome page we added earlier.

Let's Encrypt Certbot

We will now install the certificate for 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.

Click Generate New Token.

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?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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

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/

server {

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


        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/; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/; # 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 = {
        return 301 https://$host$request_uri;
    } # managed by Certbot

        listen 80;
        listen [::]:80;

    return 404; # managed by Certbot


Now reload the NGINX conf.

sudo nginx -s reload

Now for the moment of truth.

Navigate to
The browser will be redirected to and you should see the lock icon displayed


Certbot also adds a cron entry for renewing the certificate which you can inspect.

less /etc/cron.d/certbot

Top comments (0)