DigitalOcean ❤ Let’s Encrypt

DigitalOcean + Let’s Encrypt

Bad news, My beloved DigitalOcean’s droplets just get hijack. Good news is it’s nothing there just random POC Meteor, And after spiked traffic which somehow break DigitalOcean’s limit, my poor droplets has been shut down completely. (sigh)

That moment when you get hijack

At that time I didn’t care much about security because it’s just development droplet for POC. But this hijack kick me to get serious and harden my security!

Speaking of which, Wasin Thonkaew just share letsecure.me link which very nice tutorials there but it’s not wrote for my DigitalOcean.

Also today Let’s Encrypt just leaving beta which mean it’s a perfect time to mess with it. (1.7 million certificates can’t we wrong!)

1.7 million certificates

I decide to rewrite my own version to suite my need for DigitalOcean and my new deomain name rabbot.io which I just register at NameCheap.

cheapest price for .io and super nice UI/UX there!

If you on the same path, I wish this’ll save you sometime like always. :)

Infrastructure setup

New Droplets and better use SSH this time ;)

Add SSH to DigitalOcean’s droplets
# Lists the files in your .ssh directory, if they exist
$ ls -al ~/.ssh
# Copies the contents of the id_rsa.pub file to your clipboard
$ pbcopy < ~/.ssh/id_rsa.pub

Add a Domain

Create Record

Add www

Add DigitalOcean's nameservers to NameCheap’s domain registrar.

ns1.digitalocean.com.
ns2.digitalocean.com.
ns3.digitalocean.com.
Setup DigitalOcean’s nameservers on NameCheap

Get in

$ ssh root@rabbot.io
locale warning

Not again, Just set it and exit.

export LANGUAGE="en_US.UTF-8" 
echo 'LANGUAGE="en_US.UTF-8"' >> /etc/default/locale
echo 'LC_ALL="en_US.UTF-8"' >> /etc/default/locale
exit

Get back in

$ ssh root@rabbot.io

Basic security hardening of your server

Setup UFW (Uncomplicated Firewall) rules

sudo ufw allow out 22/tcp
sudo ufw allow out 80/tcp
sudo ufw allow out 443/tcp

Get update to ensure all software is up to date and reboot.

sudo apt-get update && sudo apt-get dist-upgrade -y && sudo reboot

Enable automatic security updates

sudo dpkg-reconfigure --priority=low unattended-upgrades
Automatic security updates

Install fail2ban to prevent brute force SSH attacks

sudo apt-get install -y fail2ban

Setup NGINX

# Sadly the key is being served over http, so you need to check it's sha256 hash to ensure that it hasn't been somehow compromised
wget --quiet http://nginx.org/keys/nginx_signing.key -O nginx_signing.key && sha256sum nginx_signing.key
# At the time of writing the sha256sum is "dcc2ed613d67b277a7e7c87b12907485652286e199c1061fb4b3af91f201be39"
# Please ensure that you get the same result before proceeding further
sudo apt-key add nginx_signing.key
echo "deb http://nginx.org/packages/mainline/ubuntu/ trusty nginx" | sudo tee --append /etc/apt/sources.list.d/nginx_org_packages_mainline_ubuntu.list
sudo apt-get update && sudo apt-get install -y nginx

Create the website root folder with demo

sudo mkdir /var/www/
# download our demo website
wget https://github.com/llambiel/letsecureme/releases/download/1.0.0/demo.tar.gz
sudo tar zxf demo.tar.gz -C /var/www
sudo chown -R root:www-data /var/www/

Remove the default Nginx configuration and start with a fresh blank file

sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.orig
sudo nano /etc/nginx/conf.d/default.conf

Copy and Paste this and cmd+o and enter to save then cmd+x to exit

server {
listen 80;
server_name default_server;
root /var/www/demo;
}

Reload Nginx to apply our configuration

sudo nginx -t && sudo nginx -s reload

Now when you enter your site you should see this.

Let’s Encrypt Default Page

Setup Let’s Encrypt

sudo apt-get install -y git
sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
/opt/letsencrypt/letsencrypt-auto

SSL certificates

