Tutoriel Linux : sécuriser NginX avec mTLS (authentification mutuelle)

Lorsqu’on auto-héberge des services accessibles par le web, il est courant d’utiliser le serveur web NginX.

Le cadre architectural dans lequel on travaille est toujours le même :

Un client web se connecte à l’un de nos services web auto-hébergés, protégé par du https.

Concrètement cela signifie que le client va pouvoir authentifier le serveur web, en récupérant le certificat https que celui-ci présente et en vérifiant la signature de ce certificat par une autorité de confiance (ici Let’s encrypt). Pour réaliser cela, le navigateur web du client utilise juste la clé publique de l’autorité Let’s encrypt, dont il dispose (les clés publiques de toutes les autorités ayant pignon sur rue sur Internet sont intégrés dans les navigateurs). Le client n’aura donc même pas à contacter Let’s encrypt pour vérifier que le certificat présenté par le serveur est authentique, et donc que l’on peut faire confiance à ce site : il est bien qui il prétend être.

Cette mécanique a été pensée pour favoriser le e-commerce, pour donner confiance au consommateur de sorte qu’il accepte de fournir son numéro de carte bancaire tout en ayant l’assurance ni qu’elle soit interceptée pendant la transmission, ni qu’elle soit transmise à un site margoulin.

Pratiquement tout le web fonctionne comme cela, et vous serez obligé de rester sur ce modèle si vos services auto-hébergés s’adressent à un large public, que vous ne connaissez pas (un site wordpress par exemple).

Dans ce modèle, le serveur est authentifié par le client, mais le client n’est pas authentifié par le serveur. Dans ce modèle, le serveur tente de susciter la confiance du client, mais le client ne se soucie pas de susciter la confiance du serveur. Le postulat de base est que le serveur est connu, et les clients sont anonymes. Le site se chargera de vérifier l’identité du client plutôt en lui présentant une page demandant son login et son mot de passe de client commercial.

Ce modèle est-il adapté par contre si votre VPS n’a pas vocation à offrir des services web à la terre entière mais seulement à vous et à quelques uns de vos amis ?

La réponse est oui, cela fonctionnera. Les pages vous seront accessibles bien sûr, mais elles seront également accessibles à la terre entière. Ok la terre entière tombera sur une page de login / mot de passe, qu’elle n’a pas. Mais qu’est-ce-qui l’empêche de tenter des milliards de mots de passe pour essayer de rentrer ? Rien en fait.

Il existe donc un autre modèle, le mTLS.

Dans ce modèle, le serveur présente toujours au client les certificats que lui a fournis Let’s encrypt. Ce qui change par contre c’est que le client dispose lui aussi de certificats prouvant son identité et il les présente au serveur. Cette vérification mutuelle donne son nom au modèle : mTLS (pour mutual TLS, TLS étant le nom technique de ce que le grand public appelle https).

Avec ce modèle, il faut que l’authentification mutuelle réussisse pour que la page web proposée par le serveur soit envoyée au client et apparaisse sur son écran.

Un tiers qui ne dispose pas des certificats client et qui tente une connexion au serveur ne verra même pas la page de login. Il ne sera donc pas capable de lancer une salve de tentatives d’intrusion.

Lorsqu’on met en ligne un VPS dédié à un nombre très restreint d’utilisateurs (par exemple les employés de notre micro-entreprise), il peut être utile d’activer l’authentification mutuelle mTLS. Elle permet aux utilisateurs authentifiés d’accéder aux services web du VPS, tout en rendant ces derniers invisibles au reste d’Internet.

Nous allons voir dans cette section comment activer le mTLS

La première chose à faire est de créer un certificat pour une machine client donnée (client1), en tapant les commandes :

cd
mkdir mtls
cd mtls
openssl req -x509 -nodes -days 3650 -newkey rsa:4096 -keyout client1.key -out client1.crt
openssl pkcs12 -export -out client1.pfx -inkey client1.key -in client1.crt

Dans le répertoire il y a maintenant 3 fichiers : client1.crt, client1.key et client1.pfx

Sur le VPS nous allons créer le répertoire /etc/ssl/selfsigned, dans lequel nous allons copier le fichier client1.crt créé sur le client.

Le mTLS va être activé sur le serveur web NginX du VPS en ajoutant au fichier /etc/nginx/sites-enabled/default les 2 lignes :

ssl_client_certificate /etc/ssl/selfsigned/client1.crt;
ssl_verify_client on;

Ces deux lignes sont à rajouter juste après les lignes :

ssl_certificate …;
ssl_certificate_key …;

Vous noterez que le certificat de notre unique client est déclaré sur le serveur. Si l’on a x clients, on concatène les certificats des x clients dans un fichier pem unique et c’est ce fichier qu’on déclare dans la rurique ssl_client_certificate :

cat client1.crt client2.crt ... clientx.crt > clients.pem

On peut vérifier que le fichier de configuration est correct par la commande :

sudo nginx -t

Pour finir on fait prendre en compte les modifications de configuration à nginx par la commande :

sudo service nginx reload

Désormais si un client lambda (qui ne dispose pas d’un bon certificat) se connecte à notre serveur il sefait jeter avec le message suivant indiquant qu’il n’a pas présenté de certificat :

Pour pouvoir accéder au serveur en mTLS, il faut importer le fichier client1.pfx dans le navigateur du client. L’endroit où l’on fait cela dépend du navigateur utilisé (chercher la rubrique « certificats »).

Personnellement j’utilise le navigateur Brave et les certificats sont dans Paramètres / Confidentialité et sécurité / Sécurité / Gérer les certificats.

Une fois le certificat importé dans le navigateur, on peut accéder au serveur web en sélectionnant le bon certificat dans la fenêtre qui s’ouvre lors de l’accès :

Et là la magie se produit, on peut accéder au serveur en mTLS :

Sécuriser en mTLS seulement un répertoire

Comme vous pouvez le constater, le tuto ci-dessus sécurise en mTLS un serveur web complet. Si l’on veut ne sécuriser qu’un répertoire donné, il faudra modifier la ligne suivante :

ssl_verify_client optional;

Et rajouter le répertoire à sécuriser comme ceci :

location /repertoire {
if ($ssl_client_verify != "SUCCESS") { return 403; }
proxy_pass http://localhost/repertoire;
}

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *