Comptes dynamique pour ProFTPd

  • 1 – intro
  • 2 – Les versions utilisés
  • 3 – Les services traités
  • 4 – Présentation de OpenLDAP
  • 5 – Stratégie d’organisation de la base
  • 6 – Mise en place de OpenLDAP
  • 7 – Mise en place de la structures de repertoires
  • 8 – Configuration de Proftpd

Intro

Le sujet traité par cet article, résulte del’agrandissement de ma société, suite a un rachat, nous passons de 30 postes à 70, il est temps de trouver un système d’administration plus aisé que les solutions traditionnelles pour gérer les utilisateurs et les différents services à base d’authentification.

Les versions utilisés

Je me sert d’un système LFS 4.0, mais les autres distributions fonctionnent de manière identique a quelques détails près, notamment au niveau de l’emplacement de fichiers de configuration. Pour Proftpd, j’utilise la version 1.2.7, compilé de la manière suivante:

./configure --prefix=/usr/local/Proftpd --with-modules=mod_ldap &&make && make install

L’option –prefix signifie que tous les fichiers relatifs à Proftpd seront rangés dans le répertoire /usr/local/proftpd le fichier de configuration sera donc dans le répertoire /usr/local/proftpd/etc/proftpd.conf.

si vous installez un paquetage rpm (pour redhat ou mandrake), le fichier sera probablement /etc/proftpd.conf, je ne connais pas les autres distributions.

Pour le serveur LDAP, j’utilise la version 2.1.12, compilé avec les commandes suivantes:

./configure --prefix=/usr/local/openldap --enable-syslog && make && make install

OpenLDAP sera intégralement installé dans le répertoire /usr/local/openldap, ces fichiers de configuration seront donc dans le répertoire /usr/local/openldap/etc/openldap, pour des distributions différentes, ils seront probablement dans /etc/openldap. L’option –enable-syslog, est active le système de log Syslog, elle n’est pas vraiment nécessaire dans le cadre de cet article, mais elle sert toujours et elle permet de centraliser ses logs.

J’utilise LDAP Browser afin d’administrer et gérer la base LDAP, je n’expliquerai pas ici son fonctionnement, mais je le trouve très utile. Le fait qu’il soit écrit en java, permet de le faire tourner sur de multiples plates-formes il est disponible a cette adresse: http://www.iit.edu/~gawojar/ldap

browser ldap

Il existe aussi GQ uniquement sous Linux http://biot.com/gq/

Les services traités:

  • La mise en place de la base LDAP et sa stratégie de gestion
  • La gestion des utilisateurs (uid, gid)
  • L’authentification ftp via LDAP avec Proftpd

Présentation de OpenLDAP

Une base LDAP permet avant tout de gérer des utilisateurs, elle est optimisée pour un accès rapide en lecture. Elle est donc idéale pour gérer ce genre de situations. Elle comprend des droits d’accès et une structure en « arbre », elle devient donc parfaite pour cacher une partie de ses entrées à certains types de services. On pourra ainsi séparer la liste du personnel de la liste des accès ftp.

Je vais tout d’abord détailler le service LDAP et son principe de fonctionnement:

Un annuaire LDAP est composé d’entités classées sous forme d’arbre, ce qui veut dire que l’on peut avoir un classement qui représente la hiérarchie de sa société ou bien une organisation des machines clientes. Chaque élément de cette hiérarchie est distingué par un dn (Distinguished Name) qui est un identifiant unique. Chaque élément possède des attributs qui sont piochés dans des schémas. Ces attributs contiennent les valeurs utiles: le nom de la personne, son adresse mail, etc.

L’annuaire LDAP fonctionne sur le modèle client serveur, son interrogation est donc faîte au travers de protocoles réseau basés sur TCP/IP. Cela implique que la base peut être consultée partout dans le monde via l’internet, ou rester interne à sa société, ou même être utilisée via des tunnels sécurisés entre plusieurs succursales.

Stratégie d’organisation de la base

Avant de commencer à mettre en place les services, il faut procéder à une analyse du problème afin de réfléchir à une structure d’annuaire.

