Table des matières
Ressources:
1. Présentation
NFS est un protocole de partage de fichiers principalement dédié à Unix, et qui existe depuis les années 80. Bien qu'il existe d'autres solutions (AFS, CIFS), sa simplicité et son efficacité l'ont rendu très populaire. Il a été inventé et largement porté par Sun depuis le début.
Version | Date | Standards | Fonctionnalités |
---|---|---|---|
v2 | 1989 | RFC1094 | UDP (stateless) |
v3 | 1995 | RFC1813 | UDP, 64bits, cache (stateless) |
v4 | 2000 | RFC3010, RFC3530 | UDP+TCP, ACL, auth+autz+sec, RPC-less (stateful) |
Stateless vs. Stateful
Le principe d'un protocole sans état est de pouvoir contenir une information complète dans chaque “paquet” (ici un ordre, comme lire ou écrire), sans reposer sur des informations envoyées dans des paquets précédents. Ceci permet une implémentation simple et robuste, mais limite les possibilités et peut affecter les performances (l'état est redondant dans chaque ordre).
Cependant, l'idée des concepteurs originaux de NFS est de pouvoir rajouter des fonctionnalités “stateful”, mais en dehors du service d'échange de fichiers principal. C'est par exemple le cas du démon lockd, responsable du verouillage des fichiers (accès exclusif), qui maintient nécessairement une information qui couvre plusieurs requêtes concernant le même fichier.
Aujourd'hui l'expérience concernant les protocoles stateful est bien plus avancé, et les avantages peuvent outrepasser ceux d'un protocol stateless: notamment lorsque les services annexes stateful en v2/v3 se multiplient, et que leurs interactions sont complexes. En particulier, l'implémentation basée sur RPC requiert de nombreux ports ouverts (imprévisibles) et sont particulièrement difficiles à firewaller.
UDP vs. TCP
UDP est précisemment un protocole stateless, qui s'adapte bien aux protocoles NFS v2/v3. Par contre il ne possède pas de mécanisme d'équilibrage de la bande passante et de gestion de la congestion comme TCP, et bien entendu pas de facilité de détection d'erreur et de retransmission. UDP est donc utilisable sur un réseau local, en général dédié, où l'on sait que les conditions de congestion et de panne sont quasi nulles.
Pour une utilisation plus large (WAN, VPN), TCP devient alors incontournable. En pratique, des implémentation des protocoles v2 et v3 en TCP ont été réalisées par la plupart des vendeurs Unix (même si elles ne font pas partie des standards). Aujourd'hui, TCP étant parfaitement géré et sans surcharge sur des équipements de commutation et de routage, c'est la solution préférée.
64 bits
NFS v2 utilise des tailles de fichier représentées sur 32 bits signés, donc jusqu'à 2^31^-1 octets (2Go). A partir de NFS v3, et si le reste de l'infrastructure de l'OS le supporte, cette information est sur 64 bits, donc virtuellement illimitée (pour 10 ans…).
NFS v4
NFS v4 a introduit de nombreux changements (inspiré par AFS et CIFS) et utilise une infrastructure nettement différente.
Il utilise notamment l'infrastructure d'authentification, d'autorisation et de cryptage GSSSAPI (ex: Kerberos), en lieu et place du test machine/IP originel. Il est donc nativement équipé pour être utilisé directement sur Internet, sans précautions supplémentaires (VPN, firewalls, etc).
Enfin il ne repose plus sur portmapper (le démon RPC) et utilise une seule session TCP sur un port prédéterminé (2049), simplifiant grandement son filtrage et sa gestion.
2. NFS v3 côté serveur
NFS v3 est implémenté complètement dans Linux 2.4 et Linux 2.6. Attention, certaines modifications importantes concernant NFS ont été réalisés pendant le développement de ces deux séries, consulter la FAQ.
Kernel vs. User
Il existe deux implémentations de référence: l'une en grande partie implementée dans le noyau, l'autre entièrement réalisée en tant qu'application utilisateur (Debian: paquets nfs-kernel-server et nfs-user-server).
Il est évident que pour des raisons de performance, on préférera si possible l'implémentation kernel: elle implique moins de changements de contexte, et pas ou peu de copies de données kernel/user. Les inconvénients:
- Liste à puce
- Liste à puce
- une mise à jour implique un reboot
- un plantage implique très probablement un crash global
- une faille de sécurité implique très probablement un remote root exploit
- le diagnostic peut être délicat (log non contrôlable, version “debug” non disponible, etc)
Heureusement NFS v3 est un composant logiciel relativement simple, et aujourd'hui mature dans les séries 2.4 et 2.6. On peut préférer son implémentation user-space quand :
- les perfomances ne sont pas un problème
- l'usage est temporaire (facile à installer puis désinstaller)
- on veut faire des essais ou diagnostics poussés
Note : le support client nécessite toujours un support spécifique côté noyau.
Démons
NFS v2/v3 requiert un écosystème de services pour fonctionner correctement côté serveur:
Nom | Port(s) | Description |
---|---|---|
portmap | 111 tcp+udp | RPC server (annuaire de services) |
[nfsd]] | 2049 tcp+udp | NFS server (opération standard sur les fichiers) |
[rpciod] | - | NFS I/O kernel thread |
[lockd] ou rpc.lockd | dynamique | Locking (accès exclusif aux fichiers) - optionnel |
rpc.mountd | dynamique | Mount/umount (filehandle initial) |
rpc.statd | dynamique | Aide pour lockd en cas de reboot - optionnel |
rpc.quotad | dynamique | Gestion des quotas - optionnel |
Configuration
Portmap utilise tcpwrappers pour gérer un système d'autorisation/rejet simple par IP ou sous-réseau, à l'aide des fichiers /etc/hosts.allow et /etc/hosts.deny. Cela peut être rapidement fastidieux, par exemple:
Exemple de /etc/hosts.allow (autorise RPC sur un sous-réseau privé)
portmap : 127. 192.168.1. : ALLOW lockd : 127. 192.168.1. : ALLOW rquotad : 127. 192.168.1. : ALLOW mountd : 127. 192.168.1. : ALLOW statd : 127. 192.168.1. : ALLOW
Note : l'utilisation de tcpwrappers est optionnelle, un fichier de configuration vide équivaut à un plein accès.
Le démon NFS est entièrement contrôlé par le fichier /etc/exports, qui a une syntaxe très simple: un partage de fichiers par ligne :
Exemple de /etc/exports :
/home 192.168.1.0/24(rw,sync) /var/log *.insia.org(ro) trusted.com(rw,all_squash) /pub (ro)
Attention: ce fichier est très sensible aux espaces (ne jamais en mettre entre une IP et la première parenthèse!).
Il est tout à fait possible de contrôler le démon indépendemment de ce fichier de configuration, mais la pratique standard consiste à effectuer les modifications dans le fichier, puis demander au démon de relire cette configuration ainsi :
# exportfs -r
Une mise à jour a pour effet :
- d'autoriser les nouvelles entrées définies
- de refuser les entrées retirées (cad. les demandes de montage ainsi que toute utilisation en cours)
- de mettre à jour les options éventuellement modifiées
Détails de la configuration
Un partage NFS peut accepter différentes options, chacune mérite une attention particulière. Il n'existe pas de réglage absolu et optimal, mais uniquement des compromis adaptés à des situations particulières. Le détail est dans man exports.
- rsize,wsize: taille d'un paquet de donnée en lecture et écriture respectivement. Elle est en général limitée à 32Ko (option de compilation). Ces paramètres ont notamment un effet en cas de perte de paquets, de congestion ou de fragmentation. Leur impact est difficile à prédire, il dépend de nombreux paramètres. Effectuer une mesure par rapport à la valeur par défaut pour vérifier sa pertinence.
- async: le mode asynchrone permet de gagner en performances (en écriture) mais au risque de ne pas détecter une perte d'information en cas de panne (reboot serveur, coupure réseau, etc). Le mode synchrone de NFS v3 possède une optimisation des caches qui le rend quasiment aussi performant que le mode asynchrone, il est donc fortement recommandé de rester synchrone.
- no_root_squash: l'implication au niveau sécurité est discutable, cette option ne contrôlant que la création ou modificiation de fichiers appartenant au compte root, et ne concerne jamais un contexte d'exécution particulier ou une opération privilégiée.
- all_squash: force l'UID et le GID de tous les attributs de fichiers aux valeurs décrites par anonuid et anongid, typiquement utilisé pour créer un volume librement modifiable par tous
- secure: oblige l'utilisation de ports sources privilégiées (<1024). Ce modèle suppose que les machines sont sécurisées et qu'effectivement l'accès à ces ports est privilégié sur l'ensemble du réseau, notion obsolète depuis Windows…
- mountpoint: n'exporte le partage que si le chemin est effectivement un point de montage. Evite de désagréables surprises (disque système plein) dans certaines situations (disques de données en panne).
- fsid: chaque export NFS possède automatiquement un identifiant (un numéro), normalement unique sur le réseau. Il est possible de décider arbitrairement de ce numéro, par exemple pour pouvoir remplacer un serveur NFS par un autre de manière transparente (on peut alors transférer le fsid).
Sécurité
Le serveur fait confiance au client et interprète les attributs UID et GID des opérations et des fichiers comme tel: pour NFS, ce sont des informations comme les autres. Le serveur utilise la sémantique du système de fichier local pour savoir ce qu'il est autorisé à faire, donc on se retrouve dans un schéma de droits et d'accès Unix traditionnel.
Hormis les options simples (et simplistes) no_root_squash, all_squash et anonuid/anongid, la correspondance entre les identifiants (UID/GID) des différentes machines (clients et serveur) est l'identité. On utilise souvent un annuaire type LDAP ou NIS pour unifier cette information sur un parc de machines.
Diagnostics
Côté serveur, on observe peu de choses. Le protocole étant sans état, si l'on utilise pas RPC on ne peut même pas détecter les sessions TCP (par ex: netstat -tnp|grep :2049). Il existe un outil pour obtenir des statistiques globales, nfsstat et la possibilité de lister le détail des services RPC disponibles sur la machine avec rpcinfo -p.
3. NFS v3 côté client
L'utilisation est un peu plus simple, mais il faut toujours à peu près le même écosystème: si le serveur principal (nfsd et rpc.moutnd) n'est bien sûr pas requis, les services secondaires (rpc.statd., rpc.lockd et donc portmap) sont requis.
Utiliser un partage NFS se fait comme un montage Unix standard. Il est recommandé d'utiliser le fichier standard /etc/fstab à cet effet :
Exemple (extraite) de /etc/fstab
device mountpoint fs-type options dump fsckorder 192.168.1.1:/home /mnt/home nfs rw,sync,hard,intr 0 0
Puis :
# mnt /mnt/home
Note: l'erreur précise en cas d'échec de montage se trouve côté serveur dans ses logs.
Détails de la configuration
Ces informations sont documentées dans man nfs.
- soft/hard: un montage soft transforme une erreur permanente NFS en une erreur immédiate, c'est-à-dire que l'appel système renvoie une erreur. Si le programme est mal équipé pour gérer l'erreur (souvent le cas), ceci peut apporter plus de problèmes que de solution (arrêts de services, plantages). hard est fortement recommandé, voir aussi intr/nointr.
- intr/nointr: le fonctionnement standard est nointr, où un appel système n'est pas interruptible tant qu'une erreur n'est pas corrigée (ex: serveur injoignable); on observe en pratique des processus dans l'état D qui ne peuvent être “tués” (cf. plus loin). En mode intr, les erreurs sont interruptibles par l'administrateur ou l'application: les erreurs doivent alors être gérées, sous peine de comportement imprévisible des programmes.
- timeo,retrans: timeout et nombre d'essais de retransmission en UDP (en général: 0.7 secondes, 3 essais)
- tcp: utilise TCP (UDP par défaut)
- noac: désactive le cache d'attribut des fichiers. Force le client à vérifier la validité de son cache souvent: génère plus de traffic réseau, mais permet de voir les modifications (d'autres clients) de façon plus rapide.
Gestion de panne
Quand le serveur n'est plus joignable, le comportement par défaut consiste à attendre indéfiniment son retour (hard,nointr). Dès que le serveur est à nouveau visible, le traffic reprend normalement dans la seconde, les clients étant “gelés” au moment de la panne.
Un client NFS v3 utilisant une stratégie de cache évoluée et concertée avec le serveur, se sert également d'informations fournies par le serveur s'il a rebooté pour savoir ce qu'il doit faire de ses caches (les invalider ou les envoyer). Ceci est vrai seulement en mode synchrone.
Si le serveur est injoignable , et ce de manière indéfinie, il ne peut être remplacé par un autre serveur que sous certaines conditions (cf. fsid). Sinon en général on chercher à débloquer les processus:
- en mode soft, ceux-ci reçoivent des erreurs et peuvent être arrêtés normalement
- en mode hard,intr, il est possible de les interrompre (kill -9)
- en mode hard,nointr, il est en fait possible de les interrompre (kill -9), mais en signalant le thread kernel rpciod (kill -9) pour forcer la livraison des signaux vers les processus bloqués
- en dernier recours, il reste umount -f
- dans les cas desespérés, il y a aussi umount -l (lazy)