Chapitre 719 minIntermédiaire
Déployer GLPI avec Nginx, PHP-FPM et MariaDB
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- déployer GLPI avec Nginx, PHP-FPM et MariaDB selon les recommandations de l'éditeur ;
- isoler l'application avec un compte et un pool dédiés ;
- relier GLPI à l'annuaire en LDAPS ;
- mener et documenter la recette d'un déploiement ;
- mettre à jour GLPI sans toucher aux données.
3 exercices corrigés
Déployer une application web, c'est assembler sur un serveur tout ce qu'elle demande (un serveur web, PHP, une base de données, un certificat, des tâches planifiées, un annuaire pour les comptes) puis la livrer prête à l'emploi, testée et documentée. Ce chapitre déroule le déploiement complet de GLPI 12, l'outil de gestion de parc et d'assistance, sur srv-glpi avec Nginx, PHP-FPM et MariaDB, en réutilisant tout ce que les chapitres 2 à 6 ont installé. C'est le type même de réalisation qu'on présente à l'épreuve E6 SISR. L'utilisation de GLPI (parc, tickets, inventaire) est l'objet du cours GLPI et gestion de parc, qui détaille aussi l'installation avec Apache.
Le cahier des charges
Sorbier remplace son outil de tickets par GLPI. Antoine Mercier fixe les exigences :
| Exigence | Réponse technique | Chapitre |
|---|---|---|
Accessible aux salariés à l'adresse https://glpi.ad.sorbier.example | Alias DNS, Nginx, certificat de Sorbier-AC | 2, 6 |
| Connexion avec le compte Windows de chaque salarié | Annuaire Active Directory en LDAPS | 7, 8 |
| Données sur le serveur, base dédiée, compte limité | MariaDB local, compte glpi | 5 |
| GLPI isolé des autres services | Pool PHP-FPM et compte système dédiés | 4 |
| Notifications par courriel | Relais Postfix local | 11 |
| Sauvegarde quotidienne, restauration testée | mariadb-dump et copie des fichiers vers srv-partages | 5, 9 |
| Journaux conservés hors du serveur | Agent Wazuh, syslog vers srv-journaux | 12 |
Le serveur : Debian 13, 2 processeurs virtuels, 4 Go de mémoire, 40 Go de disque, préparé comme au chapitre 1. GLPI 12.0.1, la version courante en novembre 2026, demande au moins PHP 8.3 et MariaDB 10.11 : les versions de Debian 13 (PHP 8.4, MariaDB 11.8) conviennent.
Étape 1 : le DNS et les paquets
Sur un contrôleur de domaine, ajoute l'alias. Les utilisateurs taperont le nom du service, glpi, et non celui du serveur : le jour où GLPI change de machine, seul l'alias change.
Add-DnsServerResourceRecordCName -ZoneName "ad.sorbier.example" -Name "glpi" `
-HostNameAlias "srv-glpi.ad.sorbier.example"
Sur srv-glpi, installe la pile et les extensions PHP que GLPI exige ou recommande :
sudo apt install nginx mariadb-server php8.4-fpm php8.4-cli \
php8.4-curl php8.4-gd php8.4-intl php8.4-mysql php8.4-xml php8.4-mbstring \
php8.4-bcmath php8.4-zip php8.4-bz2 php8.4-ldap
Les autres extensions demandées par GLPI (fileinfo, filter, session, zlib, openssl, ctype, iconv, sodium, exif) sont déjà compilées dans le PHP de Debian, et OPcache est installé avec PHP-FPM.
Étape 2 : la base et son compte
Sécurise MariaDB, puis crée la base glpi, le compte glpi@localhost et charge les fuseaux horaires, comme au chapitre 5 :
sudo mariadb-secure-installation
sudo mariadb <<'SQL'
CREATE DATABASE glpi CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'glpi'@'localhost' IDENTIFIED BY 'Mot-De-Passe-Long-Et-Unique';
GRANT ALL PRIVILEGES ON glpi.* TO 'glpi'@'localhost';
GRANT SELECT ON mysql.time_zone_name TO 'glpi'@'localhost';
SQL
mariadb-tzinfo-to-sql /usr/share/zoneinfo | sudo mariadb mysql
Étape 3 : télécharger GLPI et ranger ses dossiers
cd /tmp
wget https://github.com/glpi-project/glpi/releases/download/12.0.1/glpi-12.0.1.tgz
sudo tar -xzf glpi-12.0.1.tgz -C /var/www/ # crée /var/www/glpi
Comme le recommande la documentation de GLPI, la configuration, les données et les journaux sortent du dossier de l'application, selon les conventions de Linux. Une erreur de configuration du serveur web ne pourra pas les exposer, et une mise à jour remplacera le code sans toucher aux données.
| Contenu | Emplacement | Constante de GLPI |
|---|---|---|
| Application (code) | /var/www/glpi | — |
Configuration, dont l'accès à la base et la clé de chiffrement glpicrypt.key | /etc/glpi | GLPI_CONFIG_DIR |
| Données : documents joints, cache, sessions | /var/lib/glpi/files | GLPI_VAR_DIR |
| Plugins du marketplace | /var/lib/glpi/plugins | GLPI_MARKETPLACE_DIR |
| Journaux | /var/log/glpi | GLPI_LOG_DIR |
sudo mkdir -p /etc/glpi /var/lib/glpi/files /var/lib/glpi/plugins /var/log/glpi
sudo cp -a /var/www/glpi/config/. /etc/glpi/
sudo cp -a /var/www/glpi/files/. /var/lib/glpi/files/
Deux petits fichiers indiquent à GLPI où chercher. /var/www/glpi/inc/downstream.php :
<?php
define('GLPI_CONFIG_DIR', '/etc/glpi/');
if (file_exists(GLPI_CONFIG_DIR . '/local_define.php')) {
require_once GLPI_CONFIG_DIR . '/local_define.php';
}
et /etc/glpi/local_define.php :
<?php
define('GLPI_VAR_DIR', '/var/lib/glpi/files');
define('GLPI_MARKETPLACE_DIR', '/var/lib/glpi/plugins');
define('GLPI_LOG_DIR', '/var/log/glpi');
Le cours GLPI donne les mêmes étapes avec Apache, où l'application tourne sous www-data. Ici, GLPI aura son propre compte, glpi, utilisé par son pool PHP-FPM :
sudo adduser --system --group --no-create-home --home /var/lib/glpi glpi
sudo chown -R root:root /var/www/glpi
sudo chown -R glpi:glpi /etc/glpi /var/lib/glpi /var/log/glpi
sudo chmod 750 /etc/glpi
Le code appartient à root : ni Nginx ni GLPI ne peuvent le modifier. Seul le compte glpi écrit dans la configuration, les données et les journaux, et lui seul lit la configuration (droits 750), qui contient le mot de passe de la base.
Étape 4 : le pool PHP-FPM de GLPI
/etc/php/8.4/fpm/pool.d/glpi.conf :
[glpi]
user = glpi
group = glpi
listen = /run/php/php8.4-fpm-glpi.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
; Réglages de sécurité des sessions demandés par GLPI
php_admin_flag[session.cookie_httponly] = on
php_admin_flag[session.cookie_secure] = on
php_admin_value[session.cookie_samesite] = Lax
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 20M
php_admin_value[post_max_size] = 20M
php_admin_flag[display_errors] = off
php_admin_value[error_log] = /var/log/glpi/php-errors.log
sudo php-fpm8.4 -t && sudo systemctl reload php8.4-fpm
ls -l /run/php/php8.4-fpm-glpi.sock
pm.max_children = 20 reste sous le maximum de 25 calculé au chapitre 4 pour srv-glpi, avec une marge pour les pics de mémoire. Les réglages de session sont dans le pool plutôt que dans php.ini : ils ne concernent que GLPI, et l'application ne peut pas les modifier.
Étape 5 : le server block Nginx
GLPI publie une configuration officielle pour Nginx. Elle repose sur une idée forte : la racine du site est le dossier public, qui ne contient que index.php et des ressources statiques. Toutes les adresses de l'application passent par ce seul script, le routeur de GLPI ; le reste du code, la configuration et les données ne sont jamais accessibles par une adresse web.
/etc/nginx/sites-available/glpi :
server {
listen 80;
listen [::]:80;
server_name glpi.ad.sorbier.example srv-glpi.ad.sorbier.example;
return 301 https://glpi.ad.sorbier.example$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name glpi.ad.sorbier.example;
ssl_certificate /etc/ssl/glpi/glpi.crt;
ssl_certificate_key /etc/ssl/glpi/glpi.key;
add_header Strict-Transport-Security "max-age=31536000" always;
root /var/www/glpi/public;
client_max_body_size 20m;
access_log /var/log/nginx/glpi.access.log;
error_log /var/log/nginx/glpi.error.log;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php$ {
fastcgi_pass unix:/run/php/php8.4-fpm-glpi.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
| Bloc | Rôle |
|---|---|
Premier server | Toute requête en HTTP, ou adressée au nom du serveur, est redirigée vers l'adresse officielle en HTTPS |
root /var/www/glpi/public | Seul le dossier public est servi |
try_files $uri /index.php$is_args$args | Une ressource statique existante est servie par Nginx ; toute autre adresse va au routeur, avec ses paramètres |
location ~ ^/index\.php$ | Seul index.php est transmis à PHP-FPM, et non n'importe quel fichier .php |
client_max_body_size 20m | Les pièces jointes des tickets, en cohérence avec upload_max_filesize du pool |
Le certificat est celui obtenu de l'autorité interne au chapitre 6, pour les noms glpi.ad.sorbier.example et srv-glpi.ad.sorbier.example.
sudo ln -s /etc/nginx/sites-available/glpi /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
Étape 6 : installer GLPI en ligne de commande
L'assistant web fonctionne (ouvre https://glpi.ad.sorbier.example et suis les écrans), mais la console est plus rapide, se documente mieux et se rejoue à l'identique. Elle se lance avec le compte de l'application, pour que les fichiers créés lui appartiennent ; depuis GLPI 11, elle refuse de tourner en root.
cd /var/www/glpi
sudo -u glpi php bin/console system:check_requirements
sudo -u glpi php bin/console db:install -L fr_FR -H localhost -d glpi -u glpi -p
# -p sans valeur : la console demande le mot de passe, sans l'écrire dans l'historique du shell
sudo -u glpi php bin/console database:enable_timezones
sudo rm /var/www/glpi/install/install.php
system:check_requirements affiche chaque prérequis avec son état : tout doit être validé, sauf peut-être des recommandations facultatives. Si une extension manque, installe le paquet php8.4-… correspondant et recharge PHP-FPM. Supprimer install/install.php empêche quiconque de relancer une installation par-dessus ta base.
Étape 7 : les tâches planifiées
GLPI a des actions automatiques : envoi des notifications, alertes de contrats, calcul des délais, nettoyage. Elles doivent être lancées chaque minute par le système, avec le compte de l'application :
sudo crontab -u glpi -e
* * * * * /usr/bin/php /var/www/glpi/front/cron.php
Puis, dans GLPI, Configuration > Actions automatiques : passe les actions en mode CLI. Sans cette tâche, aucune notification ne part (cours GLPI).
Étape 8 : relier GLPI à l'annuaire en LDAPS
Les salariés se connectent avec leur compte du domaine. GLPI interroge l'annuaire en LDAPS (LDAP chiffré en TLS, port 636) avec un compte de service en lecture seule, svc-glpi, créé dans l'annuaire avec un mot de passe long et sans aucun droit d'administration.
Deux conditions préalables : les contrôleurs de domaine présentent un certificat pour LDAPS (délivré par Sorbier-AC), et srv-glpi fait confiance à cette autorité (update-ca-certificates, chapitre 6). Teste avant de toucher à GLPI, avec les outils du paquet ldap-utils :
sudo apt install ldap-utils
ldapwhoami -H ldaps://srv-ad.ad.sorbier.example -D "svc-glpi@ad.sorbier.example" -W
# u:SORBIER\svc-glpi
ldapsearch -H ldaps://srv-ad.ad.sorbier.example -D "svc-glpi@ad.sorbier.example" -W \
-b "OU=Sorbier,DC=ad,DC=sorbier,DC=example" "(sAMAccountName=jlefevre)" cn mail
Si ldapwhoami répond Can't contact LDAP server, la cause est presque toujours le certificat (autorité inconnue, ou nom du contrôleur absent de son certificat) ou le pare-feu (TCP 636 de srv-glpi vers les contrôleurs). La bibliothèque LDAP de Debian, que PHP utilise aussi, lit le magasin de confiance du système (TLS_CACERT /etc/ssl/certs/ca-certificates.crt dans /etc/ldap/ldap.conf) : une fois ldapwhoami réussi, GLPI y arrivera.
La configuration dans GLPI se fait dans Configuration > Authentification > Annuaires LDAP, avec le bouton de préconfiguration Active Directory : serveur ldaps://srv-ad.ad.sorbier.example, port 636, base OU=Sorbier,DC=ad,DC=sorbier,DC=example, compte svc-glpi@ad.sorbier.example, champ de l'identifiant samaccountname, puis un second serveur, srv-ad2, en réplique. Le détail des champs, l'import des comptes et les règles d'habilitation sont dans le cours GLPI.
Étape 9 : la recette
Une installation n'est terminée que quand chaque exigence est vérifiée et prouvée. La recette de srv-glpi :
| Test | Commande ou action | Résultat attendu |
|---|---|---|
| Redirection vers HTTPS | curl -sI http://glpi.ad.sorbier.example | 301 vers https://glpi.ad.sorbier.example/ |
| Certificat valide sur un poste du domaine | Ouvrir l'adresse dans Edge sur un poste du VLAN 10 | Cadenas, aucun avertissement, émetteur Sorbier-AC |
| Code et configuration inaccessibles | curl -sI https://glpi.ad.sorbier.example/config/config_db.php et …/inc/downstream.php | 404 (ou page du routeur), jamais le contenu d'un fichier |
| Prérequis de GLPI | sudo -u glpi php bin/console system:check_requirements | Tous validés |
| Connexion avec un compte du domaine | Se connecter en jlefevre | Accès à l'interface simplifiée |
| Comptes par défaut | Se connecter en glpi / glpi | Refusé : compte désactivé ou mot de passe changé |
| Actions automatiques | Configuration > Actions automatiques | Dernière exécution datée de moins de deux minutes |
| Isolement | ps -o user,cmd -C php-fpm8.4 | Processus du pool glpi sous le compte glpi |
| Base non exposée | ss -tlnp | grep 3306 | Écoute sur 127.0.0.1 seulement (ou l'adresse prévue au chapitre 5) |
| Sauvegarde | Restaurer la dernière sauvegarde dans une base de test | Base complète, GLPI démarre sur la copie |
N'oublie pas les gestes de sécurité de la première connexion, que GLPI rappelle par des bandeaux : désactiver ou changer les mots de passe des comptes glpi, tech, normal et post-only, renseigner l'URL de l'application (https://glpi.ad.sorbier.example) dans Configuration > Générale, activer la double authentification pour les administrateurs.
Mettre GLPI à jour
GLPI publie régulièrement des versions correctives, souvent pour des failles de sécurité (la 12.0.1 corrige notamment un contournement de l'authentification externe). Grâce au rangement de l'étape 3, une mise à jour remplace le dossier de l'application sans toucher aux données :
# 1. Sauvegarde de la base et de la configuration
sudo mariadb-dump --single-transaction glpi | gzip > /var/backups/glpi-avant-maj.sql.gz
sudo tar -czf /var/backups/glpi-etc-avant-maj.tgz /etc/glpi
# 2. Remplacement du code
sudo mv /var/www/glpi /var/www/glpi-ancien
sudo tar -xzf glpi-12.0.2.tgz -C /var/www/
sudo cp /var/www/glpi-ancien/inc/downstream.php /var/www/glpi/inc/
sudo rm /var/www/glpi/install/install.php
# 3. Mise à jour du schéma de la base
cd /var/www/glpi && sudo -u glpi php bin/console db:update
12.0.2 est ici un exemple : prends la dernière version publiée sur la page du projet. La procédure complète (mode maintenance, plugins, retour arrière) est dans le cours GLPI.
Exercices
Exercice 1 sur 3 Le code source téléchargé Réussi À revoir
Un collègue a écrit le server block de GLPI en mettant root /var/www/glpi; au lieu de root /var/www/glpi/public;, avec un bloc location ~ \.php$ générique vers PHP-FPM. L'application semble fonctionner. Explique deux risques de cette configuration et la correction.
Voir la réponseMasquer la réponse de l'exercice 1
Risques.
- Tous les dossiers de l'application deviennent accessibles par une adresse web :
/files/,/config/,/vendor/,/install/install.phpsi le fichier existe encore. Selon les fichiers présents, on peut télécharger des documents joints, des journaux, ou exécuter un script qui n'était pas prévu pour être appelé directement. - N'importe quel fichier
.phpde l'arborescence est exécutable par une adresse, y compris des scripts d'outils de développement fournis dansvendor/. C'est exactement ce que la racinepublicet le routeur unique de GLPI évitent.
Correction. root /var/www/glpi/public;, le try_files $uri /index.php$is_args$args; du bloc /, et un seul bloc PHP, location ~ ^/index\.php$. Puis vérifier que curl -sI https://glpi.ad.sorbier.example/config/ ne renvoie plus rien d'autre que la réponse du routeur. Le rangement de l'étape 3 (configuration dans /etc/glpi, données dans /var/lib/glpi) est une seconde barrière : même avec une racine mal réglée, ces fichiers ne sont pas sous /var/www.
Tu avais trouvé ?
Exercice 2 sur 3 La console en root Réussi À revoir
Pressé, un technicien lance sudo php bin/console cache:clear au lieu de sudo -u glpi php bin/console cache:clear sur une ancienne version de GLPI qui l'acceptait encore. Depuis, des pages de GLPI affichent des erreurs. Explique pourquoi et comment corriger.
Voir la réponseMasquer la réponse de l'exercice 2
Lancée en root, la console a recréé des fichiers de cache (et peut-être de journaux) qui appartiennent à root. Les processus du pool, qui tournent sous glpi, ne peuvent plus les modifier ni les supprimer : les pages qui en ont besoin échouent.
Correction : rendre au compte glpi la propriété de ses dossiers, puis vider le cache avec le bon compte.
sudo chown -R glpi:glpi /var/lib/glpi /var/log/glpi /etc/glpi
cd /var/www/glpi && sudo -u glpi php bin/console cache:clear
C'est la raison pour laquelle les versions récentes de la console refusent de tourner en root.
Tu avais trouvé ?
Exercice 3 sur 3 Préparer la recette Réussi À revoir
La commission d'E6 te demande de prouver, pendant l'oral, que GLPI chiffre la connexion à l'annuaire. Quelles preuves peux-tu montrer, sur le serveur et sur le réseau ?
Voir la réponseMasquer la réponse de l'exercice 3
- La configuration : dans Configuration > Authentification > Annuaires LDAP, le serveur est
ldaps://srv-ad.ad.sorbier.example, port 636. - Le test en ligne de commande :
ldapwhoami -H ldaps://srv-ad.ad.sorbier.example -D "svc-glpi@ad.sorbier.example" -Wrépondu:SORBIER\svc-glpi; la même commande avec un certificat inconnu (en retirant la racine du magasin, sur une maquette) échoue, ce qui prouve que le certificat est vérifié. - Les connexions établies :
ss -tnp | grep 636pendant une connexion d'utilisateur montre une connexion desrv-glpivers192.168.10.177:636, et aucune vers le port 389. - Une capture de trames :
sudo tcpdump -i ens18 -nn host 192.168.10.177 and port 636 -w ldaps.pcappendant une connexion, ouverte ensuite dans Wireshark : on voit une poignée de main TLS puis des données chiffrées (Application Data), sans aucun nom de compte ni mot de passe lisible. À comparer avec une capture sur le port 389 en maquette, où le mot de passe apparaît en clair dans la requête bindRequest.
Tu avais trouvé ?