Replace rabbot.io with your domain
export DOMAINS="rabbot.io,www.rabbot.io"
export DIR=/var/www/demo
/opt/letsencrypt/letsencrypt-auto certonly --server https://acme-v01.api.letsencrypt.org/directory -a webroot --webroot-path=$DIR -d $DOMAINS

Enter email and agree term then you should see

Nginx HTTPS config

Open default.conf for add our SSL Certificate.

sudo nano /etc/nginx/conf.d/default.conf

Modify

Replace rabbot.io with your domain
server {
listen 443 ssl;
server_name rabbot.io www.rabbot.io;
root /var/www/demo;
ssl_certificate /etc/letsencrypt/live/rabbot.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rabbot.io/privkey.pem;

}

Save, Exit and then restart Nginx

sudo nginx -t &&  sudo nginx -s reload

Now we got https://www.rabbot.io/ up and running! But we not done yet!

Automatic Renew

sudo nano /home/recers.sh

Then add below content then Save, Exit.

#!/bin/sh
# This script renews all the Let's Encrypt certificates with a validity < 30 days
if ! /opt/letsencrypt/letsencrypt-auto renew > /var/log/letsencrypt/renew.log 2>&1 ; then
echo Automated renewal failed:
cat /var/log/letsencrypt/renew.log
exit 1
fi
nginx -t && nginx -s reload

Add daily cron job.

sudo crontab -e

Then add below content then Save, Exit.

@daily /home/recers.sh

Make it executable.

chmod +x /home/recers.sh

Now it become zombies yeah! But if you test with SSL analyser, You’ll grade only B grade which is bummer! Gimme A!

Nginx SSL/TLS hardening

Edit config all over again.

Do you think why we’ve to do this again and again? Me too! But I’ll keep continue with the original flow for now. ;p
server {
listen 80;
listen 443 ssl http2;
server_name rabbot.io www.rabbot.io;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_certificate /etc/letsencrypt/live/rabbot.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rabbot.io/privkey.pem;
ssl_session_cache shared:SSL:128m;
add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
ssl_stapling on;
ssl_stapling_verify on;
# Your favorite resolver may be used instead of the Google one below
resolver 8.8.8.8;
root /var/www/demo;
index index.html;
    location '/.well-known/acme-challenge' {
root /var/www/demo;
}
    location / {
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
}
}

Save, Exit and then restart Nginx

sudo nginx -t &&  sudo nginx -s reload

Now you should get A+ SSL and HTTP/2 nice!

HTTP/2 SSL
To proof you get HTTP/2 protocol you need to add Protocol column via Dev tools and it’ll shown as “h2”

Are we done yet? Not really this analyser still give us E Noooooo.

Security headers hardening

Open default.conf for add our SSL Certificate. (Yes again!, below step is copy and paste from original site)

sudo nano /etc/nginx/conf.d/default.conf

The X-Content-Type-Options header stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type.

add_header X-Frame-Options "SAMEORIGIN" always;

The X-Frame-Options header tells the browser whether you want to allow your site to be framed or not. By preventing a browser from framing your site you can defend against attacks like clickjacking.

add_header X-Xss-Protection "1";

The X-Xss-Protection header sets the configuration for the cross-site scripting filter built into most browsers.

add_header Content-Security-Policy "default-src 'self'";

Then enable report mode , You can read more about this here.

Content-Security-Policy-Report-Only instead of Content-Security-Policy

Final config will look like this… (Gist look better huh?)

Save, Exit and then restart Nginx

sudo nginx -t &&  sudo nginx -s reload

Now you should finally get A from securityheaders.io , Yeah!

About Public Key Pinning, please read more here.

Addition Steps

Backup letsencrypt from remote to local by run this via local.

$ scp -r root@rabbot.io:/etc/letsencrypt letsencrypt

Keep your private key secure by set the right permission so only you can read it at remote.

# chmod 400 -R /etc/letsencrypt/archive

Conclusion

letsecure.me really help me understand what going on along the way. Next step for me is try to squeeze HTTP/2 speed to it limit. This should be fun! :)

Note (ref)

You can get free $10 for DigitalOcean (which is 2 months free hosting).
You can get cheapest .io and nice UI/UX at NameCheap.

Happy Securing!

Show your support

Clapping shows how much you appreciated katopz’s story.