Serveur web mutualisé (3/5) : Architecture basique
Sommaire
I. Présentation
Dans de précédents billets (concept et limites), nous avons vu en quoi un serveur web mutualisé consistait et quelles étaient leurs limités générales. Nous allons ici aborder un post un peu plus technique où nous allons, pour mieux les comprendre, monter une architecture système qui nous permettrait de faire tourner un serveur web mutualisé (aussi basique soit il). Je ne prétends pas ici révolutionner le serveur web mutualisé mais au contraire montrer de quoi il est, en théorie, composé et faire en sorte que les services que nous allons installer permettent un fonctionnement global pouvant être assimilé à celui d'un serveur web mutualisé.
II. De quoi a t-on besoin ?
Comme nous l'avons résumé dans le précédent billet sur le sujet, nous avons vu que le serveur web mutualisé a besoin de plusieurs services basiques pour être considéré en tant que tel :
Chaque accès utilisateurs ayant alors besoin d'un accès au FTP, au minimum une base de données et d'un système pouvant comprendre et traiter les requêtes HTTP de ses clients.
III. Installation
Nous allons ici partir sur une base d'une machine sous Debian 7 à jour sans système de sécurité particulier (type Selinux). Une machine standard en somme sur laquelle nous allons installer nos différents services. On commence bien entendu par mettre à jour notre liste de dépôt :
apt-get update
Puis on pourra commencer par installer notre service web :
apt-get install apache2 php5
On installera également notre service de base de données ainsi que l'ensemble des fonctions qui permettront au PHP de communiquer et de communiquer avec ce service :
apt-get install mysql-server php5-mysql
On installe ensuite notre service FTP pour que les utilisateurs puissent manager le tout à distance :
wget http://http.us.debian.org/debian/pool/main/v/vsftpd/vsftpd_3.0.2-3_amd64.deb -O vsftpd.deb
dpkg -i vsftpd.deb
On va éviter de donner un accès shell à nos utilisateurs, on va donc leur donner tout de même la possibilité de manager leurs bases de données via une interface prévu à cet effet :
apt-get install phpmyadmin
On a ici un début de quelque chose, la base tout du moins. Il ne nous reste plus qu'à réfléchir sur leur configuration puis à la mettre en place.
Note : Sur des infrastructures réelles, les hébergeurs peuvent être amenés à mettre des services supplémentaires comme des services de caches, des services de surveillance des accès, de monitoring des ressources utilisées. etc. Nous aurons surement l'occasion de les étudier en détail plus tard.
IV. Les besoins de configuration
Nous allons à présent réfléchir sur comment les services doivent être calibrés et configurés pour avoir un fonctionnement global se rapprochant au maximum d'un serveur web mutualisé, le plus basique soit il. Nous avons déjà ciblé les services à mettre en place. Réfléchissons sur les points de configuration qu'il va falloir calibrer.
A. Apache/ PHP
Notre serveur Apache va être utilisé par plusieurs dizaines, centaines voire milliers de sites web en même temps qui génèrent eux aussi des milliers de visites par jour. Il faut donc penser à adapter sa configuration en conséquence. On vérifiera les valeurs de connexions simultanées par défaut et on les adaptera si besoin. On prévoira également un dossier web par client ainsi qu'un vHost pour chacun d'entre eux.
B. VSFTP
Chaque utilisateur doit avoir accès à son espace web, et pas à celui du voisin. On doit donc éviter qu'un utilisateur puisse remonter l'arborescence du serveur pour aller s'y balader dans son client FTP. On mettra, de façon plus technique, un chroot sur l'accès FTP de chaque utilisateur. On se basera sur la base utilisateur du système pour se connecter. C'est un choix qui n'est peut être pas judicieux mais qui est le plus utilisé aujourd'hui (pour sa simplicité principalement), il faut donc veiller à ce que les utilisateurs d'administration (root par exemple) ne puisse pas avoir d'accès FTP. Eh bien oui, si "client1" a envie de tester un mot de passe pour accéder au compte FTP de root, il sera bien qu'il n'y arrive pas... On y déterminera également les droits qui seront par défaut affectés aux fichiers uploadés pour que ceux-ci soient lisibles par apache directement sans intervention de l'administrateur. Nous verrons cela dans un deuxième temps car cela introduira le prochain billet sur les risques que cela engendre.
C. MySQL
Notre base de données, comme notre serveur Apache va subir un nombre très important de requêtes en simultané. Il faudra donc également adapter les valeurs de connexion maximales en conséquence. On prendra également soin de donner un accès dédié à chaque base de données client pour chaque client. Il est évident qu'un client ne devra pas avoir accès à la base de donnée du voisin !
D. Linux, groupe, droits et home
Comme dit précédemment, nous utiliserons les comptes UNIX pour créer les accès utilisateurs. On y déterminera leurs droits, le groupe dans lequel ils seront, leur mot de passe, leur "home" qui correspondra à leur chroot FTP, etc. C'est un point central de la configuration du serveur.
E. Arborescence
Nous allons mettre en place une arborescence logique où nous pourrons nous y retrouver facilement parmi les futurs milliers de sites web qui viendront s'y déposer ;). Il nous faut :
- Un répertoire pour les utilisateurs contenant un emplacement où stocker leur site web, c'est sur ce répertoire que chaque vHost d'Apache pointera.
- Un répertoire pour les logs de chaque site, il est difficilement envisageable de tout stocker dans un seul et unique fichier.
- Un emplacement pour les sessions/upload temporaires PHP.
V. Configuration
Nous allons maintenant passer à la configuration de nos différents services. Afin de mieux vous faire comprendre la problématique du serveur web mutualisé, je vais volontairement dans un premier temps oublier des modifications de configuration que je corrigerais dans le dernier chapitre de cet article. Le but est simplement de vous montrer qu'avec une configuration par défaut (standard), on ne peut monter un serveur web mutualisé et que nous sommes obligé d'ouvrir des accès qui peuvent être dangereux pour qu'un serveur web mutualisé soit monté. Cet aspect sécurité sera expliqué plus en détail par la suite.
A. Création de l'arborescence
On va donc commencer par créer notre arborescence, on va par exemple décider de tout stocker dans "/storage" en supposant par exemple que c'est le montage d'un deuxième cluster de disque en RAID 1 (au minimum) et que notre premier disque est donc un disque réservé au système :
mkdir /storage
On va dire que les accès clients seront dans les répertoires "/storage/web/www-client1", "/storage/web/www-client2" et ainsi de suite, les sessions dans "/storage/web/sessions" et les uploads temporaires dans "/storage/web/uploads". Les Logs étant des éléments critiques, nous les mettrons dans "/storage/logs". On pensera également à leur rotation au moment voulu. Enfin on créera par sécurité un dossier vide pour qu'Apache amène les utilisateurs qui tentent des accès un peu suspicieux à l'intérieur. Par exemple "/storage/web/default" :
mkdir /storage/web mkdir /storage/web/default mkdir /storage/web/sessions mkdir /storage/web/uploads mkdir /storage/logs
Voilà une arborescence de base, nous l'étofferons au fur et à mesure si besoin. On met ensuite Apache en groupe et propriétaire de toute cette arborescence web, sinon il ne pourra aller lire les fichiers qui s'y situent.
chown www-data:www-data /storage/web -Rf
On va également enlever les droits "other" sur ces répertoires pour que tout le monde ne puisse pas tout lire par défaut :
chmod 770 /storage/web -Rf
B. Configuration du service HTTP - Apache
Nous allons commencer par modifier la configuration de base d'Apache. Nous nous occuperons des vHosts clients après leur création (sous peine de créer une erreur au redémarrage d'apache car les répertoires spécifiés n'existeront pas ;)). On va donc modifier le fichier "/etc/apache2/apache2.conf" où nous irons chercher et modifier ces différentes valeurs (elle sont réparties dans le fichier de configuration voire commentées pour certaines) :
ServerTokens Prod MaxClients 500 ServerLimit 500 ServerSignature Off NameVirtualHost *:80
- J'ai déjà expliqué l'intérêt de modifier les valeurs ServerTokens et ServerSignature dans ce tutoriel que je vous invite à aller voir si vous avez besoin de plus de précisions.
- La valeur MaxClient du fichier de configuration d'Apache2 permet de fixer le nombre maximal de connexions qui pourront être traitées de façon simultanées, les autres seront mises dans une fil d'attente. Comme nous l'avons précisé plus haut, nous aurons énormément de connexions sur notre serveur web mutualisé, il convient donc d'élever cette barrière qui est à 150 par défaut, il est en lien avec "ServerLimit" que nous modifions également, les valeurs sont à adapter selon la puissance de voter serveur et la charge de celui-ci, il s'agit ici d'un exemple.
- On déclare ensuite que nos vHost se positionneront sur le port 80.
On ira ensuite modifier le fichier "/etc/apache/sites-enabled/000-default" qui contient la configuration du site par défaut. En effet, celui-ci pointe toujours sur "/var/www". Nous allons donc changer la valeur "Directory" et "DocumentRoot" pour les faire pointer vers "/storage/web/default". On pourra ensuite recharger une première fois notre service http :
service apache2 reload
C. PHP
Nous allons à présent modifier quelques paramètres au niveau de la configuration générale de PHP :
vim /etc/php5/apache2/php.ini
A nouveau, nous irons trouver et/ou décommenter certains paramètres. D'abord on précisera le chemin du répertoire d'upload temporaire. PHP se sert de ce répertoire pour uploader de façon temporaire les fichiers des utilisateurs (via le web, rien à voir avec le FTP) puis pour les vérifier (par un système antivirus par exemple ou pour des vérifications de format), une fois l'upload terminé, il transfert ce fichier vers son emplacement réel :
upload_tmp_dir = /storage/web/uploads
Même chose pour les fichiers de sessions PHP :
session.save_path="/storage/web/sessions"
On précise combien de mémoire nous souhaitons allouer au processus PHP :
memory_limit = 256M
On pourra à nouveau redémarrer Apache pour être certain que ces paramètres sont bien pris en compte :
service apache2 reload
D. MySQL
Nous allons également apporter quelques modifications à la configuration de base de notre service de base de données (Mysql server) pour qu'il s'adapte à la charge qu'il est susceptible de supporter. On va pour cela modifier le fichier ("/etc/mysql/my.cnf") pour changer certaines valeurs qui permettent d'augmenter le nombre de requêtes et la taille des requêtes que MySQL peut supporter. Parmi ces valeurs qu'il faut adapter selon la puissance du serveur, on trouve Max_allowed_paquet, max_connexions ou encore open_files_limit.
F. Logs et PhpMyadmin
Je mets ces deux éléments dans le même sous-titre car ils n'ont aucune importance dans le contexte du tutoriel et de ce que je souhaites démontrer. On va donc installer phpmyadmin :
apt-get install phpmyadmin
La configuration de PhpMyadmin prévoit automatiquement un alias qui sera disponible pour chaque URL (ex : www.blog1.fr/phpmyadmin)
Pour les logs on indique simplement notre répertoire de logs personnalisés dans /etc/logrotate.d/apache2/ de la façon suivante :
/storage/logs/*/*log { weekly compress missingok notifempty sharedscripts delaycompress endscript }
Puis on redémarre les services affectés :
service apache2 reload service rsyslog reload
Nous indiquerons également dans la configuration Apache la répartition des logs par vHosts.
E. FTP avec VSFTPD
Nous allons enfin configurer notre service FTP. La chose la plus importante à configurer autour de ce service est le chroot qui permettra de restreindre chaque utilisateur utilisant son compte FTP à son seul répertoire web, l'empêchant ainsi d'aller dans celui des voisins ou autre part dans le serveur. Je vous mets ici le fichier de configuration complet de vsftpd qui peut, quand on ne le connais pas, être un peu complexe à configurer, "/etc/vsftpd/vsftpd.conf" :
anonymous_enable=NO local_enable=YES write_enable=YES dirmessage_enable=YES use_localtime=YES dual_log_enable=YES xferlog_enable=YES xferlog_file=/var/log/vsftpd.xfer.log log_ftp_protocol=YES vsftpd_log_file=/var/log/vsftpd.log connect_from_port_20=YES chroot_local_user=NO chroot_list_enable=YES chroot_list_file=/etc/vsftpd.chroot_list secure_chroot_dir=/var/run/vsftpd/empty pam_service_name=vsftpd rsa_cert_file=/etc/ssl/private/vsftpd.pem allow_writeable_chroot=YES
On commence bien sûr par rendre l'authentification et l'accès anonyme impossible en désactivant ce type d'accès. On va ensuite indiquer que nous allons chrooter nos utilisateurs en mettant la variable "chroot_local_user" à NO puis en activant la chroot list que nous indiquerons à la ligne suivante. Il s'agira d'un fichier texte possédant la liste des utilisateurs à chrooter (un utilisateur par ligne). On autorise ensuite l’affichage des fichiers cachés (ceux qui commencent par un ".") pour que nos utilisateurs puissent, par exemple manager leur fichier .htaccess. On pourra ensuite redémarre notre service VSFPD :
service vsftpd reload
F. Création d'un utilisateur
Nous allons à présent créer deux accès pour faire nos tests et vérifier que notre petit bout de serveur fonctionne. On utilisera pour cela la commande adduser puis passwd pour affecter les mots de passe, on crée par exemple deux espaces web "blog1" et "blog2" :
adduser user-blog1 -d /storage/web/www-blog1 passwd blog1 adduser user-blog2 -d /storage/web/www-blog2 passwd blog2 chmod 770 /storage/web/www-blog1 /storage/web/www-blog2
On précise donc le nom des utilisateurs ainsi que leur "home" qui sera également leur répertoire web et chroot FTP. On crée également les répertoires de logs :
mkdir /storage/logs/www-blog1 mkdir /storage/logs/www-blog2 chmod 777 /storage/logs -Rf
Leur base de données respectives dans MySQL :
create database sql01blog1; create user "user_blog1"@"localhost" identified by "password"; grant all privileges on sql01blog1.* to "user_blog1"@"localhost";
Puis on fait la même chose pour l'utilisateur blog2 et sa base de données :
create database sql01blog2; create user "user_blog2"@"localhost" identified by "password"; grant all privileges on sql01blog2.* to "user_blog2"@"localhost";
On va ensuite ajouter nos deux utilisateurs dans la liste des utilisateurs chrooté, pour rappel, un utilisateur par liste pour le fichier "/etc/vsftpd/chroot_list" :
mkdir /etc/vsftpd
user_blog1 user_blog2
On va ensuite créer la configuration Apache pour chacun des site. www.blog1.fr et www.blog2.fr
Note : J'utilise mon fichier hosts au niveau DNS pour tester rapidement ces accès.
On passe maintenant à la configuration Apache. On va utiliser un fichier de configuration par espace web ("/etc/apache2/sites-enabled/blog1.conf" et blog2.conf par exemple) dans lesquels on mettra cette configuration :
<VirtualHost *:80> ServerAdmin webmaster@localhost ServerName www.blog1.fr DocumentRoot /storage/web/www-blog1 Alias /database/ "/usr/share/php5/phpmyadmin/" ErrorLog "/storage/logs/www-blog1/blog1.error.log" CustomLog "/storage/logs/www-blog1/blog1.access.log" common <Directory /storage/web/www-blog1> Options -Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny allow from all </Directory> </VirtualHost>
Note : Remplacer blog1 par blog2 pour configuration du deuxième site
On pourra redémarrer le tout :
service vsftp reload service apache2 reload
VI. Test des accès
Pour tester notre accès, on va simplement faire un fichier "index.php" qui affichera "Bonjour blog1" :
<html> <body> <?php echo "Bonjour blog1"; ?> </body> </html>
A.Uploader d'un fichier web via FTP
On va donc se connecter à notre service FTP, j’utiliserai FileZilla pour cela :
Première chose , on voit que nous sommes bien chrooté et que nous ne pouvons pas remonter sur le serveur pour aller se balader. On upload donc notre fichier index.php et on va alors sur notre navigateur sur l'URL www.blog1.fr pour y voir notre fichier...
B. Ca ne marche pas !!
Eh oui ça ne marche pas, comme je l'ai dit précédemment. Le but de ce poste est de vous montrer qu'avec des droits et des paramètres par défaut, l'ensemble ne peut fonctionner pour un système mutualisé. Ici, le transfert de fichiers se fait via FTP, ceux-ci prennent donc user_blog1 en propriétaire et en groupe et doit être lu par www-data pour être affiché en HTTP. On est donc obligé d’élargir les droits et de donner des permissions supplémentaires. On doit donc faire en sorte qu'apache puisse lire les fichiers uploadés. On a deux choix, soit on donne les permissions de lecture et d'exécution à "other" via :
chmod o+rx /storage/web -Rf
Mais il faudra réaffecter ces droits à chaque nouveau fichier uploadé. Sinon on peut mettre tous les utilisateurs web dans le groupe d'Apache et ensuite dire dans la configuration vsftpd que les fichiers uploadés doivent être lisibles par le groupe de celui qui les uploads. En d'autres mots, il faut dire à vsftpd que les fichiers uploadés par les utilisateurs doivent être exécutables, modifiables et lisibles par le propriétaire et au moins son groupe, qui pour l'instant est son propre groupe. Dans un deuxième temps on mettra les utilisateurs web dans le groupe apache pour que l'utilisateur Apache dispose des droits de lecture sur ces fichiers de façon automatique.
C. Correction / le début des ennuis !
On commence donc par automatiser le fait que tous les fichiers uploadés pourront être lu par "other" pour qu'apache puisse y accéder, on effectue cette modification dans VSFTPD :
local_umask= 002
Pour que les fichiers en question soient exécutables (il faut bien que nos scripts php s'exécutent) il faut également ajouter cette option :
file_open_mode=0777
On redémarre ensuite vsftpd pour que les changements de configuration soient pris en compte :
service vsftpd restart
On met ensuite nos utilisateurs dans le groupe Apache :
adduser user_blog1 www-data adduser user_blog2 www-data
On peut alors essayer d'uploader à nouveau notre index.php et on verra alors les droits du fichier se mettre en rwxrwxr-x. Pour rappel, les droits du répertoire au dessus ("/storage/web/www-userblog1") sont :
rwxrwx--- user_blog1 www-data
Autrement dit, l'utilisateur peut y écrire, www-data aussi (ce qui est utile dans le cadre d'un wordpress par exemple) qui crée des fichiers en tant que www-data. Nous pourrons rester via navigateur et voir que cela fonctionne. Seulement voilà, quand on y réfléchit nous avons un petit problème de droit. La permission rwxrwxr-x est quand même bien permissive, tout les utilisateurs du système ont le droit de lire les fichiers des répertoires web. Je vous laisse y méditer jusqu'au prochain billet où je développerais cette problématique.
Les autres articles :
Salute,
Quelques petites erreurs :
service apache2 relaod –> service apache2 reload
adduser ussr_blog2 –> add user_blog2
Dans les paquets que tu proposes d’installer, il ne va pas manquer libapache2-mod-php5 ? D’ailleurs il faudrait rajouter logrotate.
Ce serait top aussi de rajouter les # et les $ en début de ligne voire les mysql> afin qu’on comprenne bien tout.
Une raison que tu n’utilises pas ces paramètres dans vsftpd ?
chroot_local_user=YES
# On autorise les utilisateurs virtuels à se mapper sur un compte local #
guest_enable=yes
# On autorise l’utilisation des privilèges des utilisateurs locaux pour les utilisateurs virtuels #
virtual_use_local_privs=YES
# L’utilisateur virtuel user-blog1 est mappé sur le compte local www-data #
guest_username=www-data
Super boulot et sacré tuto ! Bravo !
Tcho !
Bonjour, merci pour tes corrections. Je vais m’occuper de cela. Pour libapache2-mod-php5, il s’installe automatique avec php5 si apache est installé (Sous Debian du moins).
Les paramètres que tu proposes pour VSFPTd me semble concorder avec le fonctionnement que je propose. Je ferait des test, tu prends le problème dans l’autre sense, au lieu d’ouvrir les droits à www-apache, tu le mets en pseudo-propriétaire, ca me semble intéressant et fonctionnel. Néanmoins je t’invite à suivre les tutoriels qui sortiront prochainement et qui sont la suite de ce tuto, j’y indiquerais des problèmes de sécurité quant à l’ouverture des droits comme nous le faisons.
Merci pour ta contribution =)
La permission rwxrwxr-x est quand même bien permissive, tout les utilisateurs du système ont le droit de lire les fichiers des répertoires web.
Comprendre : ussr_blog2 aura accès en lecture aux fichiers de ussr_blog1 ?
Bonjour Teh,
Oui c’est exacte, c’est ce que je vais développer dans la prochaine partie de ce dossier, le billet sort mardi. Je t’invite à le lire lors de sa sortie, j’y développe le problème de sécurité que nos derniers ajustement fait dans ce tutoriel amènent. Mais oui tu as effectivement raison, le fonctionnement que nous développons dans un serveur web mutualisé fait que user_blog2 aura accés à user_blog1. Heureusement on suppose ici que nos utilisateurs n’ont pas de shell sur le serveur, auquel cas il faut mettre des barrières de sécurité autre. Mais il reste plusieurs moyens d’aller sur le dossier du voisin, encore une fois je te laisse y réfléchir, je le développerais dans le billet de mardi prochain 😉
bonjour Mickael.
Pour ces tutos Vous préciser que vous utilisez une debian 7 (Wheezy) on ne peut plus classique.
Vous avez choisi pour Vstpd comme serveur ftp.
Le dernier disponible ds les dépots Debian est la 2.3.5-3.
Comme moi vous avez optez pour une version plus récente une 3.0.2-X ( wget ….)
Par contre je suis étonné que vous avez pas connu de problèmes de dépendances notamment la libc6.
Pour une version récente vsftpd réclame au moins une 2.15 alors que par défaut debian 7 n’offre que la version 2.13.
Il existe des « bidouilles » pour installer une version 2.15 ou plus. Mais cela est déconseillé parce que cela peut casser le fonctionnement d’autres logiciels.
En gros comment avez vous fait pour contourner ce problème ?
Manque plus que les boîtes mail virtuelles par user et c’est parfait
Bravo tout est décrit avec beaucoup de pédagogie et de clarté.
Super boulot !
Merci.