NFS - Network File System

ArticleCategory:

System Administration

AuthorImage:

TranslationInfo:

Original in fr Frédéric Raynal

AboutTheAuthor:

Frédéric Raynal prépare une thèse en informatique sur le tatouage d'images à l'INRIA. Il lit un très bon roman policier qui met en scène Th. Roosevelt au début du siècle alors qu'il était préfet de police. L'ambiance est très sombre. Il s'agit d'une enquête d'un groupe de personnes pour retrouver un tueur en série qui s'attaque à des enfants. Ce groupe s'appuie sur des techniques nouvelles (psychologies, empreintes digitales, etc...) pour parvenir à ces fins. Ce roman de de Caleb Carr, L'ange des ténèbres, dresse un portrait surprenant du début du siècle.

Abstract:

Le Network File System (NFS) permet de gérer des fichiers distribués sur plusieurs ordinateurs d'un réseau comme s'ils étaient sur un disque dur local. Ainsi, l'utilisateur n'a plus à se soucier de savoir où sont physiquement ses fichiers pour y accéder.

ArticleIllustration:

ArticleBody:

Introduction

NFS permet très simplement de partager des données entre plusieurs machines. Par exemple, un utilisateur qui se connecte sur un réseau n'aura plus à se logger sur une machine précise : via NFS, son home directory lui sera livré (on dit en fait exporté) sur la machine où il se connecte.

NFS n'est toutefois pas un protocole très performant et n'est pas utilisable de manière confortable au travers une connexion par modem. En revanche, sur un réseau local, son utilisation offre une grande souplesse tant aux utilisateurs qu'aux administrateurs.

Il faut néanmoins prendre quelques précautions par rapport à ce service. En effet, permettre à n'importe qui d'écrire des données sur son réseau n'est pas franchement conseillé ;-) Certaines mesures indispensables limitent les risques.

Cet article commence donc par une très brève introduction sur les systèmes de fichiers. Ensuite, nous verrons le fonctionnement du protocole NFS. La partie suivante, moins théorique, détaillera l'installation d'un serveur et d'un client NFS, ainsi que les précautions minimum à prendre. Enfin, nous illustrerons dans un exemple, comment combiner NFS, NIS et autofs.
 

Présentation générale et non-exhaustive de la notion de système de fichiers

Avant de parler de NFS, il est nécessaire de bien comprendre la notion de système de fichiers. Ce terme désigne la manière dont les données sont stockées sur un support, dont elles sont organisées et gérées. Il en existe toute une pléthore, certains plus utilisés que d'autres (New Technology FileSystem (NTFS), High Performance FileSystem (HPFS), DOS, FAT 12/16/32, VFAT, Macintosh Hierarchical Filesystem (HFS), ISO 9660 (pour les CD-ROM), extended filesystems (Ext, Ext2, Ext3), et encore de nombreux autres).

Par exemple, on peut envisager tout support physique de données (un disque dur par exemple) comme un suite de petites cases contenant des informations : on parle de blocs (blocks en Anglais). Chaque système de fichiers gère ces blocs différemment. Par exemple, dans la figure 1 , on cherche à insérer un fichier tenant sur 2 blocs. Dans l'illustration supérieure, le fichier a été placé après le dernier bloc occupé, laissant des espaces vides au début. A l'inverse, dans le schéma inférieur, il a été placé au premier endroit pouvant le contenir. Si le terme "fragmentation" vous évoque quelque chose, vous savez maintenant ce qu'il représente ;-)
 
 


Fig. 1 : 2 politiques différentes pour insérer des blocs








Le système de fichiers le plus commun sous Linux s'appelle ext2fs (extended 2 file system). Chaque fichier y est représenté par un inode1. Les répertoires contiennent des listes de fichiers, l'accès aux devices se fait par l'intermédiaire d'opérations de lecture/écriture sur des fichiers particuliers.

Le rôle d'un serveur NFS est donc de distribuer à ses clients les inodes des fichiers auxquels ils veulent accéder. Toutefois, le client ne pourrait fonctionner correctement s'il ne recevait que l'inode de ces fichiers! Un serveur NFS offre donc une couche réseau supplémentaire pour permettre à des machines distantes de manipuler ses inodes.
 