Il faut pouvoir stocker la liste des utilisateurs afin de pouvoir affecter des propriétaires aux différents fichiers. Ces données devront pouvoir remplacer le fichier /etc/passwd il faut donc inclure les données suivantes:

  • un numéro uid
  • un numéro gid
  • un nom uid
  • un shell attribué à l’utilisateur
  • un répertoire home
  • un mot de passe crypté

Plusieurs choix sont possibles pour gérer l’accès FTP, soit on crée une liste d’utilisateurs destinés uniquement au service FTP, soit on décide que chaque utilisateur existant (la liste des utilisateurs) possède un attribut LDAP qui autorise le ftp à l’utilisateur.

Dans mon cas, la deuxième solution n’a aucun intérêt. En effet l’accès au serveur est destiné à certains clients et non aux usagers du réseau local, il faut donc entrer une liste d’utilisateur ftp. Pour ces utilisateurs, il faudra les données suivantes:

  • numéro uid
  • numero gid
  • nom uid
  • un répertoire home
  • un mot de passe crypté

Nous pouvons constater que les données sont quasiment les mêmes pour les utilisateurs et pour le service FTP. Pour organiser la base, nous créerons deux branches dans l’arbre, une que nous nommerons « People », pour les utilisateurs et « FTP » pour les comptes ftp.

Mise en place de OpenLDAP

Maintenant que nous connaissons la structure à mettre en place dans l’annuaire, nous pouvons choisir les schémas à utiliser. Les schémas sont les fichiers de définition des types de données stockées dans la base. Voila la liste des schémas fournis en standard avec OpenLDAP: corba.schema, core.schema, cosine.schema, inetorgperson.schema, java.schema, misc.schema, nis.schema, openldap.schema.

Voila les schémas que j’ai ajoutés à ma base LAP via le mot clé « include » dans le fichier slapd.conf.

include        /usr/local/openldap/etc/openldap/schema/core.schema
include        /usr/local/openldap/etc/openldap/schema/cosine.schema
include        /usr/local/openldap/etc/openldap/schema/inetorgperson.schema
include        /usr/local/openldap/etc/openldap/schema/nis.schema

Le schéma core et le schéma cosine, sont indispensables, la base refuse de démarrer sans ceux-ci et le schéma inetorgperson inclus des définitions interressantes pour l'organistaion de la base. Le schéma essentiel pour le projet, est le schéma nis, qui contient les définitions des comptes et groupes au standard posix. Voila par exemple la définition d’un compte utilisateur :

objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' SUP top AUXILIARY
  DESC 'Abstraction of an account with POSIX attributes'
  MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
  MAY ( userPassword $ loginShell $ gecos $ description )
)

Sa syntaxe est assez intuitive, objectclass est le mot clé pour définir un nouvel objet, l’objet est défini entre parenthèses. Le premier chiffre est un identifiant unique et standardisé, il est suivi du nom de l’objet (posixAccount). SUP indique les classes dont l’objet hérite. DESC est la description de l’objet, MUST liste les élément de l’objet qui sont indispensable au fonctionement, chaque élément est séparé par un « $ », on retrouve cn (Common Name) qui est le nom de la personne, uid (User IDentifier) est le login de la personne, uidNumber, est le numéro unique qui identifie la personne sur le système compatible Posix (présent dans /etc/passwd pour le système d’utilisateurs standard), gidNumber (Group Identifier), est le numéro du groupe auquel la personne appartient et homeDirectory est le répertoire utilisateur, dans lequel sera stocké les fichiers utilisateurs et les fichiers de configuration propre à l’utilisateur.

MAY liste les éléments facultatifs, le mot de passe (userPassword), le shell utilisé (loginShell), le gecos qui sert à stocker le nom complet de l’utilisateur et description contient une éventuelle description de l'entrée.

Il n’est pas indispensable que vous compreniez ces syntaxes pour la suite de l’article, les fichiers schémas sont fournis par OpenLDAP et contiennent tous les éléments couramment utilisés pour la construction d’une base LDAP.

Un service LDAP est fort aisé à mettre en place, après avoir compilé et installé le service, éditez le fichier slapd.conf, voila le mien :

