5 October 2016

Nginx & Letsencrypt: HTTPS for all


Securing a web server using HTTPS not only gives you encryption but also guarantees that the request reaches the destination without being compromised.

The request crosses over a lot of network paths to reach the destination, and if not secured (using HTTP), its content can be changed or it can be redirected to another server without knowing it.

With a well configured HTTPS server, using a public Web-Of-Trust, you have this assurance.

This post will briefly cover how Letsencrypt works, and how to configure Nginx to handle the ACME challenge protocol. Also, we will see how to tell Nginx to redirect all HTTP traffic to HTTPS, and will use safe SSL/TLS Ciphers parameters.

Letsencrypt overview

For about one year now (mid 2015) Letsencrypt has been able to generate HTTPS certificates for free. We will not dig into the details of HTTPS certificate validation here, you can find plenty of information about that on the Internet. Basically, your web browser includes some well known Certificate Authorities (CA) public keys that are trusted by default. Then if a certificate encountered on a web site is signed with one of these authorities, your web browser will considered the connection as secure.

Letsencrypt has cross-signed their certificate with IdenTrust. IdenTrust is a certificate authority that is known by major browsers. So if you encountered a Letsencrypt certificate, it will be trusted without any security alert by your web browser.

The following diagram gives you an overview of the actual Letsencrypt Certificate Chain-Of-Trust:

[object Object]
isrg_keys.png, Oct 2016

ACME protocol challenge

Letsencrypt certificate generation and signature is made really easy thanks to their ACME (Stands for : Automatic Certificate Management Environment) protocol challenge.

Everything described below aims at giving an overview of the ACME protocol challenge. But you do not have to do this step manually (and in fact, it can be pretty hard to do it), everything is automated in the Letsencrypt tool (called certbot).

Register to the ACME server

The first time you want to deal with Letsencrypt certificate generation, you generate locally an asymmetric key pair and contact information. Contact information is then signed by this key pair.

Letsencrypt ACME server will get your public key and your contact information to register an account on the server. The server will verify that you hold the private key. All messages sent to the ACME server will be signed using your private key, and the ACME server will authenticate these messages with your public key.

Certificate Generation

When you ask for a certificate generation for a domain name, the ACME protocol challenge checks that you own the domain name (DNS). An HTTP request is made on this domain, and verify on a pre-defined URL request on your web server, that you own the private key associated with your account.

Once the validation is done, the client generates a certificate and a Certificate Signing Request (CSR) which is signed using your private key. Then the signature and CSR are sent to the ACME server.

ACME server checks the signature and if it matches and if the server agrees to issue the certificate, signs your certificate and sends back to you the signature.

You can find more information about the ACME protocol challenge here.

Install Letsencrypt on Debian Jessie (8)

The easiest Letsencrypt client (that implements the ACME protocol challenge) to use is called « certbot ». It is not available directly in Debian Jessie (8) but a back-ported version is available.

Check in your APT sources.list that you have enabled the jessie-backports repository:

deb http://httpredir.debian.org/debian jessie-backports main

Do not worry, packages from jessie-backports are configured with a version (~) that does not take precedence over Stable Debian packages. So if you do not force your package management utility to use Jessie-backports, it will keep using those coming from stable.

To install certbot run:

# apt update
# apt install -t jessie-backports certbot

Certbot is written in Python, and it depends on a lot of Python packages.

A crontab is also set-up by the Debian package to automate the certificate renewal. You can see it in:


Using certbot with Nginx

If you followed the previous steps, you understand that, in order to do the ACME challenge, certbot needs to :

  • Be able to communicate with the ACME server on port 80
  • Access a directory of your Nginx server to expose some signing materials that are checked by the ACME server.

Certbot offer several methods to accomplish that:

  1. Apache2
  2. Standalone
  3. Webroot

Apache2 method can be ignored, we are targeting Nginx.

Standalone method can also be ignored, since we already have an Nginx web server on port 80 (HTTP). We can use it, but manually, because Nginx server must be stopped before using certbot.

Here we use the webroot method.

Configure Nginx to accept the ACME challenge and redirect all HTTP traffic to HTTPS

We create a default virtualhost for all HTTP requests on port 80. If a request is for the ACME server, we serve a path to handle the challenge. If not, we redirect the request to the same address but using a secured HTTPS connection.

