Configuration du serveur web
Dans cette partie nous installerons et configurerons tous les outils nécessaire au fonctionnement d'un serveur web. En particulier on utilisera Apache, PHP et MariaDB.
Étape 1 : Apache
On commence par installer le paquet apache2
.
sudo apt install apache2
Paquets suggérés :
apache2-doc apache2-suexec-pristine | apache2-suexec-custom www-browser gdbm-l10n perl-doc libterm-readline-gnu-perl | libterm-readline-perl-perl make libtap-harness-archive-perl
Paquets recommandés :
ssl-cert libldap-common publicsuffix libsasl2-modules netbase
Les NOUVEAUX paquets suivants seront installés :
apache2 apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libcurl4 libgdbm-compat4 libgdbm6 libicu72 libldap-2.5-0 liblua5.3-0 libnghttp2-14 libperl5.36
libpsl5 librtmp1 libsasl2-2 libsasl2-modules-db libsqlite3-0 libssh2-1 libxml2 media-types perl perl-modules-5.36
Apache fournit un certain nombre de module (voir la liste ici) que l'on peut activer avec la commande a2enmod
et désactivé avec la commande a2dismod
. Pour voir la liste des modules activés on utilise la commande
sudo apache2ctl -M
Loaded Modules:
core_module (static)
so_module (static)
watchdog_module (static)
http_module (static)
log_config_module (static)
logio_module (static)
version_module (static)
unixd_module (static)
access_compat_module (shared)
alias_module (shared)
auth_basic_module (shared)
authn_core_module (shared)
authn_file_module (shared)
authz_core_module (shared)
authz_host_module (shared)
authz_user_module (shared)
autoindex_module (shared)
deflate_module (shared)
dir_module (shared)
env_module (shared)
filter_module (shared)
mime_module (shared)
mpm_event_module (shared)
negotiation_module (shared)
reqtimeout_module (shared)
setenvif_module (shared)
status_module (shared)
Parmi les modules il y a le module MPM (Modules multi-processus) qui a un rôle particulier. Il est responsables de l'association des ports réseau de la machine, accepte les requêtes, et se charge de répartir ces dernières entre les différents processus enfants. Il y a trois MPM différents pour Apache2 : prefork
, worker
et event
.
prefork
: est le plus simple des MPM. Il crée plusieurs processus enfants, chacun gérant une seule requête à la fois. Ce MPM est facile à comprendre et à configurer, et constitue un bon choix pour les sites Web simples à faible trafic ou nécessitant une compatibilité et une stabilité maximales.worker
: est similaire àprefork
, mais il utilise plusieurs threads de travail au lieu de processus séparés. Ce MPM est plus efficace que le MPM Prefork, car il peut gérer plusieurs requêtes avec un seul processus. Il est conçu pour les sites Web à fort trafic, et il est particulièrement bien adapté aux sites Web qui nécessitent des performances élevées.event
: est le plus récent des MPM (introduit dans Apache 2.4). Il est assez similaire au worker MPM mais il est conçu pour gérer des charges plus élevées. Il est conçu pour permettre le traitement d'un nombre accru de requêtes simultanées en déléguant certaines tâches aux threads d'écoute, libérant par là-même les threads de travail et leur permettant de traiter les nouvelles requêtes. C'est le choix par défaut dans Debian 12.
Pour plus de détails voir la documentation officielle.
On garde ici le MPM event
. On peut s'assurer d'utiliser le bon MPM avec la commande
sudo a2query -M
On peut désactiver tous les modules qui ne nous sont pas utiles. Par exemple on décide de désactiver access_compat_module, autoindex et status. On en profite également pour désactiver le site 000-default
générer par défaut lors de l'installation de Apache.
sudo a2dismod access_compat
sudo a2dismod -f autoindex
sudo a2dismod status
sudo a2dissite 000-default
On active maintenant les modules suivants :
headers
(doc) : fournit des directives permettant de contrôler et modifier les en-têtes de requêtes et de réponses HTTP.http2
(doc) : ajoute le support de HTTP/2 au serveur HTTP Apache.ssl
(doc) : fournit le support TLS v1 au serveur HTTP Apache (pour le support de https).
sudo a2enmod headers
sudo a2enmod http2
sudo a2enmod ssl
sudo systemctl restart apache2
On peut maintenant configurer Apache et ses différents modules. On commence par supprimer le contenu de /var/www
et interdire l'accès à tous les répertoires en commentant les blocs <Directory /usr/share>
et <Directory /var/www/>
. On ajoute également ServerName localhost
.
sudo rm -R /var/www
sudo nano /etc/apache2/apache2.conf
[...]
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
#<Directory /usr/share>
# AllowOverride None
# Require all granted
#</Directory>
#<Directory /var/www/>
# Options Indexes FollowSymLinks
# AllowOverride None
# Require all granted
#</Directory>
ServerName localhost
[...]
On minimise les données affichées sur la configuration du serveur en éditant les directives ServerTokens, ServerSignature et TraceEnable.
sudo nano /etc/apache2/conf-enabled/security.conf
ServerTokens Prod
ServerSignature Off
TraceEnable Off
On configure Apache pour utiliser exclusivement le protocole HTTP/2
sudo nano /etc/apache2/mods-enabled/http2.conf
Protocols h2
On s'intéresse maintenant au protocole TLS. Pour cela on va suivre les recommandations de sécurité relatives à TLS de l'ANSSI. On pourra aussi prendre pour référence ce guide et le générateur de configuration SSL de Mozilla. Pour plus de détails voir la documentation officielle.
Il faut alors ajouter ou modifier les valeurs suivantes dans le fichier /etc/apache2/mods-enabled/ssl.conf
.
sudo nano /etc/apache2/mods-enabled/ssl.conf
[...]
SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
SSLHonorCipherOrder off
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2
SSLSessionTickets off
On décide d'utiliser uniquement le protocole TLS en version 1.3 et on ne garde que les trois algorithmes de chiffrement supposés les plus fort.
On passe maintenant à la configuration du module header
. Il permet de contrôler et modifier les en-têtes de requêtes et de réponses HTTP. Pour plus de détails sur le protocole HTTP voir ici. Pour améliorer la sécurité on se référera à la référence précédente et aux recommandations pour la mise en œuvre d'un site web de l'ANSSI. Voici les en-têtes que l'on va configurer
Strict-Transport-Security
: Pour éviter de perdre du trafic, les sites web acceptent souvent des connexions en HTTP et les redirigent vers HTTPS, laissant ainsi à un attaquant l’opportunité d’intercepter cette communication. L’utilisation de HTTP Strict Transport Security indique au navigateur d’utiliser automatiquement HTTPS pour tous les accès au site web. Il empêche également un utilisateur d’accepter de poursuivre la navigation sur un site non sécurisé en outrepassant les alertes de sécurité (certificat invalide, certificat généré par une autorité non reconnue, etc.) levées par les navigateurs.Content-Security-Policy
: permet aux administrateurs d'un site web de contrôler les ressources que l'agent utilisateur est autorisé à charger pour une page donnée. Implémenté par tous les navigateurs modernes, CSP est à mettre en place lors de l’intégration d’un site web. Il s’agit d’une contre-mesure très efficace contre le XSS qui vient en complément des bonnes pratiques de conception et de développement. CSP est une mesure de défense en profondeur forte, mais elle ne se substitue pas à la correction des vulnérabilités identifiées dans l’application web. Pour avoir une liste assez exhaustive des directives CSP se réferer ici.Feature-Policy
: fournit un mécanisme pour permettre ou interdire l'utilisation de fonctionnalités du navigateur pour le document courant et le contenu que ce dernier embarquerait via des éléments iframe. Cet en-tête a maintenant été renomméPermissions-Policy
dans la spécificationReferrer-Policy
: Pour chaque requête émise, le navigateur positionne automatiquement l’en-tête Referer, qui indique l’URL de la ressource depuis laquelle l’URL cible a été obtenue. Cela a lieu lors de la navigation d’une page web à une autre (ex. : clic sur un lien), mais aussi lors du chargement de ressources (ex. : images, scripts, etc.) depuis une page web existante. Il peut être utile pour un site web de récupérer l’URL de la page web précédemment visitée pour l’analyse statistique, l’optimisation du cache, la journalisation et d’autres raisons légitimes. La spécification présente plusieurs options de configuration :no-referrer
: Aucune information n’est transmise via l’en-tête Referer.no-referrer-when-downgrade
: L’URL complète est utilisée en tant que Referer sauf dans le cas du passage de HTTPS à HTTP. Il s’agit du comportement par défaut du standard en l’absence de Referrer-Policy.origin
: L’Origin est utilisée en tant que Referer et non l’URL complète.same-origin
: Aucune information n’est transmise via l’en-tête Referer, sauf dans le cas d’accès au site courant où l’URL complète est utilisée.strict-origin
: L’Origin est utilisée en tant que Referer uniquement lorsque la communication s’effectue vers une destination de sécurité au moins équivalente ou supérieure : HTTP vers HTTP, HTTP vers HTTPS ou HTTPS vers HTTPS.origin-when-cross-origin
: L’URL complète est utilisée en tant que Referer pour le site courant, l’Origin est utilisée pour les autres cas.strict-origin-when-cross-origin
: L’URL complète est utilisée en tant que Referer pour le site courant, l’Origin est utilisée pour les autres cas à la condition que la sécurité de la destination soit au moins équivalente ou supérieure (cf. strict-origin). Dans le cas contraire aucun Referer ne sera transmis. Remarque : il s’agit du comportement par défaut dans certaines implémentations navigateur.unsafe-url
: L’URL complète est utilisée en tant que Referer, y compris lors de la navigation d’une page HTTPS vers une page HTTP. Ne doit pas être utilisée.
X-Frame-Options
: Le détournement de clic, ou clickjacking, est un type d’attaque dans lequel une page web trompeuse incite un utilisateur légitime à cliquer sur du contenu en apparence légitime qui le mène en réalité à effectuer des actions, à son insu, sur d’autres sites. Ces attaques sont en général mises en œuvre au moyen d’une page piégée incluant des cadres (iframes) invisibles qui pointent vers des sites légitimes sur lesquels l’utilisateur piégé a ouvert une session. La protection contre ce type d’attaques consiste en la mise en place de mécanismes qui interdisent au navigateur l’inclusion du site à protéger dans un site tiers au travers d’une frame ou iframe. Pour des raisons de compatibilité avec certains navigateurs, il est aussi possible de recourir à l’en-tête non standard X-Frame-Options, rendu obsolète par CSP, qui permet de préciser la stratégie d’affichage des contenus du site.X-Frame-Options: deny
est équivalent àframe-ancestors 'none'
.X-Content-Type-Options
: permet d'indiquer que les types MIME annoncés dans les en-têtesContent-Type
ne doivent pas être modifiés et suivis.X-Powered-By
: L'en-tête X-Powered-By décrit les technologies utilisées par le serveur Web. Ces informations exposent le serveur aux attaquants. En utilisant les informations contenues dans cet en-tête, les attaquants peuvent trouver plus facilement les vulnérabilités.X-XSS-Protection
: L’en-tête non-standard X-XSS-Protection n’est plus préconisé pour se prémunir des vulnérabilités XSS. Cet en-tête permettait la configuration d’une heuristique de filtrage XSS implémentée par les navigateurs. L’augmentation de la surface d’attaque entraînée par l’implémentation de ce filtre, l’introduction de nouvelles vulnérabilités et l’existence de contournements font que le support de ce mécanisme est en cours de retrait par certains navigateurs et a déjà été retiré par d’autres. La mesure de sécurité standardisée à mettre en œuvre est Content Security Policy, qui offre une protection supérieure contre ce type d’attaques. L’usage de X-XSS-Protection ne présente d’intérêt que pour les navigateurs anciens qui ne supporteraient pas CSP.Expect-CT
: L'en-tête Expect-CT a été conçu pour permettre aux sites Web d'accepter l'application de la transparence des certificats avant qu'elle ne soit appliquée par défaut. Cependant, à partir de mai 2018, les nouveaux certificats devraient prendre en charge les SCT par défaut, ce qui rend l'en-tête Expect-CT obsolète. Cette en-tête n'est donc pas utile.Proxy
:httpoxy
est un ensemble de vulnérabilités qui affectent le code d'application s'exécutant dans des environnements CGI ou de type CGI. Cela conduit à une vulnérabilité exploitable à distance. Si vous utilisez PHP ou CGI, vous devez bloquer l'en-tête Proxy. Pour plus de détails voir ici.
sudo nano /etc/apache2/mods-enabled/headers.conf
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header set Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none'; frame-ancestors 'none';"
Header set Feature-Policy "camera 'none'; geolocation 'none'; microphone 'none';"
Header always set Referrer-Policy "same-origin"
Header always set X-Frame-Options "deny"
Header always set X-Content-Type-Options "nosniff"
Header always unset X-Powered-By
#Header always set X-XSS-Protection "0"
RequestHeader unset Proxy early
Pour vérifier les en-têtes envoyer par un site web on peut utiliser la commande curl -I
. Par exemple
curl -I http://gnu.le-site-alice.fr
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
referrer-policy: same-origin
x-frame-options: deny
x-content-type-options: nosniff
last-modified: Fri, 16 Sep 2022 17:17:00 GMT
etag: "32a1-905164a0ae2eb"
accept-ranges: bytes
content-length: 9408
vary: Accept-Encoding
content-security-policy: default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none'; frame-ancestors 'none';
feature-policy: camera 'none'; geolocation 'none'; microphone 'none';
content-type: text/html
date: Wed, 16 Aug 2023 15:05:37 GMT
server: Apache
On peut vérifier que la configuration est correcte (au niveau de la syntaxe) avec la commande apachectl configtest
puis redémarrer le service apache
.
sudo apachectl configtest
sudo systemctl restart apache2
Étape 2 : Certificats Let's encrypt
Dans cette partie on configure le serveur pour l'associer à un nom de domaine et à un certificat qui permettra d'utiliser le protocole TLS (et donc avoir une adresse en https
). Cela permettra d'authentifier le serveur et d'assurer la confidentialité et l'intégrité des données échangées grâce au chiffrement.
Pour cela il faut tout d'abord associer notre nom de domaine (ou un sous domaine) à l'adresse IP de notre serveur en configurant un serveur DNS. Ici on prendra directement le serveur DNS du registraire à qui on loue le nom de domaine. Disons que notre nom de domaine est le-site-alice.fr
et qu'on veuille utiliser les sous-domaines gnu.le-site-alice.fr
et tux.le-site-alice.fr
. Il faut alors ajouter la ligne suivante dans le fichier de configuration DNS. Pour plus d'information voir ici.
gnu IN A 10.11.12.13
tux IN A 10.11.12.13
On créer ensuite les répertoires des deux sites.
sudo mkdir /home/www/gnu
sudo mkdir /home/www/tux
sudo chown -R aliceweb:www-data /home/www/gnu
sudo chown -R aliceweb:www-data /home/www/tux
Avant de pouvoir mettre en place la connection sécurisée aux sites avec https
on doit avoir un accès au site sur le port 80 (en http
) pour pouvoir effectuer le challenge HTTP-01. On configure donc les VirtualHost
pour chaque site.
sudo nano /etc/apache2/sites-available/gnu.le-site-alice.fr.conf
<VirtualHost *:80>
ServerName gnu.le-site-alice.fr
DocumentRoot "/home/www/gnu"
<Directory "/home/www/gnu">
Require all granted
</Directory>
</VirtualHost>
sudo a2ensite gnu.le-site-alice.fr
sudo nano /etc/apache2/sites-available/tux.le-site-alice.fr.conf
<VirtualHost *:80>
ServerName tux.le-site-alice.fr
DocumentRoot "/home/www/tux"
<Directory "/home/www/tux">
Require all granted
</Directory>
</VirtualHost>
sudo a2ensite tux.le-site-alice.fr
sudo systemctl reload apache2
On utilisera l'autorité de certification Let’s Encrypt pour générer les certificats de nos sites. Pour tous les détails sur son fonctionnement on peut se référer à cette page.
Ce service utilise le protocole ACME
pour générer automatiquement un certificat. On commence donc par installer un client ACME. On peut avoir une liste assez complète de tous les client ACME ici. On choisit le logiciel uacme
(voir le GitHub). C'est un logiciel assez minimaliste codé en C qui a l'avantage d'être déjà présent dans les dépôts de Debian 12.
sudo apt install uacme
Les NOUVEAUX paquets suivants seront installés :
libcurl3-gnutls libev4 uacme
Ce logiciel ne nécessitant pas d'être exécuter avec les droits administrateurs on va créer un utilisateur dédié à son utilisation.
sudo adduser acme
On créer le répertoire /etc/letsencrypt
qui sera utilisé par acme
pour générer les clefs et certificat
sudo mkdir /etc/letsencrypt
sudo chown acme:acme /etc/letsencrypt
et on créer le répertoire nécessaire à la résolution du challenge sur chaque site. On lui donne les bons droits.
sudo mkdir /home/www/gnu/.well-known
sudo chown -R acme:www-data /home/www/gnu/.well-known
sudo mkdir /home/www/tux/.well-known
sudo chown -R acme:www-data /home/www/tux/.well-known
On passe sur le compte acme
et on créer un compte sur Let's encrypt avec la commande uacme new
. La clef privée de ce compte sera dans le fichier /etc/letsencrypt/private/key.pem
.
su acme
uacme -v -y -c /etc/letsencrypt new
uacme: created directory /etc/letsencrypt/private
uacme: loading key from /etc/letsencrypt/private/key.pem
uacme: /etc/letsencrypt/private/key.pem not found
uacme: generating new 2048-bit RSA key
uacme: key saved to /etc/letsencrypt/private/key.pem
uacme: fetching directory at https://acme-v02.api.letsencrypt.org/directory
uacme: creating new account at https://acme-v02.api.letsencrypt.org/acme/new-acct
uacme: terms at https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf autoaccepted (-y)
uacme: account created at https://acme-v02.api.letsencrypt.org/acme/acct/1254539256
Pour générer le certificat d'un site on doit prouver que l'on contrôle bien le nom de domaine en résolvant un challenge. On va utiliser le challenge HTTP-01 qui consiste à déposer un certain code fourni par l'autorité de certification à une certaine adresse. La certification se fait en 4 étapes :
- on fait une demande de certification à Let's encrypt ;
- Let's encrypt nous demande de résoudre le Challenge avec certains codes ;
- on crée la page nécessaire sur notre site pour résoudre le challenge ;
- Let's encrypt confirme la bonne résolution du challenge en nous fournissant le certificat.
Pour faire une demande de certification on utilise la commande uacme issue
. Pour automatiser la demande de certificat, on créer le script suivant qui sera donné en paramètre à uacme
et qui permettra de faire les opérations nécessaire à la résolution du challenge dans l'étape 3.
mkdir /etc/letsencrypt/scripts
nano /etc/letsencrypt/scripts/gnu.sh
#!/bin/bash
SITE_PATH=/home/www/gnu
METHOD=$1
TYPE=$2
IDENT=$3
TOKEN=$4
AUTH=$5
if [ "$METHOD" = "begin" ]
then
mkdir -p "$SITE_PATH"/.well-known/acme-challenge
echo "$AUTH" > "$SITE_PATH"/.well-known/acme-challenge/"$TOKEN"
exit $?
elif [ "$METHOD" = "done" ] || [ "$METHOD" = "failed" ]
then
rm -R "$SITE_PATH"/.well-known/acme-challenge
exit $?
else
exit 1
fi
chmod +x /etc/letsencrypt/scripts/gnu.sh
Le script est appelé à l'étape 3 avec la variable $METHOD
valant begin
. On créer alors le fichier du nom de la valeur $TOKEN
et contenant la valeur de $AUTH
pour résoudre le challenge. Ensuite le script est de nouveau appelé à la toute fin avec la variable $METHOD
valant done
(si tout s'est bien passé) ce qui nous permet de nettoyer le fichier ajouté pour le challenge.
On peut alors générer le certificat avec la commande
uacme -v -c /etc/letsencrypt -t EC -h /etc/letsencrypt/scripts/gnu.sh issue gnu.le-site-alice.fr
On choisit ici d'utiliser l'algorithme de chiffrement asymétrique ECDSA en 256 bits sur la courbe prime256v1
au lieu d'utiliser RSA en 2048 bits ou 4096 bits.
Cela génère deux fichiers : /etc/letsencrypt/gnu.le-site-alice.fr/cert.pem
qui est le certificat fourni par Let's encrypt et /etc/letsencrypt/private/gnu.le-site-alice.fr/key.pem
qui est la clef privée qui permet de générer les signatures numériques.
Il faut maintenant faire la même chose pour le site tux.le-site-alice.fr
nano /etc/letsencrypt/scripts/tux.sh
#!/bin/bash
SITE_PATH=/home/www/tux
METHOD=$1
TYPE=$2
IDENT=$3
TOKEN=$4
AUTH=$5
if [ "$METHOD" = "begin" ]
then
mkdir -p "$SITE_PATH"/.well-known/acme-challenge
echo "$AUTH" > "$SITE_PATH"/.well-known/acme-challenge/"$TOKEN"
exit $?
elif [ "$METHOD" = "done" ] || [ "$METHOD" = "failed" ]
then
rm -R "$SITE_PATH"/.well-known/acme-challenge
exit $?
else
exit 1
fi
chmod +x /etc/letsencrypt/scripts/tux.sh
uacme -v -c /etc/letsencrypt -t EC -h /etc/letsencrypt/scripts/tux.sh issue tux.le-site-alice.fr
Pour simplifier le renouvellement des certificat on va écrire un script permettant de renouveler les certificats de tous les sites.
nano /etc/letsencrypt/renew_cert.sh
#!/bin/bash
uacme -v -c /etc/letsencrypt -t EC -h /etc/letsencrypt/scripts/gnu.sh issue gnu.le-site-alice.fr
uacme -v -c /etc/letsencrypt -t EC -h /etc/letsencrypt/scripts/tux.sh issue tux.le-site-alice.fr
chmod +x /etc/letsencrypt/renew_cert.sh
On peut alors se déconnecter du compte acme
et le verrouiller.
exit
sudo usermod -L -s /usr/sbin/nologin acme
Chaque certificat Let's encrypt a une durée de validité de 90 jours. On va donc automatiser le renouvellement des certificats. Pour cela on crée un service et un timer systemd pour chaque site. On décide de renouveler les certificats tous les lundis à 5h (dans un intervalle de 1h). Par défaut uacme
ne renouvellera le certificat que s'il arrive à expiration dans moins de 30 jours.
sudo nano /root/renew_cert.service
[Unit]
Description=Renew Lets encrypt cert
After=network.target
[Service]
Type=oneshot
ExecStart=/etc/letsencrypt/renew_cert.sh
User=acme
SuccessExitStatus=0 1
[Install]
WantedBy=multi-user.target
sudo nano /root/renew_cert.timer
[Unit]
Description=Renew Lets encrypt cert
[Timer]
OnCalendar=Mon *-*-* 5:00:00
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
On peut maintenant activer le service et le timer et les tester
sudo systemctl enable /root/renew_cert.service
sudo systemctl enable /root/renew_cert.timer
sudo systemctl start renew_cert.service
sudo journalctl -u renew_cert.service
sudo systemctl start renew_cert.timer
sudo systemctl status renew_cert.timer
sudo systemctl list-timers
On peut également, pour vérification, afficher le certificat avec la commande
sudo openssl x509 -in /etc/letsencrypt/gnu.le-site-alice.fr/cert.pem -noout -text
On finit en configurant Apache. On commence par activer l'agrafage OCSP. Le protocole OCSP permet de vérifier la validité d'un certificat numérique TLS en temps-réel auprès de l'autorité ayant émis le certificat. L’agrafage OCSP consiste à attacher (agrafer) à l’échange initial TLS, une réponse OCSP horodatée et signée par l’Autorité de certification (CA), permettant d'économiser au client TLS la vérification auprès de la CA. Pours plus de détails voir ici et là.
Pour cela on ajoute à la toute fin du fichier /etc/apache2/mods-enabled/ssl.conf
les directives SSLUseStapling et SSLStaplingCache
sudo nano /etc/apache2/mods-enabled/ssl.conf
[...]
SSLUseStapling On
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
On configure maintenant les VirtualHost
. Par chacun des sites
- On autorise la connection au port 80 (http) à l'adresse
*.le-site-alice.fr/.well-known/acme-challenge/*
pour le résolution du challenge ACME nécessaire au renouvellement du certificat. - On redirige toutes les autres requêtes sur le port 80 (adresse en
http
) vers la même adresse enhttps
. - On autorise toutes les requêtes sur le port 443 et on active la connection chiffrée avec
SSLEngine on
et on indique l'emplacement du certificat et de la clef privée. - Pour séparer les logs de chaque site on ajoute les directives
ErrorLog
etCustomLog
.
sudo rm /etc/apache2/sites-available/gnu.le-site-alice.fr.conf
sudo nano /etc/apache2/sites-available/gnu.le-site-alice.fr.conf
<VirtualHost *:80>
ServerName gnu.le-site-alice.fr
DocumentRoot "/home/www/gnu"
<Directory "/home/www/gnu/.well-known/acme-challenge">
Require all granted
</Directory>
RedirectMatch permanent "^(/(?!.well-known/acme-challenge/).*)" "https://gnu.le-site-alice.fr$1"
</VirtualHost>
<VirtualHost *:443>
ServerName gnu.le-site-alice.fr
DocumentRoot "/home/www/gnu"
<Directory "/home/www/gnu">
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/gnu.le-site-alice.fr/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/private/gnu.le-site-alice.fr/key.pem
ErrorLog /var/log/apache2/error.gnu.le-site-alice.fr.log
CustomLog /var/log/apache2/access.gnu.le-site-alice.fr.log combined
</VirtualHost>
sudo rm /etc/apache2/sites-available/tux.le-site-alice.fr.conf
sudo nano /etc/apache2/sites-available/tux.le-site-alice.fr.conf
<VirtualHost *:80>
ServerName tux.le-site-alice.fr
DocumentRoot "/home/www/tux"
<Directory "/home/www/tux/.well-known/acme-challenge">
Require all granted
</Directory>
RedirectMatch permanent "^(/(?!.well-known/acme-challenge/).*)" "https://tux.le-site-alice.fr$1"
</VirtualHost>
<VirtualHost *:443>
ServerName tux.le-site-alice.fr
DocumentRoot "/home/www/tux"
<Directory "/home/www/tux">
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/tux.le-site-alice.fr/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/private/tux.le-site-alice.fr/key.pem
ErrorLog /var/log/apache2/error.tux.le-site-alice.fr.log
CustomLog /var/log/apache2/access.tux.le-site-alice.fr.log combined
</VirtualHost>
sudo systemctl reload apache2
Les deux sites web d'Alice sont maintenant accessibles en https. Pour tester on peut monter le repertoire dédié aux sites web sur la machine locale avec la commande
knock -d 100 10.11.12.13 1111:tcp 2222:udp 3333:tcp 4444:udp && sshfs aliceweb@10.11.12.13:/data /home/alice/pointDeMontage -p 32 -o IdentityFile=~/.ssh/webalice/aliceweb-ed25519
puis déposer une page web statique dans l'un des deux répertoires gnu
et tux
.
Étape 3 : PHP
Dans le cas où l'on souhaite un site web dynamique, par exemple pour pouvoir utiliser des formulaires, on doit utiliser un language de programmation pour produire dynamiquement les pages du site. Le language le plus utilisé pour cela est PHP mais on pourrait utiliser tout autre language interprété comme par exemple Python. Nous faisons ici le choix d'utiliser PHP.
Il existe plusieurs modes d'exécution de PHP sur un serveur Apache :
- Le module Apache (le paquet
libapache2-mod-php8.2
sous Debian). Cette méthode ne fonctionne que pour le MPMprefork
. Lors de l'utilisation de ce module, l'interpréteur PHP est intégré dans chaque processus Apache (et on rappelle qu'avec le MPMprefork
, une requête engendre un processus). De cette façon, chaque processus Apache est capable de gérer et d'exécuter lui-même des scripts PHP, éliminant ainsi le besoin de traiter avec des processus externes ; contrairement à CGI ou FastCGI. Cela le rend très utile pour les sites "lourds en PHP" où de nombreuses requêtes sont susceptibles de contenir du code PHP (comme WordPress, Drupal, Joomla, etc.) car toutes les requêtes peuvent être gérées par Apache. L'inconvénient est que l'empreinte de chaque processus Apache est plus importante car il nécessite plus de ressources système. Même lors de la diffusion de contenu statique tel que des images, du texte et des feuilles de style où aucun code PHP n'a besoin d'être exécuté, le processus contient toujours l'interpréteur PHP. - L'utilisation de l'interface CGI est la manière traditionnelle d'exécuter des applications sur un serveur Web. Cependant elle est très inefficace et rarement utilisée. Elle a été introduite à l'origine dans les années 1990, mais a été jugé trop inefficace pour être utilisée sur autre chose que de très petits sites. L'exécution d'applications sur CGI présente l'avantage de séparer l'exécution du code du serveur Web, ce qui offre des avantages supplémentaires en matière de sécurité. Par exemple, un script PHP bogué ou non sécurisé exécuté via CGI ne peut pas corrompre ou affecter la sécurité d'autres fichiers en dehors du domaine sur lequel il est hébergé. Cela signifie également que l'interpréteur PHP n'est appelé qu'en cas de besoin, permettant ainsi au contenu statique d'être servi uniquement par le serveur Web. L'inefficacités de l'exécution de PHP avec le support CGI résulte de la nécessité de créer un nouveau processus chaque fois qu'un code PHP doit être exécuté. Cela peut être très gourmand en ressources.
- FastCGI a été introduit comme un compromis entre le module PHP Apache et l'application CGI. Il permet aux scripts d'être exécutés par un interpréteur en dehors du serveur Web et inclut les avantages de sécurité de CGI mais n'inclut aucune des inefficacités de CGI. Lors de l'exécution de scripts PHP avec FastCGI, chaque requête est transmise du serveur Web à FastCGI via un socket de communication. Cela permet une évolutivité beaucoup plus grande car le serveur Web et l'interpréteur PHP peuvent être divisés en leurs propres environnements de serveur individuels si nécessaire.
- PHP-FPM (PHP-FastCGI Process Manager) est une implémentation FastCGI alternative pour PHP dans un serveur Web. Ici, nous avons toujours (au moins) un processus PHP-FPM parallèle au processus du serveur Web qui gère l'interprétation PHP. Les processus FPM se regroupent en différents "pools". Dans ces pools, il y aura généralement plusieurs processus créés qui gèrent l'interprétation PHP pour une page Web spécifique.
Nous utiliserons PHP-FPM
. On commence par installer le paquet php-fpm
.
sudo apt install php-fpm
Paquets suggérés :
file php-pear
Les NOUVEAUX paquets suivants seront installés :
libmagic-mgc libmagic1 libsodium23 php-common php-fpm php8.2-cli php8.2-common php8.2-fpm php8.2-opcache php8.2-readline psmisc
Il faut également activer le module proxy_fcgi
et la configuration php8.2-fpm
pour permettre le serveur Apache et l'interpréteur PHP de communiquer via PHP-FPM
.
sudo a2enmod proxy_fcgi
sudo a2enconf php8.2-fpm
On peut alors configurer PHP en éditant les fichiers suivants : /etc/php/8.2/fpm/pool.d/www.conf
et /etc/php/8.2/fpm/php.ini
.
Dnas le fichier /etc/php/8.2/fpm/pool.d/www.conf
on configure les paramètres des pools
. Pour plus d'informations voir la documentation.
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
[www]
user = aliceweb
group = aliceweb
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = ondemand
pm.max_children = 32
pm.process_idle_timeout = 10s
On modifie les valeur de user
et group
car on souhaite que l'utilisateur propriétaire des processus PHP soit aliceweb
. On peut également modifier le paramètre pm
pour améliorer les performances du serveur PHP.
- En mettant
pm = static
le serveur gardera en permanance un nombre fixe de processus PHP (pools). C'est la solution la plus performante mais la moins flexible car il faut connaitre une bonne estimation de la fréquentation des sites sur le serveur. - En mettant
pm = dynamic
on peut déterminer le nombre minimum et le maximum de processus PHP. C'est une solution pertinente pour des sites à traffic moyen qui flucte legerment. - En mettant
pm = ondemand
aucun processus n'est créer automatiquement au démarrage mais ils vont se créer et détruire en fonction des requêtes. C'est en théorie la solution la moins performante mais en pratique elle est très acceptable pour les sites à faible traffic ou à grosse varaiation de traffic. C'est aussi la solution la plus économe en ressource car seul les processus utilisés sont créés.
Pour plus d'information sur ces trois possibilités voir ici ou pour avoir une étude assez complète (avec un benchmark) voir là.
On a fait le choix ici d'utiliser pm = ondemand
avec pm.max_children = 32
pour un serveur de 4Go de RAM.
On passe alors au fichier /etc/php/8.2/fpm/php.ini
de configuration général de PHP. Voir la documentation pour plus de détails. C'est un fichier imposant donc on indiquera ici uniquement les modifications effectuées par rapport au fichier fourni par défaut par Debian. Toutes les options y sont très bien documentées et par défaut ces options sont mises sur des valeurs pour un serveur en production.
sudo nano /etc/php/8.2/fpm/php.ini
;;;;;;;;;;;;;;;;;;;;
; Language Options ;
;;;;;;;;;;;;;;;;;;;;
open_basedir = /home/www/
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
;;;;;;;;;;;;;;;;;;;
; Module Settings ;
;;;;;;;;;;;;;;;;;;;
[Date]
date.timezone = Europe/Paris
[opcache]
opcache.enable = 1
On finit en redémarrant le service PHP et en relançant le serveur apache.
sudo systemctl restart php8.2-fpm
sudo systemctl reload apache2
Pour avoir une vue assez détaillée des paramètres prise en compte par PHP, on peut créer un fichier PHP contenant <?php phpinfo(); ?>
. Attention cependant les informations contenues sur cette page sont sensibles et ne doivent pas être publiques.
Étape 4 : MariaDB
On termine par l'installation de MariaDB, un système de gestion de base de données qui est une alternative libre à MySQL. On commence par installer le paquet mariadb-server
.
sudo apt install mariadb-server
Paquets suggérés :
gawk-doc libclone-perl libmldbm-perl libnet-daemon-perl libsql-statement-perl mailx mariadb-test netcat-openbsd readline-doc python3 python3-braceexpand
Paquets recommandés :
libgpm2 libdbd-mariadb-perl | libdbd-mysql-perl libterm-readkey-perl libhtml-template-perl mariadb-plugin-provider-bzip2 mariadb-plugin-provider-lz4 mariadb-plugin-provider-lzma mariadb-plugin-provider-lzo mariadb-plugin-provider-snappy pv
Les NOUVEAUX paquets suivants seront installés :
galera-4 gawk libconfig-inifiles-perl libdaxctl1 libdbi-perl libmariadb3 libmpfr6 libncurses6 libndctl6 libnuma1 libpmem1 libreadline8 libsigsegv2 liburing2 lsof mariadb-client mariadb-client-core mariadb-common mariadb-server mariadb-server-core mysql-common readline-common rsync socat
Pour configurer MariaDB il suffit de lancer la commande mysql_secure_installation
et de répondre oui (Y) à toutes les questions. Pour la première question il ne faut rien mettre et faire juste entrée.
sudo mysql_secure_installation
NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!
In order to log into MariaDB to secure it, we'll need the current
password for the root user. If you've just installed MariaDB, and
haven't set the root password yet, you should just press enter here.
Enter current password for root (enter for none):
OK, successfully used password, moving on...
Setting the root password or using the unix_socket ensures that nobody
can log into the MariaDB root user without the proper authorisation.
You already have your root account protected, so you can safely answer 'n'.
Switch to unix_socket authentication [Y/n] Y
Enabled successfully!
Reloading privilege tables..
... Success!
You already have your root account protected, so you can safely answer 'n'.
Change the root password? [Y/n] Y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
... Success!
By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them. This is intended only for testing, and to make the installation
go a bit smoother. You should remove them before moving into a
production environment.
Remove anonymous users? [Y/n] Y
... Success!
Normally, root should only be allowed to connect from 'localhost'. This
ensures that someone cannot guess at the root password from the network.
Disallow root login remotely? [Y/n] Y
... Success!
By default, MariaDB comes with a database named 'test' that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.
Remove test database and access to it? [Y/n] Y
- Dropping test database...
... Success!
- Removing privileges on test database...
... Success!
Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.
Reload privilege tables now? [Y/n] Y
... Success!
Cleaning up...
All done! If you've completed all of the above steps, your MariaDB
installation should now be secure.
Thanks for using MariaDB!
On peut alors se connecter au serveur MariaDB sur le compte root
avec la commande
sudo mysql -u root -p
On peut alors créer un utilisateur pour chaque service nécessitant une base de donnée. Par exemple si on a besoin d'une base de donnée pour le site gnu.le-site-alice.fr
on peut créer l'utilisateur alice-gnu
ayant pour mot de passe mot_de_passe_alice_db
.
CREATE USER 'alice-gnu'@'localhost' IDENTIFIED BY 'mot_de_passe_alice_db';
GRANT ALL PRIVILEGES ON ParisL1.* TO 'alice-gnu'@'localhost';
exit;
On peut maintenant se connecter à MariaDB avec l'utilisateur alice-gnu
en utilisant la commande sudo mysql -u root -p
.
On termine en installant le paquet php8.2-mysql
permettant aux applications PHP de se connecter aux base de données.
sudo apt install php8.2-mysql
Cela conclut l'installation du serveur Web. Nous avons maintenant un serveur fonctionnel et sécurisé.