C'est quoi ton problème ?

Comme beaucoup de gens, pour transférer des fichiers sur un serveur web, j'utilise souvent FTP. Ce protocole possède plusieurs inconvénients :

  • il faut ouvrir plusieurs ports dans le pare-feu, au moins deux (connexion de contrôle et de données)
  • le mot de passe transite en clair sur le réseau, et même si on utilise FTPS, qui chiffre la partie authentification, tous les clients et serveurs ne le supportent pas ou de manière boguée (voir chez FileZilla pour une explication)
  • les données transitent en clair (mince, le fichier config.php de mon appli avec les codes d'accès à la base de données...)
  • gestion du NAT catastrophique (du moins avec Vsftpd)

Du coup, je cherche depuis plusieurs mois à éradiquer FTP de mes machines. Ce qui m'intéresse, c'est de pouvoir enfermer les utilisateurs dans une cage, de sorte qu'ils n'aient accès qu'à leurs données et pas à celles des autres, encore moins les autres fichiers et répertoires du serveurs. On appelle ceci un chroot. Je faisais déjà ceci avec Vsftpd, j'espérais donc le faire avec la solution de remplacement. D'ailleurs, cette solution de remplacement était déjà toute trouvée : je désirais utiliser le serveur SFTP contenu dans le très bon logiciel OpenSSH. Maintenant, il me fallait réussir à créer des utilisateurs en leur empêchant d'avoir accès au shell, et en les confinant dans un chroot.

Et t'as quoi comme solution ?

Pour enlever l'accès au shell, très facile : tout système Unix qui se respecte possède soit un exécutable nommé false, soit un autre nommé nologin. D'ailleurs ce dernier est très simpliste, regardons sur, au hasard, un système NetBSD 5.0.1 :

nils@tomb:~ $cat /sbin/nologin 
#! /bin/sh
echo "This account is currently not available."
exit 1

Il suffit donc de remplacer le shell de l'utilisateur par le chemin vers nologin, et cette question est résolue.

Pour créer et maintenir un chroot, c'est une autre paire de manches. Dans Vsftpd c'était assez simple, et j'espérais trouver aussi simple. De nombreuses pages sur Scponly ou rssh expliquent comment faire un chroot pour un utilisateur n'ayant accès qu'à sftp ou scp, mais le jour où il faut mettre à jour l'OS, voire le migrer vers une version majeure plus récente (ou pourquoi pas en changer, comme passer d'un Linux à un BSD ou inversement, ou tout simplement changer de distribution Linux), le chroot doit être maintenu à jour. Et ça, je trouve que c'est totalement contre-productif, en tous cas du point de vue du sysadmin fainéant que nous avons tous en nous ;)

Et là, la lumière est arrivée, par ici. Depuis la version 4.8, OpenSSH permet de créer des chroot, et n'oblige pas à recréer tout un environnement quand il s'agit de sftp. Exactement ce dont j'ai besoin ! Maintenant, reste à savoir quels systèmes disposent d'au moins OpenSSH 4.8.

Une petite liste non-exhaustive des systèmes chanceux :

  • NetBSD 5.0.1
  • FreeBSD 7.2
  • Debian Lenny
  • Mac OS 10.5.8

Une autre liste, mais de systèmes moins chanceux :

  • CentOS 3,4,5.X
  • RHEL 3,4,5.X
  • Debian Etch

Si votre système unix libre (ou pas, d'ailleurs, puisque j'ai listé Mac OS X) comporte OpenSSH, vous pouvez vérifier sa version par :

nils@darkmoon:~ $ssh -V
OpenSSH_5.1p1, OpenSSL 0.9.7l 28 Sep 2006

(exemple pris sur un mac) En utilisant sshd au lieu de ssh, ça sera sans doute plus représentatif, mais l'option -V n'existe pas sur le serveur. La réponse retournée donnera quand même la version. Exemple, toujours sur le même mac :

nils@darkmoon:~ $sshd -V
sshd: illegal option -- V
OpenSSH_5.1p1, OpenSSL 0.9.7l 28 Sep 2006
usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-f config_file]
            [-g login_grace_time] [-h host_key_file] [-k key_gen_time]
            [-o option] [-p port] [-u len]

Si votre système ne possède pas un OpenSSH assez récent, plusieurs possibilités s'offrent à nous :

  • changer de système
  • mettre à jour vers la dernière version majeure si celle-ci possède une version assez récente
  • installer sa propre version d'OpenSSH ou récupérer le paquet qu'aurait fait quelqu'un de généreux