#inclusions
#----------
include         /usr/local/openldap/etc/openldap/schema/core.schema
include         /usr/local/openldap/etc/openldap/schema/cosine.schema
include         /usr/local/openldap/etc/openldap/schema/inetorgperson.schema
include         /usr/local/openldap/etc/openldap/schema/nis.schema

#section generale
#----------------
pidfile         /var/run/slapd.pid
argsfile        /var/run/slapd.args
loglevel        -1
defaultaccess   read
sizelimit       1000
timelimit       60
schemacheck     on

#database
#--------
database        ldbm
cachesize       1000
dbcachesize     1000000
directory       /usr/local/openldap/var/lib/ldap
mode            0600
index           objectClass             eq
index           cn                      pres,eq,sub
index           dc                      pres,eq,sub
index           ou                      pres,eq,sub
index           uid                     pres,eq,sub
index           loginShell              pres,eq
index           uidNumber               pres,eq
index           gidNumber               pres,eq
index           homeDirectory           pres,eq
index           givenname               pres,eq,sub
suffix          "dc=test,dc=fr"

#controle d'access
#-----------------
rootdn          "cn=root,dc=test,dc=fr"
rootpw          secret

La section « inclusion », liste les fichiers de définition des schémas à inclure dans la base.

La section générale, liste les paramètres nécessaires au fonctionnement de la base.

Pidfile défini le fichier où sera stocké le pid (Process IDendifier) du service ldap.

Argsfile défini l’emplacement du fichier slapd.args. Loglevel défini les évènement à loguer, -1 permet de tout loguer, il est utile de positionner cette valeur à -1 durant l’installation du service, afin de pouvoir consulter toutes les erreurs. Defaultacces, donne des droits d’accès par défaut, defaultaccess=read, signifie que toute personne qui se logue anonymement sur la base, aura accès à toutes les données en lecture seule, laisser cette option sur read n’est pas une très bonne stratégie de confidentialité. Sizelimit, limite le nombre d’entrées de la base, une limite de 1000 pour une société de 70 personnes laisse une marge raisonable ;-). Timelimit, limite le temps d’une recherche, il est exprimé en secondes, concrètement si la recherche n’est pas satisfaite en 60 secondes, ldap renvoie une erreur indiquant un dépassement de temps. Schemacheck permet de vérifier l’intégrité des schémas au démarrage de la base, il sert à vérifier que les attributs entrées dans la base sont cohérents avec les schémas définis.

La section database fournit les paramètres utilisés par la base proprement dite.

Database définit le moyen de gestion et stockage de la base, ldbm est utilisé très couramment, cachesize indique la taille du cache en nombre d’entrées pour la base de données. dbcachesize indique la taille en octets du cache associée à chaque fichier d’index ouvert. Directory est le répertoire dans lequel la base sera stockée. Le mode défini les droits unix des fichiers de la base, mode=0600 indique de la base sera en lecture/ecriture uniquement pour le propriétaire (ldap). Les index, sont les champs susceptibles d’être demandés dans une recherche, le fait de les indexer accélère la recherche. Chaque nouvel index créera une nouvelle base classée par ordre d’index. Les valeurs pres, eq, sub, approx, déterminent le mode de recherche, eq optimise l’index pour une recherche basée sur l’égalité des termes, sub optimise pour les sous chaines, et approx pour l’approximation. Les personnes connaissant déjà bien LDAP, noteront peut être quelques incohérences dans les exemples, ceci est du au fait que j’ai masqué les infos propres à ma société ainsi que quelques personnalisations de la base que j’ai enlevé car elles n’entrent pas dans le cadre de cet article. Voila un « ls » des fichiers de la base:

