How To Configure LetsEncrypt In Nginx [Ubuntu 18.04]

,
how to install letsencrypt on nginx

LetsEncrypt Installation And Configuration Guide For Ubuntu 18.04

LetsEncrypt is a Certificate Authority which provides free SSL Certificates, secure as paid ones. This guide will walk you through the installation and configuration process on your Nginx Web Server. In this tutorial you will learn how to configure Letsencrypt in Nginx webserver, under Ubuntu 18.04 LTS operating system.

Prerequisites

  1. Root access, or a non-root user with sudo privileges.
  2. Nginx Server up and running, as described in this guide.
  3. A fully registered domain name. We will use Example.com in this tutorial.
  4. Your domain should resolve perfectly, and A records should be put in place prior running LetsEncrypt. To verify that, you can execute:
    sudo dig +short example.com

    If you see your server IP Address showing up, it means that domain’s A records got propagated, and you are good to go.

LetsEncrypt Installation

It’s always a good idea to install new software from PPA because its always up to date compared to Ubuntu’s ones. Certbot is in active development, and we will use this one, instead of Ubuntu’s official repository.

Lets start by installing CertBot, and CertBot Nginx Plugin:

  1. Certbot installation:
    sudo apt update && sudo apt upgrade -y
    sudo add-apt-repository ppa:certbot/certbot
    sudo apt install certbot
  2. CertBot Nginx Plugin Installation:
    sudo apt install python-certbot-nginx

Obtaining SSL Certificates

There are numerous ways to secure Nginx using:

  1. Nginx Plugin: which basically edits Nginx configuration file automatically, and reloads Nginx.
  2. Webroot Plugin: Webroot obtains a certificate by writing to the Webroot directory of an webserver that is already running.
  3. Standalone Plugin: It uses a “standalone” webserver to obtain a certificate, when there is no webserver up and running. Requires port 80 to be open.
  4. DNS Plugins: This category of plugins automates obtaining a certificate by modifying DNS records to prove you have control over a domain. Doing domain validation in this way is the only way to obtain wildcard certificates from LetsEncrypt.
  5. Manual Plugin: Helps you obtain a certificate by giving you instructions to perform domain validation yourself. Moreover, it allows you to specify scripts to automate the validation task in a customized way.

My personal favorite way to obtain a certificate, is the webroot plugin. It allows full control, with no automatic edits of Nginx’s config files.

Obtaining SSL Certificate Using Webroot Plugin

The command below runs certbot with the --webroot plugin, using -d to specify the domain name we’d like to issue a certificate for.

sudo certbot certonly --webroot --webroot-path=/home/username/example.com/html_public -d example.com -d www.example.com

Note that for each to domain that we want to issue a certificate for, the www subdomain is needed as well. That’s because when we redirect www to non-www (and the opposite), the server needs to have both validations in place for the redirect to work.

The webroot plugin works by creating a temporary file for the requested domain in <span class="pre">${webroot-path}/.well-known/acme-challenge</span>. Then the Let’s Encrypt validation server makes an HTTP request to validate that the DNS for your domain resolves to the server running the certbot. This is the reason that the location block

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

was created in our nginx server block.

Obtaining SSL Certificate Using Nginx Plugin

The command below runs certbot with the --nginx plugin, using -d to specify the domain name we’d like the issue a certificate for.

sudo certbot --nginx -d example.com -d www.example.com

In both above cases, if everything goes well you will see something like:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/example.com/privkey.pem
   Your cert will expire on 2019-09-21. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

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

Obtaining SSL Certificate Using DNS Plugin

certbot certonly --manual --preferred-challenges=dns --email user@example.com --agree-tos -d *.example.com

