# Créer un groupe d'utilisateurs pour un site web Apache ## Pourquoi un groupe par site ? Quand on héberge un site web, deux populations doivent pouvoir lire ses fichiers : **Apache** (le serveur web, qui les sert aux visiteurs) et les **personnes qui maintiennent le site** (développeurs, intégrateurs, administrateurs de contenu). Si on ne réfléchit pas à la question des droits, on tombe rapidement dans un des deux travers classiques : - **Tout ouvrir** (`chmod 777` partout) : c'est la porte ouverte aux compromissions. Si une faille permet à Apache d'écrire un fichier, l'attaquant peut alors écraser n'importe quel script PHP du site. - **Tout faire en root** : c'est pratique au début, mais cela impose à chaque mainteneur de connaître le mot de passe administrateur, et il devient impossible de tracer qui a modifié quoi. La bonne pratique est donc de créer **un groupe dédié par site**, qui rassemble les personnes habilitées à intervenir dessus. Plusieurs avantages : - **Évolutif** : ajouter ou retirer un mainteneur revient à ajouter ou retirer un utilisateur du groupe, sans rien changer aux permissions des fichiers. - **Étanche** : les mainteneurs d'un site n'ont aucun droit sur les autres sites de la machine. - **Traçable** : chaque modification est faite par un compte nommé, pas par `root`. Cette approche reste valable même pour un site géré par une seule personne : le jour où vous voudrez déléguer la maintenance ou simplement créer un compte de déploiement, le travail aura déjà été fait. Dans cet article, on prend comme exemple un site fictif **perdu.com**, dont les fichiers seront placés dans `/var/www/perdu.com`. Adaptez les noms à votre cas. ## Étape 1 — Créer le groupe dédié ``` sudo groupadd www-perdu.com ``` Le nom du groupe est arbitraire ; le préfixe `www-` est une convention qui rappelle qu'il s'agit d'un groupe lié à un site web. Vous pouvez vérifier sa création : ``` getent group www-perdu.com ``` ## Étape 2 — Ajouter les mainteneurs au groupe Ajoutez chaque utilisateur qui devra travailler sur le site : ``` sudo usermod -aG www-perdu.com chloe ``` L'option **`-a` (append) est obligatoire** : sans elle, `-G` **remplace** la liste complète des groupes secondaires de l'utilisateur au lieu d'y ajouter. C'est l'erreur classique qui fait perdre à quelqu'un l'accès à `sudo`, `docker` ou `libvirt` parce qu'on a écrasé ses autres appartenances. Pour ajouter plusieurs personnes : ``` sudo usermod -aG www-perdu.com chloe sudo usermod -aG www-perdu.com mathieu ``` **Important** : l'appartenance à un nouveau groupe n'est prise en compte qu'à la **prochaine ouverture de session**. Si chloe est déjà connectée, elle doit se déconnecter puis se reconnecter (ou ouvrir un nouveau terminal SSH). Pour vérifier ses groupes effectifs : ``` groups chloe ``` ## Étape 3 — Créer l'arborescence du site On place le site dans `/var/www`, l'emplacement standard sur Debian/Ubuntu et un choix courant sur Fedora/RHEL : ``` sudo mkdir -p /var/www/perdu.com/www sudo mkdir -p /var/www/perdu.com/logs ``` L'option `-p` crée les dossiers parents au besoin et ne renvoie pas d'erreur s'ils existent déjà. La structure proposée sépare les fichiers servis publiquement (`www/`) de ce qui ne doit jamais être exposé (logs applicatifs, sauvegardes, données privées). À ce stade, les dossiers appartiennent à `root:root` avec des droits `755`. Personne d'autre que root ne peut y écrire. ## Étape 4 — Attribuer la propriété au groupe On veut que : - **Le groupe `www-perdu.com`** puisse lire **et écrire** dans les dossiers (les mainteneurs déploient et modifient les fichiers). - **Apache** puisse lire les fichiers pour les servir. - **Personne d'autre** ne puisse y accéder. On commence par attribuer le groupe : ``` sudo chgrp -R www-perdu.com /var/www/perdu.com ``` L'option `-R` (récursif) applique le changement à tout le contenu déjà présent. Puis on applique des permissions adaptées : ``` sudo find /var/www/perdu.com -type d -exec chmod 2775 {} \; sudo find /var/www/perdu.com -type f -exec chmod 664 {} \; ``` Décortiquons ce qui se passe ici. On utilise `find` plutôt qu'un `chmod -R` unique parce que **les permissions des dossiers et des fichiers ne doivent pas être les mêmes** : un fichier de configuration n'a pas à être exécutable, alors qu'un dossier a besoin du bit `x` pour être parcouru. - **Dossiers : `2775`** soit `rwxrwsr-x`. Le propriétaire (root) et les membres du groupe `www-perdu.com` peuvent lire, écrire et entrer dans le dossier. Les autres (dont `www-data`/`apache`) peuvent lire et entrer, mais pas modifier. - **Fichiers : `664`** soit `rw-rw-r--`. Le propriétaire et le groupe peuvent lire et écrire, les autres uniquement lire. ### Le bit SGID : pourquoi le `2` en tête ? Le chiffre `2` devant `775` active le **bit SGID** sur les dossiers. Ce bit a un effet décisif : **tout nouveau fichier ou sous-dossier créé hérite automatiquement du groupe du dossier parent**, au lieu d'hériter du groupe principal de la personne qui le crée. Sans SGID, si chloe (dont le groupe principal est `chloe`) crée un fichier, ce fichier appartient au groupe `chloe`, ce qui le rend invisible en écriture pour les autres mainteneurs du site. Avec SGID, le fichier appartient automatiquement à `www-perdu.com`, et toute l'équipe peut continuer à travailler dessus sans intervention. Si vous préférez utiliser `chmod` directement plutôt que `find`, voici la version équivalente — un peu plus permissive sur les exécutables existants, mais plus simple à retenir : ``` sudo chmod -R g+rwX,o+rX,o-w /var/www/perdu.com sudo find /var/www/perdu.com -type d -exec chmod g+s {} \; ``` L'astuce du `X` majuscule (au lieu de `x` minuscule) est utile : elle n'ajoute le droit d'exécution qu'aux dossiers et aux fichiers déjà exécutables, sans rendre exécutables tous les fichiers texte par mégarde. ## Étape 5 — Garantir les bons droits sur les nouveaux fichiers Le SGID s'occupe du groupe, mais pas des permissions elles-mêmes. Pour que les fichiers créés par un mainteneur soient en `664` (et non en `644` qui interdirait l'écriture aux autres membres du groupe), il faut un **umask permissif** côté utilisateur. Demandez à chaque mainteneur d'ajouter cette ligne à son `~/.bashrc` : ``` umask 0002 ``` Cet umask conserve les droits d'écriture pour le groupe. Avec un umask `0022` (le défaut habituel), chloe créerait des fichiers en `644` que mathieu ne pourrait pas modifier. ### Solution plus robuste : les ACL Si vous voulez une garantie absolue sans dépendre du umask de chacun, les **ACL** (*Access Control Lists*) sont l'outil de référence : ``` sudo setfacl -R -m g:www-perdu.com:rwX /var/www/perdu.com sudo setfacl -R -d -m g:www-perdu.com:rwX /var/www/perdu.com ``` La première ligne applique le droit, la seconde le rend **par défaut** : tout nouveau fichier ou dossier créé dedans héritera automatiquement de ces droits, peu importe l'umask. Sur Debian/Ubuntu, les ACL sont déjà activées par défaut sur les systèmes de fichiers modernes ; sur d'autres distributions, vous pourriez devoir installer le paquet `acl`. Vérifier les ACL en place : ``` getfacl /var/www/perdu.com ``` ## Étape 6 — Le cas particulier d'Apache Sur Debian/Ubuntu, Apache fonctionne sous l'utilisateur **`www-data`** ; sur Fedora/RHEL et openSUSE, il s'agit de **`apache`**. Vérifiez le bon nom sur votre système : ``` ps aux | grep -E 'apache|httpd' | grep -v grep ``` Avec les permissions définies plus haut, Apache peut **lire** tous les fichiers du site (le droit `r-x` accordé aux *autres* suffit). C'est ce qu'on veut dans 90 % des cas : Apache sert le contenu sans pouvoir le modifier, ce qui limite considérablement les dégâts d'une éventuelle faille. ### Quand Apache a besoin d'écrire Certaines applications web ont besoin qu'Apache puisse écrire dans des dossiers précis : envoi de fichiers via un formulaire, cache, sessions, génération de miniatures… Le piège, c'est de répondre à ce besoin en ouvrant l'écriture **partout**, ce qui rend possible le téléversement d'un script malveillant qui sera ensuite exécuté. **Ne donnez l'écriture à Apache que sur les dossiers strictement nécessaires.** Exemple pour un dossier `uploads` : ``` sudo mkdir -p /var/www/perdu.com/www/uploads sudo chown www-data:www-perdu.com /var/www/perdu.com/www/uploads sudo chmod 2775 /var/www/perdu.com/www/uploads ``` Sur Fedora/RHEL, remplacez `www-data` par `apache`. **Variante avec ACL**, sans changer le propriétaire : ``` sudo setfacl -R -m u:www-data:rwX /var/www/perdu.com/www/uploads sudo setfacl -R -d -m u:www-data:rwX /var/www/perdu.com/www/uploads ``` C'est souvent plus propre : le propriétaire principal reste `root`, et on ajoute juste un droit ciblé pour Apache. Pas d'ambiguïté sur « à qui appartient ce dossier ». ### Empêcher l'exécution dans les dossiers d'upload Un dossier où Apache peut écrire et où il peut exécuter du PHP est une cible classique. Désactivez explicitement l'exécution de scripts dans `uploads/` via la configuration Apache du site : ``` php_admin_flag engine off Require all denied ``` ## Étape 7 — Cas particulier de SELinux (Fedora, RHEL, AlmaLinux…) Sur les distributions qui activent SELinux par défaut, des permissions Unix correctes **ne suffisent pas** : il faut aussi que les fichiers portent le bon contexte de sécurité. Si Apache renvoie des erreurs 403 alors que `ls -l` semble correct, c'est presque toujours SELinux. Pour un dossier de contenu en lecture seule par Apache : ``` sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/perdu.com(/.*)?" sudo restorecon -Rv /var/www/perdu.com ``` Pour un dossier où Apache doit pouvoir écrire (comme `uploads`) : ``` sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/perdu.com/www/uploads(/.*)?" sudo restorecon -Rv /var/www/perdu.com/www/uploads ``` La commande `semanage` enregistre la règle de manière persistante (elle survit aux mises à jour), et `restorecon` l'applique aux fichiers existants. Sur Debian/Ubuntu, ces commandes ne sont pas nécessaires (SELinux n'y est pas activé par défaut, AppArmor joue un rôle équivalent mais ne demande généralement pas de configuration spécifique pour Apache). ## Vérification finale Pour vérifier que tout est correct, listez les permissions : ``` ls -ld /var/www/perdu.com ls -l /var/www/perdu.com/www ``` Vous devriez voir quelque chose comme : ``` drwxrwsr-x 3 root www-perdu.com 4096 Mar 12 14:00 /var/www/perdu.com drwxrwsr-x 2 root www-perdu.com 4096 Mar 12 14:00 /var/www/perdu.com/www ``` Notez le `s` dans `rws` à la place du `x` du groupe : c'est la signature visuelle du bit SGID. C'est le signe que la configuration héritera correctement. Créez un fichier de test en tant que chloe pour valider : ``` su - chloe cd /var/www/perdu.com/www echo "

