Nginx + LetsEncrypt + redirection SSL

Posted by ZedTuX 0n R00t on March 11, 2019

Dans cet article je vais expliquer comment mettre à jour les fichiers de configuration afin que LetsEncrypt puisse mettre à jour vos certificats dans le cas où vous avez une redirection HTTP vers HTTPS.

Mon cas précis est le suivant: J’ai une application Ruby On Rails qui est servie par Nginx depuis le dossier /var/www/webroot/ROOT/one/current/public/, avec une redirection du traffic HTTP vers HTTPS.

Le problème est que lorsque certbot renouvel mes certificats, le traffic de LetsEncrypt utilises les URLs en HTTP, et non HTTPS.

Petite information supplémentaire: Le déploiement se faisant avec Capistrano, le dossier current est remplacé à chaque fois, j’ai donc décidé de déplacer le dossier root où le défis LetsEncrypt sera stocké dans /var/www/webroot/ROOT/one.

Comprendre ce qui ne vas pas

Pour ce faire, nous devons sacrifier une tentative de renouvellement des certiifcats. Mais nous allons le faire en mode debug afin d’avoir un max d’infos et comprendre ce qui ne vas pas:

1
/usr/bin/certbot renew -vvvvv --webroot --noninteractive --renew-hook "/bin/systemctl reload nginx"

Le plus important ici c’est le -vvvvv, il veux dire d’augmenter la quantitée de logs.

En observant les informations, je m’apercois que le chemin d’accès au dossier du défis de LetsEncrypt n’est pas le bon.

Ce qui est important à retenir ici c’est l’utilisation du mode debug qui donne plein d’infos en une seule fois.

Combien de fois me suis-je fais bloqué par LetsEncrypt pour avoir dépassé la limite des 5 tentatives par jour … 😅

La configuration d’Nginx

Mes fichiers de configuration d’Nginx ont tous la même structure :

  • Un block HTTP avec redirection vers le block HTTPS
  • Un block HTTPS

Voici un extrait d’un des fichiers de configuration /etc/nginx/sites-available/one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
upstream unicorn-one {
  server unix:/tmp/unicorn.one.socket;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name dev.one.eu;

    location /.well-known/acme-challenge {
        root /var/www/webroot/ROOT/one;
        allow all;
    }

    location / {
        return 301 https://$server_name$request_uri;
    }
}

server {
  listen 443 ssl default_server;
  listen [::]:443 ssl default_server;
  server_name dev.one.eu;

  include snippets/ssl-dev.one.eu.conf;
  include snippets/ssl-params.conf;

  root /var/www/webroot/ROOT/one/current/public;

  access_log  /var/log/nginx/one-access.log;
  error_log   /var/log/nginx/one-error.log;

  location / {
    try_files $uri $uri/index.html $uri.html @unicorn-bender;
  }

  location @unicorn-bender {
    proxy_read_timeout 300;
    proxy_connect_timeout 300;
    proxy_redirect     off;

    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_set_header   Host              $http_host;
    proxy_set_header   X-Real-IP         $remote_addr;
    proxy_set_header   X-Forwarded-For   $remote_addr;
    proxy_pass http://unicorn-one;

    proxy_http_version 1.1;
    chunked_transfer_encoding off;
    proxy_buffering off;
  }
}

Le plus important est donc le block :

1
2
3
4
    location /.well-known/acme-challenge {
        root /var/www/webroot/ROOT/one;
        allow all;
    }

Il permet au défis LetsEncrypt d’être accessible à http://dev.one.eu/.well-known/acme-challenge tout en continuant à rediriger le traffic autre vers mon application dans sa version HTTPS.

Configuration de certbot

Vue que je veux changer l’emplacement où va le défis de certbot, je dois aussi mettre à jour la configuration de certbot pour qu’il place le fichier au bon endroit et ainsi éviter les erreurs stupids qui me ferai atteindre la limite autorisée par jours par LetsEncrypt de 5 tentatives.

Pour retrouver les bon fichiers de configuration de LetsEncrypt rapidement, une petite commande toute simple :

1
$ rgrep "/var/www" /etc/letsencrypt/**/*

Et j’ai la liste des fichiers où je dois changer le chemin de /var/www/webroot/ROOT/one/current/public vers /var/www/webroot/ROOT/one.

Essai avant de lancer certbot

Autre petite astuce pour éviter d’atteindre la limite de 5 tentatives de renouvellement par jours de LetsEncrypt, placez un fichier text dans le dossier, et essayez avec curl d’y accéder.

Sur le serveur :

1
2
$ mkdir -p /var/www/webroot/ROOT/one/.well-known/acme-challenge
$ echo "Hello World !" > /var/www/webroot/ROOT/one/.well-known/acme-challenge/test

Depuis ma machine :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ curl http://dev.one.eu/.well-known/acme-challenge/test
*   Trying 195.16.226.97...
* TCP_NODELAY set
* Connected to dev.bender-juan.eu (195.16.226.97) port 80 (#0)
> GET /.well-known/acme-challenge/test HTTP/1.1
> Host: dev.one.eu
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx
< Date: Mon, 11 Mar 2019 05:36:43 GMT
< Content-Type: application/octet-stream
< Content-Length: 14
< Last-Modified: Mon, 11 Mar 2019 05:36:35 GMT
< Connection: keep-alive
< ETag: "5c85f3e3-e"
< Referrer-Policy: same-origin
< Accept-Ranges: bytes
<
Hello World !
* Connection #0 to host dev.one.eu left intact

Parfait, ca fonctionne !

Lancer le renouvellement

Encore une fois, j’utilises le mode debug, au cas où :

1
$ /usr/bin/certbot renew -vvvvv --webroot --noninteractive --renew-hook "/bin/systemctl reload nginx"

Ce coup-ci, les certificats sont bien renouvellés 🎉.