Le protocole NFS

Ce que nous appelons communément NFS se compose en fait de 4 protocoles distincts. Chacun repose sur les Remote Procedure Calls (RPC) et donc portmap (aussi appelé rpc.portmap). Rappelons que ce programme convertit les numéros de programmes RPC en numéros de ports. Quand un serveur RPC démarre, il va préciser à portmap quel port il utilisera et les numéros de programmes RPC qu'il gère. Quand un client souhaite envoyer une requête RPC vers un numéro de programme donné, il contacte d'abord le serveur portmap pour obtenir le numéro de port sur lequel tourne le programme souhaité. Ensuite, il adresse les paquets RPC au port correspondant.

Les 4 services permettant à NFS de fonctionner sont :
 
 
Protocole
Description
Démon
nfs Ce protocole est la base qui permet la création de fichier, leur recherche, leur lecture ou leur écriture. Ce protocole gère donc également l'authentification  et les statistiques sur les fichiers.
nfsd
mountd Celui-ci s'occupe du montage des systèmes exportés auxquels on accédera par nfs. Il envoie donc des requêtes de type mount et umount au serveur, qui doit donc conserver des informations sur les systèmes de fichiers exportés (voir section sur XXX).
mountd
nsm
(Network Status Monitor) 
Il sert à monitorer les noeuds du réseau pour connaître l'état d'une machine (cliente ou serveur) pour signaler, par exemple, qu'elle redémarre.
statd
nlm
(Network Lock Manager) 
Pour éviter que des données soient altérées par plusieurs clients en même temps, ce protocole gère un système de locks (serrure ou fermeture en Anglais) qui permettent de signaler les systèmes de fichiers utilisés. Ainsi, à l'aide du protocole nsm qui sait quand un client redémarre, il libère tous les locks du client avant de les lui restituer si une nouvelle requête est émise.
lockd

Le démon knfsd, disponible avec les dernières versions du noyau, supporte directement les protocoles nfs et nlm. En revanche, mountd et nsm ne le sont pas encore. Une fois le serveur NFS installé et démarré, on peut vérifier que tout fonctionne ainsi :
 

>> ps auxwww | egrep "nfs|mount|lock|stat"
root      1370  0.0  0.2  1176  580 ?        S    22:28   0:00 rpc.mountd --no-nfs-version 3
root      1379  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1380  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1381  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1382  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1383  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1384  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1385  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1386  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [nfsd]
root      1399  0.0  0.0     0    0 pts/0    SW   22:28   0:00 [lockd]
root      1409  0.0  0.2  1156  560 ?        S    22:28   0:00 rpc.statd
root      1652  0.0  0.1  1228  484 pts/3    S    22:49   0:00 egrep nfs|mount|lock|stat
Il existe actuellement 2 versions de NFS (versions 2 et 3 - que nous noterons respectivement NFSv2 et NFSv3 quand nous voudrons les différencier). Le serveur NFS de Linux ne supporte pour l'instant que la version 2 (d'où l'option sur la ligne du mountd de l'exemple précédent).

L'objet au centre de toutes les préoccupations de NFS s'appelle un file handle. Il s'agit d'une suite de bits relativement ésotérique permettant de désigner de manière unique chacun des objets du systèmes de fichiers (donc un fichier entre autre, mais pas uniquement). Il contient par exemple, l'inode du fichier, mais également un fichier représentant le device où se trouve ce fichier. On peut donc voir NFS comme un système de fichiers qui en encapsule un autre.

Installation

Le serveur

La toute première chose à faire, comme nous l'avons vu, est de démarrer portmap puisque les protocoles nécessaires à l'utilisation de NFS se servent des RPCs.
root >>/usr/sbin/rpcinfo -p
rpcinfo: can't contact portmapper: RPC: Remote system error - Connection refused
root >>/sbin/portmap
root >>/usr/sbin/rpcinfo -p
   program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
