⚠️ Disclaimer
La méthode que je présente ici correspond à ma propre démarche d’apprentissage. Elle peut contenir des approximations ou des erreurs, car j’apprends et je progresse chaque jour un peu plus. Ne prenez donc pas ce que je fais comme une référence absolue, mais plutôt comme un retour d’expérience personnel.
Contexte et objectifs
J’ai eu l’occasion de travailler sur un projet pour une startup du secteur de l’assurance en expansion. Le défi était de mettre en place une infrastructure web en stack LAMP (Linux, Apache, MySQL, PHP) avec une architecture 3-tiers, trois machines distinctes plutôt qu’une grosse boîte monolithique. Cette séparation promise d’améliorer la sécurité en isolant les services, ce qui m’intéressait particulièrement venant du monde de l’analyse de données où ces préoccupations n’existaient pas.
Architecture mise en place
Trois machines virtuelles Debian 13, chacune dédiée à une fonction :
| Serveur | Fonction | Adresse IP | Services |
|---|---|---|---|
| ns1 | DNS | 172.20.5.10 | Bind9 |
| web1 | Web | 172.20.5.20 | Apache 2.4, PHP 8.0 |
| db1 | Base de données | 172.20.5.30 | MySQL 8.0 |
Le domaine était techcorp.local, accessible via www.techcorp.local.
Pourquoi trois tiers ? L’idée m’a séduit au premier abord : une compromission du serveur web n’expose pas directement la base de données. Les mises à jour de chaque composant peuvent se faire indépendamment. Chaque serveur peut être dimensionné selon ses besoins réels, plus de RAM pour MySQL, plus de CPU pour Apache. C’est théoriquement plus flexible qu’une seule grosse machine.
Configurer le DNS avec Bind9
J’ai commencé par le serveur DNS, qui doit répondre correctement pour que tout le reste fonctionne. Bind9 est le standard de facto pour un environnement interne.
Déclaration des zones dans /etc/bind/named.conf.local :
| |
Configuration de la zone directe :
| |
Configuration de la zone inverse :
| |
Validation de la configuration :
| |
Serveur web et Apache
Apache 2.4 avec PHP 8.0 devait résoudre www.techcorp.local via notre DNS interne. J’ai configuré le client DNS sur web1 en pointant vers ns1 dans /etc/systemd/resolved.conf.
Configuration du VirtualHost dans /etc/apache2/sites-available/techcorp.conf :
| |
Quant aux permissions, j’ai appliqué le principe du moindre privilège, directement inspiré de mes lectures en cybersécurité :
| |
Base de données MySQL
MySQL 8.0 avec mysql_secure_installation. Ensuite, il fallait que MySQL écoute uniquement sur son IP interne, pas sur le réseau entier. Modification dans /etc/mysql/mysql.conf.d/mysqld.cnf :
| |
Création de la base et utilisateur avec privilèges limités :
| |
L’utilisateur est restreint à l’IP du serveur web ('webapp_user'@'172.20.5.20'), avec uniquement les privilèges SELECT, INSERT, UPDATE, DELETE. Rien d’autre, pas de CREATE, pas de DROP.
Structure de la table :
| |
Intégration PHP et MySQL
Le fichier config.php gère la connexion à la base :
| |
Et index.php qui affiche les données :
| |
Tests et validation
Pour la résolution DNS :
| |
Connectivité MySQL depuis web1 :
| |
Le site lui-même :
| |
Vérification des logs :
- Apache :
/var/log/apache2/techcorp_access.log,techcorp_error.log - MySQL :
/var/log/mysql/error.log - Bind9 :
journalctl -u bind9
Sécurité
Le moindre privilège s’applique à plusieurs niveaux. MySQL : utilisateur CRUD uniquement, IP-restreint, pas de wildcard. Fichiers : répertoires 755, PHP 644, configuration 640. Réseau : MySQL n’écoute que sur 172.20.5.30, pas d’exposition directe.
Chaque service sur sa propre machine. Si le web se fait compromettre, l’attaquant n’y gagne pas un accès direct au serveur de base de données.
Ce qui a accroché en chemin
Les points finaux des enregistrements DNS, oui, ce détail stupide. Oublier le point final dans Bind9 cause une double concaténation du domaine. ns1 devient ns1.techcorp.local.techcorp.local. Il faut systématiquement utiliser la notation FQDN complète avec le point : ns1.techcorp.local.
MySQL refusait les connexions depuis web1. L’utilisateur existait, les permissions étaient bonnes, mais rien. Le problème : le paramètre bind-address était sur localhost. J’ai dû le changer pour écouter sur 172.20.5.30.
Et puis web1 lui-même ne résolvait pas techcorp.local. J’ai dû configurer systemd-resolved pour pointer vers ns1 (172.20.5.10) plutôt que vers les DNS publics. Détails bêtes, mais bloquants.
Prochaines étapes
Matériellement parlant, la structure fonctionne. Mais pour un vrai environnement de production, il y aurait du travail : pare-feu sur chaque serveur (UFW), SSL/TLS pour MySQL, HTTPS avec Let’s Encrypt, Fail2Ban. Côté disponibilité, un DNS secondaire, un second serveur web avec load balancer, réplication MySQL Master-Slave. Et puis du monitoring, Prometheus, Grafana, centraliser les logs avec ELK.
Ressources :