The default path to be configured by Nginx to handle the challenge, is « /var/www/letsencrypt/ » (but you are free to choose another folder)

Configure the "default" virtualhost by editing the file « /etc/nginx/site-available/default »:

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;

        include snippets/letsencrypt-acme-challenge.conf;

        location / {
          return 301 https://$host$request_uri;
        access_log /var/log/nginx/letsencrypt_acme-access.log;

The magic line that enables redirection for all HTTP requests to HTTPS is the following:

return 301 https://$host$request_uri;

301 is an HTTP return code to tell the client (i.e. your web browser) that the address has "Moved Permanently" to another location. Here, the same location but with "https://" instead of "http://".

Now, create the file « /etc/nginx/snippets/letsencrypt-acme-challenge.conf » with this content:

location ^~ /.well-known/acme-challenge/ {
    # Set correct content type.
    default_type "text/plain";
    root         /var/www/letsencrypt;
# Hide /acme-challenge subdirectory and return 404 on all requests.
location = /.well-known/acme-challenge/ {
    return 404;

Certbot client configuration

We need to configure the certbot client to let it know the Nginx Letsencrypt root path and make it use the webroot method by default.

Create a file in « /etc/letsencrypt/cli.ini », and enter this:

# Uncomment and update to register with the specified e-mail address
email = <Your Email to register to the ACME server>

authenticator = webroot
webroot-path = /var/www/letsencrypt/

Bonus: 4096 Key size

Also, you can upgrade the default certificate key size. Letsencrypt certbot client generates by default a 2048 bits key. To generate a more secure certificate, for example 4096 bits long, add this to the file « /etc/letsencrypt/cli.ini »:

# Use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

Running Certbot

We must reload Nginx to be able to serve the virtualhost for ACME challenge protocol.

# systemctl reload nginx

We can now run certbot client in order to ask Letsencrypt for a certificate signature.

To sign the domain example.com, and the sub-domain sub.example.com, we must check that the DNS for example.com and sub.example.com point to the server that runs certbot client. ACME server will try to access both domain using HTTP request, and will check for particular files in the webroot to authenticate you as a legitimate ACME user and as the owner of example.com AND sub.example.com.

If you have checked theses prerequisites, you can run certbot:

# certbot certonly -d example.com -d sub.example.com

You should see this message:

 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/<Your.domain.tld>/fullchain.pem. Your cert will
   expire on YYYY-MM-DD. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
 - 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

Hardening Nginx for SSL

I am not a security wizard, and even if I have worked on security software (mainly for Set-Top-Box system) you should always stay up-to-date on security-related news. Computer security is constantly evolving.

The following Nginx security parameters come from this website [raymii.org].

Create a file « /etc/nginx/snippets/ssl_letsencrypt.conf ». You will include it in all your virtualhosts.

Letsencrypt Certificate, Key and Chain

You need to give Nginx the private key and Letsencrypt key chain with:

ssl_certificate /etc/letsencrypt/live/<Your.domain.tld>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<Your.domain.tld>/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/<Your.domain.tld>/chain.pem;

Diffie-Hellman parameters

First if you have not already done that, generate strong enough Diffie-Hellman parameters for Nginx:

# openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096

This process takes about 30 minutes on a good server, with enough entropy on the system.

Pass the generated Diffie-Hellman parameters:

ssl_dhparam /etc/nginx/ssl/dhparam.pem;

Use Secure Ciphers

We will use only Secure ciphers, that will works on major web browser. Also, we force the server to choose the cipher.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

HTTP Strict Transport Security

We are enabling HSTS, which stands for HTTP Strict Transport Security, and enable it for all sub-domains. By doing this, the client's web browser knows that all the domain AND sub domains (option : includeSubDomains) must be visited using HTTPS. This will be valid for 63072000 seconds (750 days)

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";

HTTP Public Key Pinning

The Web-Of-Trust mechanism for SSL/TLS can be a good thing. But in fact, you trust Letsencrypt because your browser is already bundled with the trust of Letsencrypt CA. Consider that Letsencrypt Certification Authority get compromised, and an attacker can issue SSL/TLS certificates for your site. He can forge the request (Using man in the middle) and made think to your browser that the HTTPS connection is legitimate... (Just to say that a lot of Company proxies does that, in order to be able to watch the HTTPS traffic from their employees.)

HPKP, stands for HTTP Public Key Pinning, allows you to protect yourself by providing a white list of Public key that the browser should trust. HPKP will say for a period of time, that it should only accept a set of certificates.

But HPKP can be problematic, especially with Letsencrypt CA. You have to choose to enable it or not ! Letsencrypt says that they don't support this mechanism officially. Please read this.

Personally, for the moment, I have chosen not to enable HPKP with Letsencrypt and the Nginx configuration provided here will not enable it. But if you want to, read this excellent post from Scott Helme.

Online Certificate Status Protocol Stapling

OCSP (Stands for "Online Certificate Status Protocol") is a protocol to check whether an SSL Certificate has been revoked. It will basically make a request to an OCSP responder, a server configured by the Certification Authority (CA), that will respond about the revocation of the certificate.

OCSP stapling allows the web server to query the OCSP responder directly and will be presented in the SSL/TLS handshake via the Certificate Status Request extension response. The client web browser checks that it have the same response from the web server and the OCSP responder.

ssl_stapling on;
ssl_stapling_verify on;
resolver valid=300s;
resolver_timeout 5s;

To check OCSP stapling, you can run openssl to make a TLS request to your server with this command :

$ openssl s_client -connect <You_domain>:443 -tls1 -tlsextdebug -status |grep -i ocsp

You should see something like :

OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response

Full snippet file

Below the full snippet file, remember that I choose to save it in « /etc/nginx/snippets/ssl_letsencrypt.conf »:

# SSH Certificate & Chain
ssl_certificate /etc/letsencrypt/live/<Your.domain.tld>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<Your.domain.tld>/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/<Your.domain.tld>/chain.pem;

# DH Parameters
ssl_dhparam /etc/nginx/ssl/dhparam.pem;

# Safe SSL Ciphers

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

# HSTS Header
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver valid=300s;
resolver_timeout 5s;

You should now include this file in your virtualhost defined to serve HTTPS on port 443.

Taking virtualhost example.com in "/etc/nginx/site-available/example.com" :

server {
        listen 443 ssl;
        listen [::]:443 ssl;
        include snippets/ssl_letsencrypt.conf;
        server_name example.com;

Certificate Renewal

Letsencrypt certificate are only valid for 3 months. But the certificate renewal is automated thanks to Certbot client and the crontab installed by the Debian package.

When one of your certificate will be near from expiration, Certbot will detect it and renew your certificate.

Be aware that Nginx server needs to be reloaded to take the new certificate. Maybe you will have to add a line in the crontab to reload Nginx with :

# systemctl reload nginx


You have generated a valid HTTPS certificate using Letsencrypt and configured Nginx to use this certificate with some common HTTPS security features enabled. But do not think you are always safe with that configuration. Security is a moving domain, and I advise you to read often common security news and leak.

Also we are only covering SSL Hardening here, and not all the HTTP headers hardening. To avoid confusing, I will talk about Nginx Headers hardening in another post.

You can check the security grade of your web server, using some tools on the Internet, like this one.

To conclude, Letsencrypt team have done a very good work and that lead to a revolution for a more secured Internet. At this time of writing, and since the launch of Letsencrypt to the public in Q4 2015, the HTTPS growth rate have quadrupled ! So please consider donate to them.

Nowadays, everyone should use an HTTPS connection...

Source Links

21 September 2013

Nouveau Serveur gonflé aux chroot LXC

On dépoussière le blog, ni vu ni connu ! (Et oui, ça faisait une éternité que je n’avez pas posté de billet…) Nous sommes passé de OVH (Kimsufi, sans trop de patates) à Online (Dedibox en édition limité) qui nous offre un peu plus de souplesse niveau ressources. Tellement d’ailleurs, que je me suis  […]

Continue reading

17 September 2009

Juggle Light Graph


Deux petites photos très sympathiques qui commencent à dater Ps: merci pedrov :)  […]

Continue reading

28 April 2009

Le domaine Audible de l'oreille humaine (Attention Migraine !)

La légende commune dit qu’un humain entend les sons de 20Hz à 20KHz en moyenne. La bande de fréquence se réduit avec l’âge pour à priori plusieurs raisons :

  • Dégradation de l’audition
  • Perte progressives des sons de hautes fréquences (> 16KHz) du fait du peu d’habitude d’entendre des sons aussi aigus.

Continue reading

27 April 2009

im.beneth.fr -> Ouverture d'un serveur Jabber

Pourquoi Jabber ?

Imaginer tout simplement, que depuis une adresse MSN vous puissiez rajouter un contact sur ICQ ? Une seule adresse pour joindre des contacts connectés sur des serveurs complètement différents. C’est exactement le principe d’XMPP (Jabber). Avec une adresse Gmail, en utilisant Gtalk qui utilise en fait le protocole Jabber, vous pouvez ajouter des contacts de serveurs différents comme "@jabber.fr" ou bien maintenant "@im.beneth.fr" ou tant d’autres !

Continue reading

14 October 2008

Carte Graphique ATI (Chip R500) Accélération 3d Libre !

Ça faisait longtemps que je voulais poster sur ce sujet. Depuis maintenant quelque temps, les chips ATI r500 bénéficient de l’accélération 3d libre avec le driver ati ! Je ne perd pas de vu le driver Radeonhd, qui d’ailleurs vient d’inclure DRI pour les chips r500 et rs600, cependant après  […]

Continue reading

23 June 2008

Les artistes unis contre le téléchargement

[object Object]

Étienne Daho, Christophe Maé, Kery James, Sinik, Francis Cabrel, Patrick Bruel, Jean-Jacques Goldman, Jenifer, Stanislas, Raphaël, M Pokora, Keren Ann, Thomas Dutronc, Eddy Mitchell, Isabelle Boulay, Maxime Le Forestier, Martin Solveig, Marc Lavoine, Calogero, Gérard Darmon, Pascal Obispo, Jacob  […]

Continue reading

15 May 2008

Faille de Sécurité Critique dans OpenSSL de Debian.

Une faille de sécurité existante depuis 2006 dans OpenSSL, vient d’être publiée. Celle ci est critique car toute les générations de clés censé être aléatoire, n’était pas si aléatoire que ça au final ! De ce que j’ai compris, le générateur de nombre aléatoire se basait seulement sur le PID du  […]

Continue reading

16 October 2007

Driver libre ATI " Radonhd " Enfin une Alternative !

Ma carte Graphique étant une Radeon Mobility X1700, aucun driver libre ne faisait fonctionner cette carte sur mon laptop. J’avais comme seule alternative libre le driver VESA qui est limité à une résolution de 1024x768. Sur un écran de portable supportant du 1440x900 c’est un peu limite... Je fus  […]

Continue reading

8 May 2007

ATI Radeon X1700 sous linux ( Debian Etch )

J’ai fais très récemment l’acquisition d’un ordinateur Portable muni d’une carte graphique Radeon Mobility X1700.

Je me suis empressé d’installer mon OS favoris, mais j’ai eu beaucoup de mal à installer correctement le driver propriétaire de ATI.

Pourquoi le propriétaire ? Tout simplement parce que pour ce type de carte, le driver libre ne marche pas, il ne semble pas supporter le chipset X1700; en revance, le driver propriétaire, bien que cela ne soit pas mentionné sur le site, le supporte.

Voilà donc la marche à suivre pour installer ce fameux driver et sortir du très sobre mode VESA ...

Petit recommandation:
(Je pense que cela ne marche pas avec xorg 7.2, la version stable de etch étant dans les 7.1.x , il y a surement des astuces qui devraient figurer dans les liens à la fin du billet. Mon noyau est un 2.6.21-1 fraichement compilé.)
Up : Cela marche très bien avec xorg 7.2 depuis les drivers 8.37.6. Le driver fonctionne et se compile parfaitement sous une debian Sid.

Continue reading

16 April 2007

Mencoder, encodage Avi et Split d'une Video

J’ai récupéré hier des Vidéos enregistrées depuis la Freebox HD, petit problème c’est du format DVD et on a en général beaucoup d’image entre ce qui nous interresse.   Voilà donc comment j’ai procédé vite fait bien fait, pour que plus tard je m’en rappelle facilement.   ps: Je ne saurai expliquer  […]

Continue reading

8 April 2007

Installer Postfix + Maildrop, Comptes Virtuels avec gestion Mysql

Ce billet est un aide mémoire des étapes pour l’installation d’un Serveur mail (ici Postfix) avec un ajout simplifié d’utilisateur à travers une base mysql.

Continue reading

Page top