Ressources: * [[http://doc.ubuntu-fr.org/nfs|Doc Ubuntu]] * [[http://nfs.sourceforge.net/nfs-howto/| Linux NFS Howto]] * [[http://nfs.sourceforge.net/| Linux NFS FAQ]] ====== 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 [[http://nfs.sourceforge.net/| 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//)