L’objectif ici est d’installer et mettre en service un serveur HTTP à l’aide du logiciel open source Nginx, sur un serveur GNU/Linux (Debian), puis de le configurer et lui associer des certificats afin d'établir une navigation sécurisée basée sur le protocole HTTPS (HTTP + SSL/TLS).
Nous allons installer et gérer Nginx sur un serveur Debian core, en partant de notre miniLAB virtuel :
Tout d'abord, sur notre VM debian-gui (cliente), on lance une mise à jour et l'installation des outils wget, curl et dnsutils :
sudo apt update && sudo apt upgrade && sudo apt -y install curl wget dnsutils
On supprime le fichier des hôtes connus (traces d'anciens TP) :
rm ~/.ssh/known_hosts
Maintenant, occupons-nous du serveur debian-core :
ssh pascal@192.168.1.10
sudo nano /etc/hostname
sudo nano /etc/hosts
qui doit au final ressembler à ceci :
127.0.0.1 localhost
127.0.1.1 debian-lemp-ssl
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
On redémarre le serveur :
sudo reboot
Puis on s'y reconnecte en ssh depuis le poste client :
ssh pascal@192.168.1.10
Et on lance une mise à jour et l'installation quelques utilitaires :
sudo apt update && sudo apt upgrade && sudo apt -y install wget tree curl
Comme la plupart des services courants, on l’installe tout simplement avec ;
sudo apt install -y nginx
Le service Nginx démarrera automatiquement une fois le processus d'installation terminé. Vous pouvez le vérifier en exécutant la commande curl
suivante :
curl -I 127.0.0.1
Qui, par l'option -I
nous retourne l'entête de la réponse http du serveur :
HTTP/1.1 200 OK
Server: nginx/1.nn.nn
Date: Mon, XX Mmm AAAA 22:29:02 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, XX Mmm AAAA 22:27:54 GMT
Connection: keep-alive
ETag: "5b847aea-264"
Accept-Ranges: bytes
On utilise la commande systemctl
pour gérer le service Nginx, comme n'importe quelle autre unité systemd
.
sudo systemctl stop nginx
sudo systemctl start nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
sudo systemctl disable nginx
sudo systemctl enable nginx
"fullduplex.fr" est un exemple qu'il vous faut personnaliser avec un domaine ou un sous-domaine public sur lequel vous pouvez administrer la zone DNS. Dans le cadre d'un TP en classe, demandez à votre formateur de vous en fournir un. Pour une expérience solo, vous pouvez souscrire votre propre nom de domaine chez OVH par exemple.
On commence par créer le répertoire racine (root directory) du domaine fullduplex.fr
:
sudo mkdir -p /var/www/fullduplex.fr/public
Puis on y ajoute un fichier index.html
:
sudo nano /var/www/fullduplex.fr/public/index.html
Je vous propose ce contenu :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Full Duplex</title>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<style>
body {
font-family: 'Comic Sans MS', cursive, sans-serif;
background: linear-gradient(to right, #ff9a9e, #fad0c4, #fbc2eb);
color: #444;
margin: 0;
padding: 0;
}
header {
text-align: center;
padding: 20px;
background: linear-gradient(to right, #6a11cb, #2575fc);
color: white;
font-size: 1.8em;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
section {
max-width: 800px;
margin: 20px auto;
background: #ffffff;
padding: 20px;
border-radius: 15px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
text-align: center;
}
.definition {
display: none;
padding: 15px;
margin-top: 15px;
background: #e0ffe0;
border-radius: 10px;
border: 2px dashed #00b894;
box-shadow: 0 4px 8px rgba(0, 128, 0, 0.2);
}
button {
margin: 10px;
padding: 15px 30px;
background: linear-gradient(to right, #ff6a00, #ee0979);
color: white;
border: none;
border-radius: 50px;
font-size: 1.1em;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
button:hover {
transform: scale(1.1);
box-shadow: 0 6px 15px rgba(255, 105, 135, 0.6);
}
</style>
</head>
<body>
<header>
<h1>Le saviez-vous ?</h1>
</header>
<section>
<h2>Il existe trois modes de communication</h2>
<button class="toggle-button" data-target="#full-duplex">Full Duplex</button>
<div class="definition" id="full-duplex">
<p>La communication en mode <strong>full duplex</strong> permet à deux parties d'envoyer et de recevoir des données simultanément, comme lors d'une conversation téléphonique où les deux interlocuteurs peuvent parler et écouter en même temps.</p>
</div>
<button class="toggle-button" data-target="#half-duplex">Half Duplex</button>
<div class="definition" id="half-duplex">
<p>Le mode <strong>half duplex</strong> permet l'envoi et la réception de données, mais pas simultanément. Par exemple, dans un talkie-walkie, une personne parle pendant que l'autre écoute, et inversement.</p>
</div>
<button class="toggle-button" data-target="#simplex">Simplex</button>
<div class="definition" id="simplex">
<p>Le mode <strong>simplex</strong> permet la transmission des données dans une seule direction. Un exemple courant est une télévision ou une radio.</p>
</div>
</section>
<script>
$(document).ready(function () {
// Gérer l'affichage des définitions
$(".toggle-button").on("click", function () {
const target = $(this).data("target");
$(target).slideToggle();
});
});
</script>
</body>
</html>
C'est une bonne pratique que de transférer la propriété du répertoire racine du domaine à l’utilisateur attaché au serveur http, à savoir www-data
:
sudo chown -R www-data:www-data /var/www/fullduplex.fr
On crée le fichier de configuration de bloc serveur associé à notre site :
sudo nano /etc/nginx/sites-available/fullduplex.fr.conf
On ajoute cette configuration minimale :
server {
listen 80;
server_name fullduplex.fr;
root /var/www/fullduplex.fr/public;
index index.html;
access_log /var/log/nginx/fullduplex.fr.access.log;
error_log /var/log/nginx/fullduplex.fr.error.log;
location / {
try_files $uri $uri/ =404;
}
}
On active ce nouveau bloc serveur en créant un lien symbolique du fichier vers le répertoire des sites activés (sites-enabled
) :
sudo ln -s /etc/nginx/sites-available/fullduplex.fr.conf /etc/nginx/sites-enabled/
On teste la configuration pour vérifier si la syntaxe globale est correcte :
sudo nginx -t
S'il n'y a pas d'erreur, on obtient :
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
On redémarre le service nginx
pour que les modifications prennent effet :
sudo systemctl restart nginx
Sur la machine cliente, on ajoute d'abord la résolution locale du nom de notre site :
sudo nano /etc/hosts
en insérant cette ligne parmi les autres :
192.168.1.10 fullduplex.fr
Puis on vérifie que le site fonctionne, en accédant à http://fullduplex.fr
depuis le navigateur web.
Voici par exemple le rendu du code html indiqué plus haut :
Nous allons désormais créer un certificat auto-signé à l’aide de openSSL. Nous verrons ensuite comment configurer Nginx pour utiliser ce certificat.
Retour sur le serveur : on y installe tout d’abord le package openssl
(il est en fait probablement déjà installé) :
sudo apt install openssl
Dans le dossier /etc/ssl
on crée un dossier pour notre domaine :
cd /etc/ssl
sudo mkdir fullduplex.fr
cd fullduplex.fr
La première étape est de créer une paire de clés RSA assez robuste, on prendra 4096 bits (2048 étant le strict minimum) :
sudo openssl genrsa -out fullduplex.fr.key 4096
L'étape suivante est de générer un fichier de "demande de signature de certificat", en anglais CSR, basée sur cette paire de clés :
sudo openssl req -new -key fullduplex.fr.key -out fullduplex.fr.csr
Il faut répondre à un certain nombre de questions afin de fournir les informations d’identification du certificat. Il faut bien mettre le nom du serveur tel qu'il est appelé de l'extérieur dans le champ Common Name (CN).
Enfin, on génère le certificat auto-signé au format x509, valable ici 365 jours :
sudo openssl x509 -req -days 365 -in fullduplex.fr.csr -signkey fullduplex.fr.key -out fullduplex.fr.crt
réponse :
Certificate request self-signature ok
subject=C = FR, ST = Pays de la Loire, L = Le Mans, O = Full Duplex, CN = fullduplex.fr
On voit dans cette dernière commande le sens de "auto-signature" : c’est la même clé RSA (
fullduplex.fr.key
) qui a émis la demande de certificat (CSR) qui réalise la signature.
Ce qu'on appelle Diffie-Hellman (ou DH) est une méthode d'échange sécurisé de clés cryptographiques sur un canal de communication non sécurisé, basé sur la factorisation de grandes nombres premiers.
On génére ici un nouvel ensemble de paramètres DH à 2048 bits :
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Si vous le souhaitez, vous pouvez modifier la taille jusqu'à 4096 bits, mais dans ce cas, la génération peut prendre un certain temps selon la capacité de votre système.
Le répertoire
/etc/ssl/certs
contient les certificats du système (ou plutôt des liens symboliques vers ceux-ci), notamment les certificats des Autorités de Certification Racines Approuvées (root CA).
Afin de paramétrer correctement le niveau de sécurité, on va créer un snippet ssl.conf
avec des paramètres inspirés des recommandations de Mozilla, disponibles sur https://ssl-config.mozilla.org/ :
sudo nano /etc/nginx/snippets/ssl.conf
Ce snippet devra être inclus dans les configurations de sites sécurisés sur notre serveur. Voici le contenu :
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
# modern configuration
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 30s;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;add_header X-Content-Type-Options nosniff;
Il faut désormais modifier le fichier de configuration du bloc serveur pour intégrer ces nouveaux éléments de sécurité :
sudo nano /etc/nginx/sites-available/fullduplex.fr.conf
Qui devient :
server {
listen 80;
server_name fullduplex.fr;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name fullduplex.fr;
ssl_certificate /etc/ssl/fullduplex.fr/fullduplex.fr.crt;
ssl_certificate_key /etc/ssl/fullduplex.fr/fullduplex.fr.key;
include snippets/ssl.conf;
root /var/www/fullduplex.fr/public;
index index.html;
access_log /var/log/nginx/fullduplex.fr.access.log;
error_log /var/log/nginx/fullduplex.fr.error.log;
location / {
try_files $uri $uri/ =404;
}
}
Qu'est ce qui a changé ?
Testez la configuration Nginx pour vérifier si la syntaxe globale est correcte :
sudo nginx -t
Un message d’alerte (warning) non bloquant devrait apparaître concernant le ssl_stapling qui ne peut être activé avec un certificat auto-signé.
Rechargez le service Nginx pour que les modifications prennent effet :
sudo systemctl reload nginx
Puis testez de nouveau le site, avec différentes requêtes (http, https).
Vous devriez observer l’action des redirections, et obtenir l’alerte de votre navigateur concernant le danger d’un certificat auto-signé.
Vous pouvez également consulter le certificat depuis le navigateur :
Let's Encrypt est une autorité de certification ouverte et gratuite développée par Internet Security Research Group (ISRG). Aujourd'hui, presque tous les navigateurs font confiance aux certificats émis par Let's Encrypt.
Nous verrons ici comment utiliser l'outil certbot pour obtenir un certificat SSL gratuit pour Nginx sur Debian.
Comme évoqué plus haut, pour pouvoir obtenir un tel certificat, il faut disposer d'un domaine ou un sous-domaine public sur lequel vous pouvez administrer la zone DNS (et donc a priori dont on est propriétaire).
De plus, pour que le certificat puisse être correctement émis de façon automatique, il est nécessaire que le nom de domaine (ici fullduplex.fr
) soit résolu vers par l'adresse IP publique sur laquelle est accessible le serveur Nginx (champ A et/ou AAAA du DNS). Ceci demandera probablement des ouvertures de ports (NAT/PAT) sur le ou les équipements qui vous séparent du réseau public, et une mise à jour des champs DNS chez le registrar du domaine. A voir avec votre formateur au moment de lancer la création du certificat.
Il existe cependant une autre méthode manuelle lorsque le serveur ne peut pas être exposé sur le réseau public.
Certbot est un outil complet et facile à utiliser qui peut automatiser les tâches d’obtention et de renouvellement des certificats SSL Let's Encrypt et configurer les serveurs Web pour utiliser les certificats. Le paquet certbot est inclus dans les dépôts Debian 12, ce qui simplifie l'installation :
Mise à jour la liste des packages et mise à jour au besoin :
sudo apt update && sudo apt upgrade -y
Installation de certbot et du plugin spécifique pour nginx :
sudo apt install certbot python3-certbot-nginx -y
Nous pouvons maintenant utiliser certbot pour demander (et obtenir !) un certificat auprès de let’s encrypt pour notre serveur web.
Cette première méthode, appelée validation par HTTP, est entièrement automatisée. C'est celle qui doit être utilisée pour un serveur en production. Le principe est que certbot crée temporairement un fichier de validation sur le serveur web pour prouver la propriété du domaine, en provoquant un accès externe depuis les serveurs Let's Encrypt.
Mais pour cela il faut faire les réglages de DNS et de NAT/PAT évoqués juste avant. Pour que le FQDN fullduplex.fr
(ou celui que vous avez choisi) pointe bien vers votre adresse IP publique et que les messages TCP sur les ports 80 et 443 soient bien dirigés vers votre machine :
tcp/80
et tcp/443
vers le serveur nginx (192.168.1.10
)Si ces conditions sont réunies, on lance la requête entièrement automatisée :
sudo certbot --nginx -d fullduplex.fr
Un certain nombre de questions apparaissent.
Une fois le processus terminé :
sudo nginx -t
sudo systemctl reload nginx
Si tout s’est bien passé, votre site est désormais muni d’un certificat public pour une connexion sécurisée https (le cadenas en atteste) :
Il faudra probablement vider le cache du navigateur, ou utiliser un navigation privée pour obtenir le résultat escompté.
Regardons ce qu’est devenu le fichier de configuration du serveur, modifié par certbot :
sudo nano /etc/nginx/sites-available/fullduplex.fr.conf
Son contenu est :
server {
if ($host = fullduplex.fr) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name fullduplex.fr;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name fullduplex.fr;
ssl_certificate /etc/letsencrypt/live/fullduplex.fr/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/fullduplex.fr/privkey.pem; # managed by Certbot
include snippets/ssl.conf;
root /var/www/fullduplex.fr/public;
index index.html;
access_log /var/log/nginx/fullduplex.fr.access.log;
error_log /var/log/nginx/fullduplex.fr.error.log;
location / {
try_files $uri $uri/ =404;
}
}
Les certificats de Let's Encrypt sont valables 90 jours. Pour renouveler automatiquement les certificats avant leur expiration, le package certbot crée un cronjob ou un timer systemd (tâche planifiée) qui renouvelle automatiquement tout certificat 30 jours avant son expiration.
Certbot devrait avoir automatiquement configuré un timer systemd pour le renouvellement. On peut vérifier cela avec :
systemctl list-timers | grep certbot
Si nécessaire, on peut manuellement lancer un renouvellement avec :
sudo certbot renew
Des sites comme https://www.ssllabs.com/ssltest/index.html permettent de lancer une batterie de tests afin de vérifier la conformité, la robustesse de votre DNS et du serveur.
Quel score obtenez-vous ?
Voir aussi https://securityheaders.com/
Cette méthode plus simple permet d'éviter l'exposition du serveur sur une IP publique, et se prête mieux à un cas comme celui de notre miniLAB. Il faut quand même avoir la main sur la gestion de la zone DNS du domaine.
On génére le certificat manuellement :
sudo certbot certonly --manual --preferred-challenges dns -d fullduplex.fr
Il faut suivre les instructions données par le script, et notamment ce qui concerne la validation du domaine :
_acme-challenge.fullduplex.fr
With the following value:
abc123xyz456
Before continuing, verify the record is deployed.
Comme indiqué, il faut aller ajouter dans la zone DNS un champ TXT
avec le nom _acme-challenge.fullduplex.fr
et la valeur indiquée, depuis la console de gestion du domaine. Par exemple chez OVH :
Attendre la propagation DNS (il est indiqué parfois 24h, mais en général il suffit de quelques secondes).
Pour vérifier, on peut lancer une requête DNS :
dig -t txt _acme-challenge.fullduplex.fr
Une fois l’enregistrement propagé (la requête DNS a bien retourné l'enregistrement TXT demandé, visible ligne 13) :
; <<>> DiG 9.18.28-1~deb12u2-Debian <<>> -t txt _acme-challenge.fullduplex.fr
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28406
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;_acme-challenge.fullduplex.fr. IN TXT
;; ANSWER SECTION:
_acme-challenge.fullduplex.fr. 3600 IN TXT "abc123xyz456"
;; Query time: 27 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Tue Jan 07 21:06:39 CET 2025
;; MSG SIZE rcvd: 114
on retourne dans le terminal et pour continuer.
Certbot validera le domaine et générera le certificat, avec une réponse de ce type :
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/fullduplex.fr/fullchain.pem
Key is saved at: /etc/letsencrypt/live/fullduplex.fr/privkey.pem
This certificate expires on 2025-04-07.
These files will be updated when the certificate renews.
NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cette étape est également manuelle. Comme indiqué dans la réponse ci-dessus, les fichiers générés par l'opération sont stockés par défaut dans /etc/letsencrypt/live/fullduplex.fr/
. On y trouve :
fullchain.pem
: Le certificat complet (chaîne complète).privkey.pem
: La clé privée.Il faut modifier le fichier de configuration de notre bloc serveur :
sudo nano /etc/nginx/sites-available/fullduplex.fr.conf
En insérant le certificat et la clé (lignes 10 et 11) :
server {
listen 80;
server_name fullduplex.fr;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name fullduplex.fr;
ssl_certificate /etc/letsencrypt/live/fullduplex.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/fullduplex.fr/privkey.pem;
include snippets/ssl.conf;
root /var/www/fullduplex.fr/public;
index index.html;
access_log /var/log/nginx/fullduplex.fr.access.log;
error_log /var/log/nginx/fullduplex.fr.error.log;
location / {
try_files $uri $uri/ =404;
}
}
On teste la configuration :
sudo nginx -t
et on recharge nginx pour appliquer les modifications :
sudo systemctl reload nginx
Si tout s’est bien passé, votre site est désormais muni d’un certificat public pour une connexion sécurisée https :
Il faudra probablement vider le cache du navigateur, ou utiliser un navigation privée pour obtenir le résultat escompté.
Comme indiqué aussi dans la réponse de certbot ci-dessus, le certificat généré avec la méthode DNS manuelle n'est pas renouvelé automatiquement. Il faut donc relancer cette procédure tous les 90 jours. C'est pour cela que cette méhode n'est utilisable que dans le cadre d'un test.
Disposer d’un certificat SSL pour un site web est incontournable de nos jours. Conjointement à des paramètres de configuration du serveur corrects et « affûtés », il sécurise votre site et améliore sensiblement son référencement naturel.
Vous venez de mener à bien les opérations nécessaires pour mettre en œuvre un tel serveur sécurisé. Cet environnement peut bien sûr intégrer des services complémentaires (PHP, MySQL notamment) afin d’en faire un serveur complet et sécurisé. Les différents TP réalisés sur ce sujet restent applicables ici.