La commande rpcinfo permet de connaître les services RPC qui fonctionnent sur la machine spécifiée en argument (l'option -p). On constate donc que portmap ne tourne pas encore : on le démarre (la plupart des distributions de Linux mettent en place des scripts qui permettent de faire ceci de manière automatique au démarrage de la machine) et on vérifie qu'il fonctionne correctement. Une autre possibilité expliquant la réponse négative reçue suite à l'appel de rpcinfo est tout simplement l'interdiction faite au portmapper de répondre, par l'entremise des fichiers /etc/hosts.{allow, deny}.

Avant de démarrer NFS proprement dit, il faut le configurer. Le seul fichier de configuration s'appelle /etc/exports. Chaque ligne contient l'emplacement à exporter suivi d'une liste de clients autorisés à y accéder . Il est possible, voire indispensable, de rajouter des options à la suite de chaque nom de client. La page man exports décrit clairement les syntaxes valides pour les noms de clients et les options.

Les formulations acceptées pour les noms des clients sont :

Nous ne détaillerons pas ici toutes les options de montage possibles, mais voici les plus importantes : Il reste maintenant à démarrer les démons rpc.mountd et rpc.nfs pour avoir un serveur NFS en place. On vérifie que tout tourne correctement toujours avec la commande rpcinfo. On peut également initialiser les serveurs pour les protocoles nsm et nlm (respectivement rpc.statd et rpc.lockd). Ils ne sont pas indispensables au fonctionnement d'un serveur NFS ... mais fortement conseillés au cas où une machine tomberait en panne, rebooterait intempestivement, etc...

Lorsqu'on modifie le fichier de configuration /etc/exports, il faut signaler aux démons concernés que des changements se sont produits. La commande exportfs transmet ces informations aux serveurs. L'option -r synchronise le fichier /etc/mtab2  avec le fichier /etc/exports. L'option -v permet de connaître les systèmes de fichiers exportés avec leurs options.

Une fois que tout est en place, certains fichiers contiennent des informations importantes :

Quand un client souhaite accéder à un système de fichiers, il commence par le demander à mountd. Celui-ci recherche alors dans etab si la requête est accessible. Il vérifie également auprès du noyau que le client a légitimement le droit de présenter cette requête (contrôle des hosts.{allow, deny}, règles de pare-feu, ...). Le noyau emploie exportfs pour cette vérification, ce qui lui permet en même temps de mettre à jour le fichier /var/lib/nfs/etab. Si, dans ce fichier,  le système exporté est destiné à un groupe auquel appartient le client, mountd restreint alors la requête au client. et en informe le noyau qui met à jour xtab avec ce nouvel hôte.

Le client

Rien à faire ... en général. L'accès à un système de fichiers exporté par NFS est directement géré à partir du noyau qui sait comment accéder à des données sur un support physique via les systèmes de fichiers adéquates. Pour connaître ceux supportés par votre noyau, le répertoire /lib/modules/<version du noyau>/fs contient tous les modules liés aux systèmes de fichiers. Le fichier /proc/filesystems liste tous ceux supportés directement dans le noyau. Il faut donc uniquement préciser au noyau qu'on souhaite accéder à un système exporté par NFS.

La commande mount permet d'accéder à différents systèmes de fichiers. Elle signale au noyau qu'un nouveau système de fichier est utilisable en indiquant son type, son device et un point de montage. L'option -t  permet de préciser le système auquel un client veut accéder. Seuls les modules évoqués ci-dessus sont reconnus par le noyau. Pour un système NFS, l'argument s'écrit donc : -t nfs.

mount dispose d'options propres à NFS. Par exemple, les options rsize et wsize permettent de modifier les tailles des blocs en lecture et en écriture. On peut combiner les options spécifiques à NFS avec des options plus générales, comme intr, noexec ou nosuid. La page man de mount contient toutes ces options.

Supposons que la machine charly dispose d'un serveur NFS et exporte son répertoire /usr/local. Nous souhaitons y accéder de la machine jill, il suffit alors de monter le répertoire exporté de charly sur jill :

root@jill >> mount -t nfs -o nosuid,hard,intr charly:/usr/local /usr/local
La commande indique donc que nous allons monter un système de fichiers de type NFS (-t nfs), avec les options nosuid, hard et intr. Les 2 derniers arguments sont les plus intéressants. Le premier spécifie le device qui doit être monté. Dans le cas de NFS, la syntaxe diffère de l'usage habituel de mount. En effet, on commence par stipuler un serveur, puis un répertoire de ce serveur (Attention : un répertoire n'est normalement pas un device). Le dernier argument indique l'endroit auquel correspond ce système de fichiers sur le client. On vient donc de partager le /usr/local de charly avec jill, ce qui évite d'avoir à installer plusieurs fois certains programmes. Pour que ceci soit établi de manière permanente, on peut également le spécifier dans le fichier /etc/fstab de jill qui contient tous les devices à installer au démarrage. Ainsi, de manière équivalente, le fichier /etc/fstab contiendrait la ligne :
 
