Tuesday, February 21, 2017

Let's Encrypt Free A+ Grade SSL On Nginx & Ubuntu 16.04



Prerequisites:

LEMP on Ubuntu 16.04

Start by updating apt indexes

sudo apt-get update

Install Let's Encrypt Package

(Known as certbot on Github (https://github.com/certbot/certbot))
sudo apt-get install letsencrypt
I'll assume the domain name we are working on is called mysite.com and you have installed and configured Nginx from the tutorial mentioned above.

Open Nginx Configuration file
sudo vi /etc/nginx/sites-available/mysite.com

Make .well-known directory accessible

We do this because Let's Encrypt will use .well-known directory to validate the SSL.
Add it before denying hidden files (Entire configuration file is below)
    location ~ /.well-known {

        allow all;

    }

Test Nginx Syntax

sudo nginx -t

Reload Nginx

sudo systemctl reload nginx

Generate the Certificate

sudo letsencrypt certonly -a webroot --webroot-path=/var/www/mysite.com/html -d mysite.com -d www.mysite.com

The results will be similar to the following:
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/mysite.com/fullchain.pem. Your
   cert will expire on 2017-05-17. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Generate a 2048 or 4096 bits Diffie Hellman Symmetric Key

I'll generate the Diffie Hellman in Nginx installation to keep things organized, you can create it anywhere you want (If you create it somewhere else, make sure to edit the configuration file below to reflect your changes)
sudo mkdir /etc/nginx/ssl
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096
This might take a while (4096 bits might take around 30 minutes on a modern CPU), but once it's done, you will have a 4096 bits symmetric key

Configuring Nginx to Read the Generated Certificates

By default Let's Encrypt will store all the certificates (expired and running) in
/etc/letsencrypt/archive/mysite.com
However, to avoid changing the nginx configuration file everytime, letsencrypt plugin will create a symlink to the latest generated certificate in
/etc/letsencrypt/live/mysite.com

Let's open Nginx configuration file:
sudo vi /etc/nginx/sites-available/mysite.com

And add/modify the new SSL location
listen 443 ssl http2 default_server;
ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;

Improve SSL Security to get Grade A+ on SslLabs
Paste the following below the certificates
#From cipherli.st
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver $DNS-IP-1 $DNS-IP-2 valid=300s;
resolver_timeout 5s;
##If you're not aware of how preload works, keep it disabled for now
##More info on preload can be found in
##blog.mozilla.org/security/2012/11/01/preloading-hsts
##&
##hstspreload.org
#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains;";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
#Add Diffie Hellman that we have previously generated 
ssl_dhparam /etc/nginx/ssl/dhparam.pem;

Make sure to replace $DNS-IP-1 $DNS-IP-2 with your DNS IPs, if you don't know or don't have any, you can replace them by Google's DNS 8.8.8.8 & 8.8.4.4 

The entire configuration file will look like:
server {
    # Redirect to www
    server_name mysite.com;
    rewrite ^/(.*)$ http://www.mysite.com/$1 permanent;
}

server {
    # Domain name
    server_name www.mysite.com;

    # Location of files
    root /var/www/mysite.com/html;
     # Location of access & error Logs
    access_log /var/log/nginx/www.mysite.com.access.log;
    error_log /var/log/nginx/www.mysite.com.error.log;

    # Listen to Port 80 (http)
    listen 80 default_server;

    #Listen on SSL
    listen 443 ssl http2 default_server;

    # ssl on;
    ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;

    #From cipherli.st
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off; # Requires nginx >= 1.5.9
    ssl_stapling on; # Requires nginx >= 1.3.7
    ssl_stapling_verify on; # Requires nginx => 1.3.7
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
    ##If you're not aware of how preload works, keep it disabled for now
    ##More info on preload can be found in
    ##blog.mozilla.org/security/2012/11/01/preloading-hsts
    ##&
    ##hstspreload.org/
    #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains;";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
     #Add Diffie Hellman that we have previously generated
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    # Default file to serve. If the first file isn't found,
    index index.php index.html index.htm;

    # Don't log favicons
    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    # Configuring robots.txt
    location = /robots.txt {
         allow all;
         log_not_found off;
         access_log off;
    }

    # Configure 404 Pages
    error_page 404 /404.html;

    # Error 50x Pages
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/www;
    }

    location ~ /.well-known {
         allow all;
    }

    # Denying all attempts to access hidden files .abcde
    location ~ /\. {
         deny all;
    }

    # Expiry date headers for static files and turn off logging.
    location ~* ^.+\.(js|css|swf|xml|txt|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        access_log off; log_not_found off; expires 30d;
    }

    # Rewrite rules, sends everything through index.php
    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    # Deny access to PHP Files in the uploads directory
    location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
    }

    # Enable PHP Support
    location ~ \.php$ {
       include snippets/fastcgi-php.conf;
       fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    }

    # Enable Rewrite Rules for Yoast SEO SiteMap
    rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

    # Add trailing slash to */wp-admin requests.
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;
}

Test Nginx Syntax
sudo nginx -t

Reload Nginx
sudo systemctl reload nginx

Renew Certificates Automatically

In order to renew the certificates automatically, we will setup a cron job. By default, Let's Encrypt only renew a certificate 1 month before it expires (after 2 months out of 3), otherwise it will skip renewal. Therefore, we can run the cron job once every week (or less if you wish), and Let's Encrypt will automatically skip or renew all the certificates.

Open crontab as Root
sudo crontab -e

Append the following 2 lines at the end of the file
0 0 * * 0 letsencrypt renew  >> /var/log/letsEncryptAutoRenew.log
0 0 * * 0 systemctl reload nginx

Configure the Ubuntu Firewall To Allow HTTPS
sudo ufw allow 'Nginx HTTPS'
or
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'

Test your site SSL on SSL Labs

https://www.ssllabs.com/ssltest/analyze.html?d=mysite.com




Questions or comments? Leave them below!