How To Configure LetsEncrypt In Nginx [Ubuntu 18.04]
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
- Root access, or a non-root user with sudo privileges.
- Nginx Server up and running, as described in this guide.
- A fully registered domain name. We will use Example.com in this tutorial.
- 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:
- Certbot installation:
sudo apt update && sudo apt upgrade -y sudo add-apt-repository ppa:certbot/certbot sudo apt install certbot
- CertBot Nginx Plugin Installation:
sudo apt install python-certbot-nginx
Obtaining SSL Certificates
There are numerous ways to secure Nginx using:
- Nginx Plugin: which basically edits Nginx configuration file automatically, and reloads Nginx.
- Webroot Plugin: Webroot obtains a certificate by writing to the Webroot directory of an webserver that is already running.
- 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.
- 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.
- 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.