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.
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.
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.
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.
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.
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
Certbot also adds a cron entry for renewing the certificate which you can inspect.
less /etc/cron.d/certbot
Latest comments (1)
I use acme certbot for win64, on a windows server with a DNS server.
I have not a purchased domain, Only an 'a record' in the dns: "certbot.int" that points to a local ip. although the IIS site with the same name: certbot.int is found in the browser and nslookup finds certbot.int but the certbot tool does not find this domain and says:
[certbot.int] {"type":"urn:ietf:params:acme:error:dns","detail":"DNS problem: NXDOMAIN looking up A for certbot.int - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for certbot.int - check that a DNS record exists for this domain","status":400,"instance":null}
[certbot.int] Deactivating pending authorization
So is it work with local dns server domains or should I purchase a domain and use it inside my network?