#    device           point de     système de    options          dump  fsckorder
#                     montage      fichiers
charly:/usr/local    /usr/local       nfs       nosuid,hard,intr    0      0

Les précautions

Un problème fondamental de NFS vient du fait qu'il existe une relation de confiance, par défaut, entre un client et un serveur NFS. Dans ce cas, si le compte root du serveur est compromis, celui du client le sera également. Le NFS-HOWTO décrit un ensemble de mesures élémentaires nécessaires à prendre.

Un client ne peut croire aveuglément un serveur, il faut donc préciser des options contraignantes lors de l'utilisation de la commande mount. Nous avons déjà vu la première : nosuid. Elle annule l'effet des bits SUID et SGID. Ainsi, une personne root sur le serveur doit se connecter d'abord en tant qu'utilisateur quelconque sur le client pour ensuite seulement redevenir root. Une autre option, plus contraignante, est noexec. Elle interdit l'exécution des programmes contenus sur le système exporté. Cette option n'est utilisable que pour les systèmes contenant uniquement des données.

Du côté du serveur NFS, on peut également spécifier qu'on ne fait pas confiance au compte root des clients. On doit alors préciser dans le /etc/exports l'option root_squash. Ainsi, si un utilisateur avec l'UID 0 (celui de root) sur le client accède au système exporté par le serveur,  il se voit attribuer l'UID de nobody pour effectuer les requêtes sur les fichiers. Cette option est active par défaut sous Linux mais s'annule par l'option no_root_squash. Notons qu'on peut préciser une plage d'UID pour lesquels l'option s'applique. Rappelons également que les options anonuid et anongid permettent de changer l'UID/GID de l'utilisateur nobody en celui souhaité.

Certaines mesures sont d'un ordre plus général et concernent plutôt le portmapper. Par exemple, on interdit l'accès à ce service à toutes les machines par la ligne suivante dans le fichier /etc/hosts.deny :
 

# hosts.deny : interdiction absolue pour tout le monde de
#              se servir du portmap
portmap: ALL


Ensuite, dans le /etc/hosts.allow, on contrebalance cette interdiction drastique en autorisant l'accès à toutes les machines souhaitées.

Des règles de firewalling adéquates contribuent également à une meilleure protection. Il faut prendre garde aux ports employés par les divers services ainsi qu'aux protocoles utilisés :
 

Service RPC Port Protocoles
portmap 111 upd / tcp
nfsd 2049 udp
mountd variable udp / tcp

Application : NIS, NFS et autofs

Pour illustrer notre propos, reprenons le réseau mis en place lors du précédent article sur NIS. Le serveur principal s'appelle "charly", les 3 autres machines du sous-réseau sont "sabrina", "jill" et "kelly". Nous avons configuré charly comme serveur NIS du domaine bosley. Les autres machines sont uniquement des clients NIS de charly (en pratique, nous devrions avoir un serveur NIS esclave, mais là n'est pas le propos aujourd'hui).

Voyons d'abord la configuration sur notre serveur charly. Nous commençons par définir quelques maps NIS qui contiendront toutes les informations dont nous avons besoin.