Bienvenue

" > index.html ls -l index.html ``` Le fichier `index.html` doit appartenir à `chloe:www-perdu.com` et être en `-rw-rw-r--` (mode 664). Si c'est le cas, vous êtes prêt à configurer le `VirtualHost` Apache pointant vers `/var/www/perdu.com/www`. ## En résumé La recette complète, condensée : ``` # 1. Créer le groupe et y ajouter les mainteneurs sudo groupadd www-perdu.com sudo usermod -aG www-perdu.com chloe # 2. Créer l'arborescence sudo mkdir -p /var/www/perdu.com/www # 3. Attribuer propriété et permissions avec SGID sudo chgrp -R www-perdu.com /var/www/perdu.com sudo find /var/www/perdu.com -type d -exec chmod 2775 {} \; sudo find /var/www/perdu.com -type f -exec chmod 664 {} \; # 4. (Optionnel mais recommandé) Garantir l'héritage avec des ACL sudo setfacl -R -d -m g:www-perdu.com:rwX /var/www/perdu.com # 5. Donner l'écriture à Apache UNIQUEMENT là où c'est nécessaire sudo setfacl -R -m u:www-data:rwX /var/www/perdu.com/www/uploads sudo setfacl -R -d -m u:www-data:rwX /var/www/perdu.com/www/uploads # 6. Sur les systèmes SELinux, ajuster les contextes sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/perdu.com(/.*)?" sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/perdu.com/www/uploads(/.*)?" sudo restorecon -Rv /var/www/perdu.com ``` Le principe directeur à retenir : **un groupe par site, l'écriture pour les mainteneurs, la lecture pour Apache, et l'écriture pour Apache uniquement là où l'application en a strictement besoin**. C'est la base d'un hébergement multi-sites propre, durable et raisonnablement sûr.