Using this method, the certs need to be manually re-created. If you run certbot renew you will get an error saying “there may be problems with your existing configuration: The error was: PluginError(‘An authentication script must be provided with –manual-auth-hook when using the manual plugin non-interactively.”

SSL Nginx Configuration

Now it’s time to configure Nginx to process the ssl certificate that was created. Below is a fully working ssl enabled config:

server {
    server_name xxx.xxx.xxx.xxx; #Your server IP Address.
    listen 80;
    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    return 301 https://example.com$request_uri; #replace http with https after you obtain the ssl certificate
}
server {
    server_name www.example.com;
    listen 80;
    listen 443 ssl http2;
    listen [::]:80;
    listen [::]:443 ssl http2;
    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    keepalive_timeout   70;
    ssl_buffer_size 1400;
    ssl_dhparam /etc/ssl/nginxdhparam.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=86400;
    resolver_timeout 10;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    # Non-www redirect
    return 301 https://example.com$request_uri; #replace http with https after you obtain the ssl certificate
}
server {
    server_name  example.com;
    listen       *:80;
    return 301   https://example.com$request_uri;
}
server {
    server_name example.com;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    root /home/nikolas/example.com/html_public; #this is the location on disk of where your sites files reside
    charset UTF-8;
    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Referrer-Policy no-referrer;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    keepalive_timeout   70;
    ssl_buffer_size 1400;
    ssl_dhparam /etc/ssl/nginxdhparam.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=86400;
    resolver_timeout 10;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

    location ~* .(jpg|jpe?g|gif|png|ico|cur|gz|svgz|mp4|ogg|ogv|webm|htc|css|js|otf|eot|svg|ttf|woff|woff2)(?ver=[0-9.]+)?$ {
        expires modified 1M;
        add_header Access-Control-Allow-Origin '*';
        add_header Pragma public;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        access_log off;
    }
    #access_log  logs/host.access.log  main;
    #===Let's Encrypt Hidden Directory==============
    location ~ /.well-known {
        allow all;
    }
    location / {
        index index.php;
        try_files $uri $uri/ /index.php?$args; #WordPress Syntax for try_files directive;
    }
    error_page  404    /404.php;
    #pass incoming requests to PHP-FPM service listening on a unix socket
    location ~ .php$ {
        try_files       $uri =404;
        fastcgi_index   index.php;
        fastcgi_pass    unix:/run/php/php7.3-fpm.sock;
        fastcgi_pass_request_headers on;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_param   SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_intercept_errors on;
        fastcgi_ignore_client_abort off;
        fastcgi_connect_timeout 60;
        fastcgi_send_timeout 180;
        fastcgi_read_timeout 180;
        fastcgi_request_buffering on;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
        include fastcgi_params;
    }
    location = /robots.txt {
        access_log off;
        log_not_found off;
    }
    location ~ /. {
        deny  all;
        access_log off;
        log_not_found off;
    }
}

One last thing that we need to do to have our server fully secured, is to create ssl_dhparam.

How To Create ssl_dhparam For Nginx

The DH parameters (Diffie-Hellman algorithm) provides the capability for two communicating parties to agree upon a shared secret between them. It can be created like so:

sudo openssl dhparam -out /etc/ssl/nginxdhparam.pem 4096

Depending on your server’s cpu speed, this calculation could take up to 30 mins.

After this proccess is finished, reload Nginx:

sudo systemctl reload nginx

Or

sudo service nginx reload

Automatic Certificate Renewal

Unfortunately, Let’s Encrypt certificates are only valid for 90 days, and this creates the urge for an automated renewal procedure. Fortunately for us though, the Certbot package we installed, created an cron job automatically, so we don’t have to do it by hand. On non-systemd distributions of Certbot, this functionality is located in /etc/cron.d, and runs 2 times per day.

This can be checked like so:

sudo cat /etc/cron.d/certbot

Where you will see the output:

# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc.  Renewal will only occur if expiration
# is within 30 days.
#
# Important Note!  This cronjob will NOT be executed if you are
# running systemd as your init system.  If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob.  For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root test -x /usr/bin/certbot -a ! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew

Notice that Certbot renewal process is running every 12 hours. To test that, you can simply issue the command:

sudo certbot renew --dry-run

When the time for renewal will come (30 days before certificate expiration), Certbot will renew the certificate for your domain, and will restart Nginx to apply the changes.

And there you have it. Nginx is now fully secured, with the most modern standards in place. You can test your SSL Certificate at Qualys SSL Server Test or read more documentation at cerbot website.