[root@mail ldap]# ls -al /var/lib/ldap
total 740
drwx------    2 ldap     ldap         4096 jun 18  2002 .
drwxr-xr-x   10 root     root         4096 nov  7 20:05 ..
-rw-------    1 ldap     ldap       111656 fev 21 13:19 cn.gdbm
-rw-------    1 ldap     ldap        27605 fev 21 13:16 dn2id.gdbm
-rw-------    1 ldap     ldap        14972 fev 21 13:19 gidNumber.gdbm
-rw-------    1 ldap     ldap        64208 fev 21 13:19 givenName.gdbm
-rw-------    1 ldap     ldap        15832 fev 21 13:19 homeDirectory.gdbm
-rw-------    1 ldap     ldap        77755 fev 21 13:19 id2entry.gdbm
-rw-------    1 ldap     ldap        14692 fev 21 13:19 loginShell.gdbm
-rw-------    1 ldap     ldap        12296 fev 21 13:16 nextid.gdbm
-rw-------    1 ldap     ldap        14712 fev 21 13:19 objectClass.gdbm
-rw-------    1 ldap     ldap       106552 fev 21 13:19 uid.gdbm
-rw-------    1 ldap     ldap        16012 fev 21 13:19 uidNumber.gdbm

L’option suffix donne le dn de base de la base, il est courant de le faire correspondre au suffixe dns de sa société.

La dernière partie « contrôle d’access » donne aux utilisateurs des droits sur la base.

Rootdn est le dn de l’utilisateur root qui à accès a tout, rootpw est le mot de passe root, il est écrit en clair dans la base, il est donc conseillé de mettre des droit d’accès assez restreint au fichier de configuration. On peut tout de même crypter le mot de passe, il suffit d’utiliser la commande suivante (il faut avoir installé perl).

#perl -e "print crypt('passwd','aa');"
aaU3oayJ5BcR6

perl ou passwd représente le mot de passe et aa une chaîne de deux caractères quelconques. Il faudra ensuite mettre cette ligne dans le fichier de configuration:

rootpw      {crypt}aaU3oayJ5BcR6     #la representation du mot passwd

La valeur qui suit {crypt} est bien sur la valeur retournée par la commande précédente.

La section « contrôle d’accès » permet beaucoup plus de choses que je ne détaillerai pas. Elle permet notamment de déléguer l’administration de certaines parties de la base à d’autres utilisateurs.

Pour une société de 70 personnes sans succursales, un seul Admin est suffisant. Mes droits d’accès seront donc un accès en lecture seule pour tout le monde et tous les services et un accès complet pour root.

Il n’y a plus qu’à lancer la base, en général /etc/rc.d/init.d/ldap start, pour les utilisateurs lfs, il faudra créer le fichier de démarrage.

Si OpenLDAP ne se lance pas, reportez vous aux messages d’erreurs dans les journaux de logs.

Maintenant que la base est lancée, il faut ajouter des entrées, pour cela on utilise le format ldif. Voila le format d’un fichier ldif.

#création du suffixe, le dn de base
dn: dc=test,dc=fr
objectClass: top
objectClass: posixGroup
cn: test
gidNumber: 1000

# Création de People
dn: cn=People,dc=test,dc=fr
objectClass: top
objectClass: posixGroup
cn: People
gidNumber: 2000

# Création de FTP 
dn: cn=FTP,dc=test,dc=fr
objectClass: top
objectClass: posixGroup
cn: FTP
gidNumber: 3000

# entrée de utilisateur 1
dn: cn= utilisateur1,cn=People,dc=test,dc=fr
objectClass: Top
objectClass: posixAccount
cn: utilisateur1
uid: utilisateur1
uidNumber: 2001
gidNumber: 2000
userPassword: pass1
homeDirectory: /home/utilisateur1
loginShell: /bin/false

# entrée d'un access FTP
dn: cn=ftp1,cn=FTP,dc=test,dc=fr
objectClass: Top
objectClass: posixAccount
cn: ftp1
uid: ftp1
uidNumber: 3001
gidNumber: 3000
userPassword: motdepasse
homeDirectory: /home/ftp1
loginShell: /bin/false

Le fichier au format LDIF, permet de remplir la base. L’entrée primordiale, est la première, les deux suivantes créent deux branches de l’arbre, les autres sont les entrées utilisateurs et comptes ftp.

On retrouve dans le fichier les noms définis plus haut dans les schémas.

Chaque entrée du fichier est séparée par une ligne vide. Le premier champ est le dn, il doit être composé d’un attribut de l’entrée en général le cn mais cela reste libre, il doit être suivi de tout son chemin d’accès, ce qui permet de définir l’arbre:

