Depuis quelques temps j'essayais sans succès de faire des utilisateurs virtuels avec Vsftpd, mon logiciel de serveur ftp favori, sous CentOS 5. Alors oui, la db au format Berkeley, ça marche, mais je trouve ça casse-pieds à maintenir. Et puis pour changer le mot de passe, galère. J'avais vu qu'il était possible d'utiliser MySQL comme base pour les utilisateurs et leurs mots de passe. Je me met en quête d'un how-to pour CentOS, sans succès. J'adapte donc ce how-to de Howtoforge pour CentOS.

Commençons par l'installation des paquets qui vont bien. En supposant que vous ayez, comme moi, une CentOS 5 minimaliste mais à jour, ça se passe comme ceci :

root@tristram:~ # yum -y install vsftpd mysql-server

Ensuite, soit on ajoute à ses dépôts le dépôt extras en mode testing (et là je vous encourage à faire très attention, et de n'activer que les noms des paquets nécessaires), soit on installe "à la main" le paquet pam-mysql, qui permettra à vsftpd de dialoguer avec MySQL. Le RPM est disponible sur Pbone. Moi j'ai fait :

 root@tristram:~ # wget ftp://ftp.pbone.net/mirror/centos.karan.org/el5/extras/testing/i386/RPMS/pam_mysql-0.7-0.5.rc1.el5.kb.2.i386.rpm

puis :

root@tristram:~ # rpm -ivh pam_mysql-0.7-0.5.rc1.el5.kb.2.i386.rpm

Une fois les logiciels qui vont bien installés, on peut avoir envie de gérer MySQL via phpMyAdmin, pour celà je vous renvoie à un autre billet qui en parle.

Commençons par MySQL, pour respecter l'ordre originel du howto. Une fois celui-ci installé, on configure le mot de passe de root :

root@tristram:~ # service mysqld start

MySQL indique les commandes pour changer le mot de passe de root pour MySQL, en indiquant quel est le nom d'hôte MySQL de la machine (détail très important !)

root@tristram:~ # /usr/bin/mysqladmin -u root password 'changemoi'
root@tristram:~ # /usr/bin/mysqladmin -u root -h tristram.anotherhomepage.loc password 'changemoi'

(on voit donc que la machine servant à ce howto se nomme tristram.anotherhomepage.loc) Ensuite on se connecte à MySQL :

root@tristram:~ # mysql -u root -p

On crée la base de données et son utilisateur, vsftpd et mot de passe ftpdpass :

mysql> CREATE DATABASE vsftpd;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON vsftpd.* TO 'vsftpd'@'localhost' IDENTIFIED BY 'ftpdpass';
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON vsftpd.* TO 'vsftpd'@'localhost.localdomain' IDENTIFIED BY 'ftpdpass';
mysql> FLUSH PRIVILEGES;

Ensuite on créé le schéma (on est toujours dans le shell de MySQL) :

mysql> USE vsftpd;
mysql> CREATE TABLE `accounts` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` VARCHAR( 30 ) NOT NULL ,
`pass` VARCHAR( 50 ) NOT NULL ,
UNIQUE (
`username`
)
) ENGINE = MYISAM ;

Et on quitte MySQL :

mysql> quit;

On créée l'utilisateur virtuel pour accéder aux comptes ; sous CentOS 5, le groupe de l'utilisateur nobody est nobody, avec comme gid 99 (vu dans /etc/groups) :

root@tristram:~ # useradd --home /home/vsftpd --gid 99 -m --shell /sbin/nologin vsftpd

On note aussi que pour empêcher un compte d'avoir un shell, on met plutôt /sbin/nologin.

Passons à Vsftpd. Sauvegardons la configuration par défaut et ajoutons la nôtre :

root@tristram:~ # cp -p /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf_orig
root@tristram:~ # cat /dev/null > /etc/vsftpd/vsftpd.conf
root@tristram:~ # vi /etc/vsftpd/vsftpd.conf

Le fichier vsftpd.conf est le suivant ( les options sont expliquées en anglais sur le site de vsftpd) :

listen=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
nopriv_user=vsftpd
chroot_local_user=YES
secure_chroot_dir=/var/run/vsftpd
pam_service_name=vsftpd
guest_enable=YES
guest_username=vsftpd
local_root=/home/vsftpd/$USER
user_sub_token=$USER
virtual_use_local_privs=YES
user_config_dir=/etc/vsftpd/user_conf

Une première différence avec celui de Howtoforge, je n'ai pas mis l'option rsa_cert_file=/etc/ssl/certs/vsftpd.pem, je verrai ça pour un autre billet. Une autre différence est l'endroit où je stocke les configurations personnalisées par utilisateur : comme il y a un répertoire /etc/vsftpd, j'ai créé un sous-répertoire user_conf :

root@tristram:~ # mkdir /etc/vsftpd/user_conf

Cette possibilité est bien entendue totalement optionnelle.

Il nous faut maintenant configurer pam, qui va permettre à vsftpd d'aller chercher les utilisateurs dans la base mysql plutôt que dans les utilisateurs système, stockés dans /etc/passwd et /etc/shadow. Comme avec Vsftpd, on sauvegarde l'ancien et on en crée un tout neuf :

root@tristram:~ # cp -p /etc/pam.d/vsftpd /etc/pam.d/vsftpd_orig
root@tristram:~ # cat /dev/null > /etc/pam.d/vsftpd
root@tristram:~ # vi /etc/pam.d/vsftpd

Le contenu de ce fichier est le suivant :

auth required pam_mysql.so user=vsftpd passwd=ftpdpass host=localhost db=vsftpd table=accounts usercolumn=username passwdcolumn=pass crypt=3
account required pam_mysql.so user=vsftpd passwd=ftpdpass host=localhost db=vsftpd table=accounts usercolumn=username passwdcolumn=pass crypt=3

La différence avec la version howtoforge est que j'ai changé l'algorithme de hash du mot de passe. Au lieu d'utiliser la fonction PASSWORD(), je vais utiliser MD5(). Je reviendrai sur ce qui a motivé ce choix après. Pour le moment, relançons Vsftpd :

root@tristram:~ # service vsftpd restart

Et maintenant, créons notre premier utilisateur dans MySQL :

root@tristram:~ # mysql -u root -p

Nous sommes dans le shell MySQL :

mysql> USE vsftpd;
mysql> INSERT INTO accounts (username, pass) VALUES('testuser', MD5('secret'));
mysql> quit;

Le répertoire de l'utilisateur testuser est /home/vsftpd/testuser, mais Vsftpd ne peut pas le créer automatiquement pour nous, faisons-le à la main, en prenant soin qu'il appartient bien à vsftpd :

root@tristram:~ # mkdir /home/vsftpd/testuser
root@tristram:~ # chown vsftpd:nobody /home/vsftpd/testuser

Connectons-nous à notre serveur FTP en utilisant Filezilla sous Windows, Konqueror ou gFTP (ou bien en ligne de commande, ftp ou lftp) sous Linux/BSD, ou encore Cyberduck sous Mac OS X. Ca marche? Parfait :-)

Maintenant le pourquoi du comment que j'ai mis 3 au lieu de 2 et MD5 au lieu de PASSWORD : tout simplement parce que ça ne fonctionne pas sous CentOS 5. L'explication vient du fichier README de pam-mysql, dispo là : /usr/share/doc/pam_mysql-0.7/README

The method to encrypt the user's password:

0 (or "plain") = No encryption. Passwords stored in plaintext. HIGHLY DISCOURAGED.

1 (or "Y") = Use crypt(3) function.

2 (or "mysql") = Use MySQL PASSWORD() function. It is possible that the encryption function used by PAM-MySQL is different from that of the MySQL server, as PAM-MySQL uses the function defined in MySQL's C-client API instead of using PASSWORD() SQL function in the query.

3 (or "md5") = Use plain hex MD5.

4 (or "sha1") = Use plain hex SHA1.

La fonction PASSWORD de MySQL et celle de pam-mysql ne renvoient donc pas le même hash de mot de passe. Dommage, hein? J'ai aussi essayé l'option 0, mais elle ne m'intéressait pas. Je n'ai pas encore essayé la fonction crypt ni la fonction sha1 pour vérifier si elles fonctionnent, mais il n'y a pas de raison ;)

Il ne reste à présent qu'à créer une page php ou un script shell qui permette de créer, modifier et effacer les utilisateurs.