La dernière solution est assez documentée pour CentOS et RHEL, pour faire ses propres RPM, mais j'ai décidé de ne pas la suivre, car cela pose le problème des mises à jour : de la même manière que maintenir un chroot ne me satisfait pas, passer mon temps à guetter les nouvelles versions d'OpenSSH pour compiler un paquet ne me plait pas plus. La deuxième solution s'avère sans doute la moins gênante selon les applications en production. De mon côté, j'ai choisi la première : migration de serveur dédié oblige, j'en ai profité pour élargir mes horizons dans le monde des unix libres et depuis quelques mois, ce blog tourne sous NetBSD. C'est donc avec cet OS que je vais décrire la manipulation de création de chroot.

On passe à l'action ?

Je pars du principe dorénavant que nous avons un système avec un OpenSSH 4.8 ou supérieur, que le serveur sshd est activé, que nous avons deux utilisateurs : root, et notre utilisateur habituel avec lequel nous faisons tout ce qui n'a pas besoin d'être fait en root. Le but est d'avoir un ou plusieurs utilisateurs supplémentaires, enfermés dans un répertoire défini, sans shell, et pouvant accéder à ce réperoire en sftp. On pourra, en supplément, faire en sorte que l'utilisateur accède à son compte sftp avec une clé (et éventuellement une phrase de passe) plutôt qu'un mot de passe.

Cela va se faire en modifiant dans un premier temps le fichier de configuration /etc/ssh/sshd_config (en tant que root, et le chemin peut varier selon le système). Cherchons la ligne contenant sftp-server, pour NetBSD elle ressemble à ceci :

Subsystem      sftp    /usr/libexec/sftp-server

On constate que le serveur sftp est un programme externe. Nous allons le remplacer par le sous-système sftp de sshd :

Subsystem       sftp    internal-sftp

J'ai donc remplacé /usr/libexec/sftp-server par internal-sftp. Allons ensuite à la fin du fichier, et ajoutons les directives suivantes :

Match Group wwwusers
        ChrootDirectory %h
        ForceCommand internal-sftp
        AllowTcpForwarding no
        X11Forwarding no

Cela signifie que pour les utilisateurs du groupe wwwusers, je les emprisonne dans leur répertoire home, je les oblige à utiliser le sftp interne, et je les empêche d'utiliser les différentes techniques de forwarding habituellement à disposition avec sshd. j'aurais pu les chrooter ailleurs, d'autres sites indiquent par exemple /chroot/%u, où %u désigne le nom de l'utilisateur. Une fois ces modifications effectuées, il ne reste qu'à redémarrer le serveur ssh et à créer le groupe et les utilisateurs.

Petit aparté concernant NetBSD 5.01 : j'ai remarqué un bug sur cette version, qui doit aussi être présent dans la 5.0; il ne faut surtout rien ajouter au fichier de configuration /etc/ssh/sshd_config après cette directive, pas même un commentaire ! Si cela venait à arriver, la directive que nous venons d'ajouter serait tout simplement ignorée.

Créons le groupe :

root@vhost:~ #groupadd wwwusers

Créons ensuite un utilisateur nommé test :

root@vhost:~ #useradd -m -g wwwusers -s /sbin/nologin test

Attribuons un mot de passe à cet utilisateur :

root@vhost:~ #passwd test
Changing password for test.
New Password:
Retype New Password:

(le mot de passe est tapé en aveugle, bien entendu). Ensuite, assurons-nous que le répertoire home de l'utilisateur appartient non pas à l'utilisateur mais à root, avec des permissions en 755. Si ce n'est pas le cas, on y remédie de cette manière :

root@vhost:~ #chown root:wheel /home/test
root@vhost:~ #chmod 755 /home/test

On notera que pour les OS Linux, on indique root:root par rapport à NetBSD qui n'a pas de groupe root mais un groupe wheel. A ne pas oublier aussi, seul /home/test appartient à root, pas les fichiers et répertoires à l'intérieur (i.e. pas de chmod/chown -R)

Depuis une autre machine, vérifions que nous pouvons nous connecter en sftp :

sftp test@vhost
Connecting to vhost...
Password:
sftp> ls
sftp> pwd
Remote working directory: /

Ici, on remarque que je me suis déjà connecté à cette machine avant, puisqu'on ne me réclame pas d'accepter de clé. On remarque aussi qu'on est directement dans le répertoire / et qu'il n'y a rien, la commande pwd indique / et pas /home/test. Si on crée dans ce répertoire un deuxième /home/test, sftp nous y emmènera directement dedans. De plus, on note qu'on ne peut pas créer ou ajouter de répertoire/fichier (normal, le répertoire appartient à root). Créons donc, sur le serveur, le second home de l'utilisateur :

root@vhost:~ #cd /home/test
root@vhost:/home/test # mkdir -p home/test
root@vhost:/home/test # chown -R test:wwwusers home
root@vhost:/home/test # chmod 755 home

Reconnectons-nous à notre serveur sftp :

sftp test@vhost
Connecting to vhost...
Password:
sftp> ls
sftp> pwd
Remote working directory: /home/test/

Je peux maintenant créer des répertoires, envoyer des fichiers, en rapatrier d'autres. Mission accomplie !