Le fichier /etc/netgroup contient des groupes de machines ayant des caractéristiques communes (une même architecture par exemple). Il s'agit d'une map de NIS très pratique à utiliser pour NFS. Il suffit juste de rassembler dans un groupe toutes les machines pouvant accéder à un même système de fichiers exporté. Ce groupe sert ensuite dans le /etc/exports plutôt que de préciser tous les client un à un :

# /etc/netgroup
charlysangels (sabrina,,) (jill,,) (kelly)
 Concernant NFS, nous savons que la configuration est assez restreinte. Le fichier /etc/exports de charly contient :
# /etc/exports
/usr/local    @charlysangels(ro)
Par ailleurs, nous décidons d'utiliser l'automount pour accéder au répertoire /usr/local ainsi exporté. En effet, plutôt que de monter directement ce système au boot, ce sera fait automatiquement si un utilisateur accède à un fichier de ce répertoire.  Nous créons le fichier /etc/auto.map pour définir ce qui sera accessible à la fois par automount et par NIS :
# /etc/auto.map
charly          charly:/usr/local
Comme nous voulons que ces informations (les nouveaux auto.map et netgroup) soient intégrées dans la base de données de NIS, nous devons modifier le Makefile avant de la reconstruire. En effet, il faut s'assurer que netgroup sera bien ajouté à la base. Concernant auto.map, ce fichier n'est pas défini par défaut, il faut signaler son existence. Il suffit pour cela d'ajouter une nouvelle entrée dans le Makefile, ainsi que la règle associée (en prenant modèle sur ce qui existe déjà) :
#A ajouter dans le Makefile des Yellow Pages
AUTO_MAP    = $(YPSRCDIR)/auto.map
# ...
#...
auto.map: $(AUTO_MAP) $(YPDIR)/Makefile
            @echo "Updating $@..."
            -@sed -e "/^#/d" -e s/#.*$$// $(AUTO_MAP) | $(DBLOAD) \
            -i $(AUTO_MAP) -o $(YPMAPDIR)/$@ - $@
            -@$(NOPUSH) || $(YPPUSH) -d $(DOMAIN) $@
Cette règle de production se contente d'enlever les commentaires, d'ajouter une nouvelle entrée à la base de données puis de transmettre l'information à tous les serveurs.

Il ne reste plus qu'à exécuter un make dans le répertoire /var/yp.

Au tour de nos trois clients sabrina, jill et kelly. Là, il n'y a presque rien à faire :) Nous devons préciser à autofs qu'il doit gérer une nouvelle map qui lui sera fournie via les YPs. Dans chacun des /etc/auto.master des clients, la ligne suivante permet de signaler la présence d'une map auto.map qui sera obtenue via les services des YPs.

#/etc/auto.master
/usr/local    yp auto.map    --intr,nosuid,nodev
puis il faut redémarrer autofs pour que cette nouvelle map soit prise en compte.

Ainsi, il existe un unique répertoire /usr/local physiquement sur charly. Du coup, pour installer des programmes très spécifiques, en les disposant sur charly, toutes nos machines pourront également en profiter.

Cet exemple peut aller beaucoup plus loin en n'installant qu'un seul système /usr, /usr/doc ou d'autres, mais la pratique montre que ce n'est pas très raisonnable. Les installations nécessitent souvent de modifier des fichiers dans le répertoire /etc ou autres. Il faudrait donc aller faire des retouches sur toutes les machines pour mettre à jour les fichiers non-exportés, ce qui devient vite très fastidueux.
 

Références

Systèmes de Fichiers


NFS



Footnotes

... inode1
Il s'agit d'un descripteur (une suite de bits) contenant, entre autre, les droits d'accès au fichier, son propriétaire, les adresses des blocs physiques contenant les données, etc ...
... /etc/mtab2
Ce fichier contient la liste de tous les systèmes de fichiers qui sont montés par le noyau, que ce soit "en dur" (par un mount, comme c'est le cas avec les systèmes décrits dans la fstab), ou par des moyens moins permanents (via autofs/automount).