dn: dc=test,dc=fr
+- dn: cn=People,dc=test,dc=fr | +- dn: cn=utilisateur1,cn=People,dc=test,dc=fr | +- dn: cn=FTP,dc=test,dc=fr +- dn: cn=FTP1,cn=FTP,dc=test,dc=fr

Il faut ensuite lister les objets utilisés par l’entrée dans notre cas ils seront top, et posixGroup pour les branches de l’arbre et top et posixAccount pour les utilisateurs.

Il faut ensuite renseigner les attributs choisis pour chaque entrée.

Pour entrer ces données dans la base ldap, il faut que la base soit lancée, ensuite les données pourront être entrées à l’aide de la commande ldapadd avec cette syntaxe:

ldapadd -x -h 127.0.0.1 -w secret -D cn=root,dc=test,dc=fr -f fichier.ldif

Le paramètre « -x » est utilisée pour accéder à la base sans cryptage. Le paramètre « -h » donne l’adresse du serveur ldap à remplir. Le « -w » donne le mot de passe de la base, il est utile dans les scripts, si vous tapez les commandes sous les yeux attentifs d'une assemblée remplacez le par le paramètre « -W » (majuscule) car il propose une invite pour taper le mot de passe. Le « -D » spécifie de dn de l’utilisateur root et le « -f » indique le fichier ldif a utiliser.

Mise en place des propriétaires des fichiers

Maintenant que nous avons entré les utilisateurs, il nous reste à les faire reconnaître par le système. En effet si l’utilisateur ftp1 se connecte au serveur ftp et qu’il uploade quelques fichiers, le fichier aura comme propriétaire 3001:3000 au lieu de ftp1:FTP. Pour que le système reconnaisse les utilisateurs stockés dans la base ldap, nous modifierons le fichier /etc/nsswitch.

Il faut faire attention en modifiant ce fichier, car une erreur de manipulation peut nuire au fonctionnement du serveur.

Voila les lignes à modifier dans le fichier /etc/nsswitch.conf:

passwd: files ldap
group: files ldap
shadow: files ldap

Il suffit de rajouter ldap derrière chacune de ces lignes. Ceci indiquera au système de gestion des propriétaires qu’il faut rechercher l’utilisateur dans les fichiers (respectivement /etc/passwd, /etc/group et /etc/shadow) puis si aucun uid n’est trouvé, il doit regarder dans la base ldap. Il faut vérifier que la bibliothèque qui permet le fonctionnement de nss avec ldap est bien installée, elle est généralement présente dans /lib, elle porte le nom de libnss_ldap.xxx, si ce fichier est présent, tout va bien, s’il n’est pas la, il faut installer le package libnss_ldap, procurez vous le et installez le, avec un système rpm ou deb. Avec une lfs, récupérez l’archive, décompressez la,puis compilez la (./configure -prefix=/usr/local/nss-ldap && make && make install), il faut ensuite créer un lien symbolique de /usr/ocal/nss-ldap/lib/libnss_ldap.xxx dans /lib.

Il reste juste à renseigner la bibliothèque sur l’emplacement du serveur ldap. Le fichier est en général /etc/ldap.conf, dans le cas de la lfs, il est situé dans /usr/local/nss-ldap/etc/ldap.conf, il suffit de mettre ces deux lignes à l’intérieur:

host 127.0.0.1
base dc=test,dc=fr

Si tout ceci a été effectué sans erreurs, vous pourrez taper les commandes suivantes pour tester le système:

touch test               #création d'un fichier quelconque de test
chown ftp1:FTP test      #si vous n'avez pas d'erreur, le proprietaire du fichier de
                         #test, est un utilisateur declar�dans la base ldap.

vous faîte un ls -ail, vous verrez :

# ll
total 0
-rw-r--r--    1 ftp1     FTP             0 Mar  1 20:15 test

Mise en place de la structures de repertoires:

Avant d’installer Proftpd, il manque a créer les répertoires qui seront utilisés par le serveur ftp pour stocker les données à partager. Pour cela, je vous propose ce script largement commenté.

Le lancement de ce script, crée les répertoires utilisés par le serveur ftp, si le répertoire existe déjà, il ne sera pas créé. Il ajuste ensuite les droits d’accès à ces répertoires:

