Network File System (NFS) How To ================================ NFS, what's that? ----------------- "Network File System (NFS) is a distributed file system protocol originally developed by Sun Microsystems in 1984,[1] allowing a user on a client computer to access files over a computer network much like local storage is accessed. NFS, like many other protocols, builds on the Open Network Computing Remote Procedure Call (ONC RPC) system. The NFS is an open standard defined in Request for Comments (RFC), allowing anyone to implement the protocol." (https://en.wikipedia.org/wiki/Network_File_System) Versions: NFSv1, used at Sun Microsystems internally, not released into the wild. NFSv2 (as per RFC1094) as of March 1989 uses UDP (User Datagram Protocol), is designed to keep the server stateless. File locking is implemented elsewhere, has a 32-bit-limitation, limiting file sizes to 2GB. NFSv3 (RFS1813) as of June 1995 uses 64-bit offsets to allow file sizes > 2GB, asynchronous writes to not block clients by having them wait for write completion, supports TCP as transport to support NFS on WAN, increasing read and write block sizes to more than UDP's 8kb blocksize. NFSv4 (RFC3010, 12/2000; RFC3530, 4/2003; RFC7530 3/2015) improves performance, adds mandatory support for strong security and a stateful protocol. NFSv4.1 includes support for parallel access to files across multiple servers for highly parallel computing environments. Similar protocols: - SMB (Server Message Block) running on Microsoft Windows, see - AFP (Apple Filing Protocol) for Apple Macintosh (?) (Can anybody verify?) Components: - a server implements NFS access by running an NFS daemon (nfsd), specifying directories to be remote accessible by adding them to /etc/exports - a client mounts a server's shared directories either at startup, or using automount mechanisms on demand. Installation ------------ - Back up your data (you do have backups, right? RIGHT?) - Bring up apt's configuration to current: sudo apt update -y sudo apt upgrade -y (fix what's broken) sudo reboot - Install packages: sudo apt install -y nfs-common nfs-server portmap autofs Server Configuration -------------------- - Servers reside at static IP addresses. That way clients can find them without much guesswork. When using a Pi in its default (DHCP-dependent) network configuration (like a 0-W's one and only WiFi interface), reserve an IP address in the access point's DHCP server for at least that Pi. - Configure the server, by adding one line per directory to /etc/exports: pi@rpi002w:/etc $ cat exports # /etc/exports: the access control list for filesystems # which may be exported # to NFS clients. See exports(5). # [...] /cfs *(ro) /home/pi *(rw) pi@rpi002w:/etc $ /etc/exports.d may contain additional export tables. These must be visible (not start with "." (dot)), end in ".exports" (dot export). /etc/exports's entries consist of a directory name, followed by client and access specifications. Clients can be (lists of) hosts, networks, subnets. Access is at least one of ro (read-only) or rw (read-write), but not both. Other access specs: - async The server sends the response to a write request before the request's data has been written to disk, decreasing the client's wait, - sync The server sends the response to a write request after the request's data has been committed to disk. - no_wdelay The NFS server normally delays committing a write request to disc slightly if it suspects that another related write request may be in progress or may arrive soon. This allows multiple write requests to be committed to disc with the one operation which can improve perfor- mance. If an NFS server received mainly small unrelated requests, this behaviour could actually reduce performance, so no_wdelay is available to turn it off. The default can be explicitly requested with the wdelay option. For this to work, async must not be set. User IDs, ID Mapping -------------------- Access to a server's files is granted or denied using the same useer and group IDs on the client, so user accounts on client and server should exist and match in configuration. Running mismatched is an option resulting in occasional interesting behavior. By default, a client's root account with a user and group ID of 0 (zero) has its IDs remapped to nobody, with a user ID of 65534. To change this behavior, add one of the following options to the shared directory's access specification: - root_squash maps requests from uid/gid 0 to the anonymous uid/gid. This is the default. Other system accounts user and group IDs, such as bin, uucp, dialer, staff, operator, ... are not affected, means NFS-exporting directories such as /bin, /usr/bin, /usr/local using (rw) is a really bad idea. - no_root_squash does not map IDs of requests from clients' root account to nobody's. Diskless workstations require this. - all_squash maps all user and group IDs to nobody's. - anonuid and anongid substitute default values for nobody with the ones provided. Example entries include: / master(rw) trusty(rw,no_root_squash) /projects proj*.local.domain(rw) /usr *.local.domain(ro) @trusted(rw) /home/joe pc001(rw,all_squash,anonuid=150,anongid=100) /pub *(ro,insecure,all_squash) /srv/www -sync,rw server @trusted @external(ro) /foo 2001:db8:9:e54::/64(rw) 192.0.2.0/24(rw) /build buildhost[0-9].local.domain(rw) The first line exports the entire filesystem to machines master and trusty. In addition to write access, all uid squashing is turned off for host trusty. The second and third entry show examples for wildcard hostnames and net- groups (this is the entry `@trusted'). The fourth line shows the entry for the PC/NFS client discussed above. Line 5 exports the public FTP directory to every host in the world, executing all requests under the nobody account. The insecure option in this entry also allows clients with NFS implementations that don't use a reserved port for NFS. The sixth line exports a directory read-write to the machine 'server' as well as the `@trusted' netgroup, and read- only to netgroup `@external', all three mounts with the `sync' option enabled. The seventh line exports a directory to both an IPv6 and an IPv4 subnet. The eighth line demonstrates a character class wildcard match. Alternatively, export directories by invoking the "exportfs" command. "man exportfs" says: exportfs(8) System Manager's Manual exportfs(8) NAME exportfs - maintain table of exported NFS file systems SYNOPSIS /usr/sbin/exportfs [-avi] [-o options,..] [client:/path ..] /usr/sbin/exportfs -r [-v] /usr/sbin/exportfs [-av] -u [client:/path ..] /usr/sbin/exportfs [-v] /usr/sbin/exportfs -f Exportfs uses /var/lib/nfs/etab to store its configuration. /etc/exports is used to initialize /var/lib/nfs/etab. Issues ------ nfsd (the daemon granting clients access to a server's resources) exits if there are not network interfaces up and running, especially on a Pi 0 W. To restart nfsd, use this crontab entry: pi@rpi002w:~ $ crontab -l # Edit this file to introduce tasks to be run by cron. # # Each task to run has to be defined through a single line # indicating with different fields when the task will be run # and what command to run for the task # # To define the time you can provide concrete values for # minute (m), hour (h), day of month (dom), month (mon), # and day of week (dow) or use '*' in these fields (for 'any').# # Notice that tasks will be started based on the cron's system # daemon's notion of time and timezones. # # Output of the crontab jobs (including errors) is sent through # email to the user the crontab file belongs to (unless redirected). # # For example, you can run a backup of all your user accounts # at 5 a.m every week with: # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ # # For more information see the manual pages of crontab(5) and cron(8) # # m h dom mon dow command * * * * * /home/pi/bin/crontab.sh > /dev/null 2>&1 pi@rpi002w:~ $ cat bin/crontab.sh #!/bin/sh if [ `ps -ef | grep idmapd | grep -v grep | wc -l` -eq 0 ] then /usr/bin/sudo /etc/init.d/nfs-common restart > /dev/null 2>&1 fi if [ `ps -ef | grep nfsd | grep -v grep | wc -l` -eq 0 ] then /usr/bin/sudo /etc/init.d/nfs-kernel-server restart > /dev/null 2>&1 fi pi@rpi002w:~ $ Available Shares ---------------- To see a server's shares, use "showmount -e", optionally followed by a hostname: pi@rpi002w:~ $ showmount -e Export list for rpi002w: /home/pi * /cfs * pi@rpi002w:~ $ rpi 1 ssh -L5931:127.0.0.1:5931 rpi001w Linux rpi001w 4.14.50+ #1122 Tue Jun 19 12:21:21 BST 2018 armv6l The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Fri Aug 3 15:10:20 2018 from 192.168.0.32 pi@rpi001w:~ $ showmount -e rpi002w Export list for rpi002w: /home/pi * /cfs * pi@rpi001w:~ $ pi@rpi001w:~ $ showmount -e Export list for rpi001w: pi@rpi001w:~ $ Client Configuration -------------------- Clients may establish access to a server's share in two different ways, either by adding one entry per share to /etc/fstab, or using the automounter. Mounting shares via /etc/fstab ------------------------------ To mount a server's share, create a directory as a mountpoint, then modify /etc/fstab to add lines of this form: server:/export /net/server/export nfs soft,rsize=8192, wsize=8192,bg,intr,nfsvers=3 0 0 (This is supposed to be one line. \-continuation is not an option) "server" is a hostname whose IP address can be obtained during system startup, possibly even before any name services (such as DNS, NIS, YP) are available, easy to do by adding an entry to /etc/hosts. "/export" is a directory on that server. The server must have an entry in /etc/exports for that directory, otherwise the request fails. "/net/server/export" is the client's mount point for the remote directory, i.e. the location where the remote directory is grafted on. "nfs" is the type of mount to use. Next field are options: - soft/hard selects how a client behaves if a request is not responded to in realistic time (aka timeout). A "hard" timeout causes the client to retry the request indefinitely. A "soft" timeout causes the client to retry a request "retrans" times, then return an error to the application triggering the request. Not catching and acting upon a soft timeout might corrupt data. - timeo=n causes a client to retry a request after n/10 seconds. - retrans=n sets the number of retries of a request before attempting other recovery methods. - rsize=n, wsize=n set the number of bytes of data to be sent, received for each request. - bg or fg (but not both) specify foreground or background mounting. A foreground mount causes the "mount" command to exit immediately if a mount request fails, or, in other implementations, to retry until interrupted. A background mount causes "mount" to fork itself into the background to retry the request. - retry=n sets the number of minutes to retry a mount request. - proto=udp, proto=tcp sets the transport mechanism for NFSv2 and NFSv3. - nfsvers=n sets the NFS version to use. The default is to try 4 first, if the server doesn't like that, 3, then 2. If NFS operations in a OS-diverse environment fail, use 3, not 4. - intr/nointr allows signals (SIGKILL, anyone) to interrupt file operations. This may come in handy if an operation is hanging due to network or server issues. Automounting shares using the automounter ----------------------------------------- - Modify /etc/auto.master to add this line: /net /etc/auto.net "/net" specifies a mount point (must exist!), "/etc/auto.net" specifies a script to invoke if something not yet mounted underneath that mountpoint is accessed. - Review, perhaps modify options used in /etc/auto.net and /etc/default/autofs: pi@pi0w00:/etc $ cat auto.net #!/bin/bash # This file must be executable to work! chmod 755! # Look at what a host is exporting to determine what we can mount. # This is very simple, but it appears to work surprisingly well key="$1" # add "nosymlink" here if you want to suppress symlinking local filesystems # add "nonstrict" to make it OK for some filesystems to not mount # choose one of the two lines below depending on the NFS version in your # environment [ -f /etc/default/autofs ] && . /etc/default/autofs if [ -z "$MOUNT_NFS_DEFAULT_PROTOCOL" -o "$MOUNT_NFS_DEFAULT_PROTOCOL" == "3" ]; then # Showmount comes in a number of names and varieties. "showmount" is # typically an older version which accepts the '--no-headers' flag # but ignores it. "kshowmount" is the newer version installed with knfsd, # which both accepts and acts on the '--no-headers' flag. #SHOWMOUNT="kshowmount --no-headers -e $key" #SHOWMOUNT="showmount -e $key | tail -n +2" for P in /bin /sbin /usr/bin /usr/sbin do for M in showmount kshowmount do if [ -x $P/$M ] then SMNT=$P/$M break fi done done [ -x $SMNT ] || exit 1 # Newer distributions get this right SHOWMOUNT="$SMNT --no-headers -e $key" $SHOWMOUNT | LC_ALL=C cut -d' ' -f1 | LC_ALL=C sort -u | \ awk -v key="$key" -v opts="$opts" -- ' BEGIN { ORS=""; first=1 } { if (first) { print opts; first=0 }; print " \\\n\t" $1, key ":" $1 } END { if (!first) print "\n"; else exit 1 } ' | sed 's/#/\\#/g' opts="-fstype=nfs,hard,intr,nodev,nosuid" else # NFSv4 opts="-fstype=nfs4,hard,intr,nodev,nosuid,async" echo "$opts $key:/" fi pi@pi0w00:/etc $ pi@pi0w00:/etc/default $ cat autofs # # Init system options # # If the kernel supports using the autofs miscellanous device # and you wish to use it you must set this configuration option # to "yes" otherwise it will not be used. # USE_MISC_DEVICE="yes" # # Use OPTIONS to add automount(8) command line options that # will be used when the daemon is started. # #OPTIONS="" # pi@pi0w00:/etc/default $ Party Time ---------- Time to power on. Cross fingers, reflect, repent, reboot everything. Order might return. Log into a client, access one of the server's shared directories: pi@rpi003w:~ $ pwd /home/pi pi@rpi003w:~ $ showmount -e rpi002w Export list for rpi002w: /home/pi * /cfs * pi@rpi003w:~ $ ls /net pi@rpi003w:~ $ ls -ls /cfs 0 lrwxrwxrwx 1 root root 16 Jun 13 10:51 /cfs -> /net/rpi002w/cfs pi@rpi003w:~ $ ls -lsL /cfs total 8 4 drwxr-xr-x 3 pi pi 4096 Apr 11 20:40 group 4 drwxr-xr-x 3 pi pi 4096 Apr 11 20:18 groups pi@rpi003w:~ $ ls /net rpi002w pi@rpi003w:~ $ ls /net/rpi002w cfs home pi@rpi003w:~ $