#!/bin/sh
#cette ligne recherche toutes les entrées de la branche ftp, elle demande uniquement
#le paramètre homeDirectory, un grep filtre la réponse et ne laisse apparaître que les
#lignes commençant par «homeDirectory:», on obtiendra donc la ligne suivante:
#   homeDirectory: /home/ftp1
#on entame un boucle, pour laquelle à chaque tour
#$i sera remplacé par un nouveau
#répertoire a créer
for i  in `ldapsearch -x -b "cn=FTP,dc=test,dc=fr" homeDirectory | grep homeDirectory: | awk '{print $2 }'`
do
  #on verify que le repertoire n’existe pas
  if [ ! -e /home/$i ]; then
    #on crée le répertoire
    echo "Creation de $i"
    mkdir $i
    #on va rechercher l’uid
    USER=`ldapsearch -x -b "cn=FTP,dc=test,dc=fr" homeDirectory=$i uid | grep uid | awk '{print $2 }'`
    #on change l’user pour tout le repertoire
    chown $USER:FTP $i
  fi
done

configuration de Proftpd

Voila, il ne nous reste plus qu’à configurer Proftpd, si la compilation c’est bien passée, il suffit d’éditer le fichier de configuration proftpd.conf, voila le mien:

ServerName              "test file server"
ServerType              standalone
ServerAdmin             admin@test.fr

DeferWelcome            off
DefaultServer           on

Port                    21
Umask                   022

User                    ftp
Group                   ftp

<Directory /*>
   AllowOverwrite  on
</Directory>

MaxInstances            10
UseReverseDNS           off
IdentLookups            off
TimeoutStalled          300

ScoreboardFile          /var/run/Proftpd.pid
TransferLog             /var/log/Proftpd.xfer_log
DefaultRoot             ~/
MaxLoginAttempts        3

LDAPServer              "127.0.0.1"
LDAPDoAuth              on "cn=FTP,dc=test,dc=fr"

J’ai volontairement simplifié le fichier, car la configuration de Proftpd n’est pas l’objet de l’article. J’ai juste laissé ce qui est nécessaire à son fonctionnement. Les paramètres qui nous intéressent sont: LDAPServer et LDAPDoAuth.

LDAPServer donne l’adresse du serveur ldap utilisé pour l’authentification.

L’option suivante est bien plus intéressante, c’est celle qui choisit la liste des utilisateurs à authentifier, ainsi que d’éventuels filtres de recherche. En mettant « on "cn=FTP,dc=test,dc=fr" » le serveur effectuera des recherches uniquement dans la branche FTP de l’annuaire, c'est-à-dire que la liste des utilisateurs (dans la branche People) sera ignorée lors de la validation des accès ftp autorisés. Si l’on met « on "dc=test,dc=fr" », tous les utilisateurs pourrons se connecter au serveur au même titre que les comptes ftp.

Cette commande peut inclure des filtres de recherche, ceci est utile par exemple pour accorder un accès ftp a certains utilisateurs et pas d’autres. Si l’on crée un nouvel attribut pour chaque utilisateur dans la base ldap du nom de allowFtp, on lui alloue une valeur booléenne (vrai ou faux). On met « vrai » pour tous les utilisateurs authorisés à se connecter, « faux » pour les autres. Il ne reste plus qu’à rajouter un filtre dans le paramètre « LDAPDoAuth on "dc=test,dc=fr" "(allowFtp=true)" », le serveur recherchera uniquement les personnes dont l’attribut autorise la connexion.

Il ne reste plus qu’à lancer le serveur, et à le tester.

Voila, l’annuaire LDAP utilisé avec Proftpd doit être fonctionnel (il fonctionne très bien chez moi). Il faut savoir que LDAP peut être utilisé avec une multitude d’autres services, il peut se coupler avec samba (très intéressant pour gérer un domaine, cette technique se rapproche du système Ms active directory), il peut aussi être utilisé avec Sendmail pour attribuer une ou plusieurs adresses mail aux usagers. Il sert également avec pam pour les authentifications en tous genres. Et pour finir, il existe des bibliothèques pour quasiment tous les langages de programmation.