CS 111 Scribe Notes for 11/30/2010
NFS and Security (Lecture 22)
Written by Michael Sechooler based on a lecture by Professor Paul Eggert.


Contents

Network File System (NFS; example of RPC)

Introduction

Files reside on server computer, not on a localhost.
Figure 1: Network layout for an NFS system.
Image nfs_graph

There are three ways to extend programs to utilize NFS filesystems:

  1. Modify all applications to use special syscalls to open NFS files:
    	nfs_open("name", ...) -> nfsfd_t
    
    However, this requires modifying all existing applications.
  2. Make available the same syscalls and extend libraries (e.g., C/C++ standard libraries with stdio and iostream) to seamlessly and automatically use them when working with NFS shares. To an application using these libraries, nothing would change. However, too many applications directly use syscalls for I/O, so this approach would not work for a wide set of programs.

  3. Change the syscall implementations themselves. Make the kernel support it natively, seamlessly, and automatically. In Linux (and using similar concepts in others), all filesystems sit below the VFS layer. High-level kernel code works with the VFS abstraction, so it does not need to be modified for each filesystem. Only the low-level portion that maps VFS functions and concepts to the filesystem-specific ones must be changed. See Figure 2 for more on this mapping.

    Figure 2: VFS and filesystem-specific map.
    Image nfsvfs

Since the third option does not require changing any previous application code, NFS uses it. It is its own filesystem (under VFS) and uses RPC, as opposed to local disk access. To the user, however, it looks like ordinary files and directories. An NFS filesystem is typically mounted into the filesytem as a subdirectory (or, possibly, as the root one).

NFS Protocol

With NFS, the syscalls actually become message passing calls. Even the protocol used to pass these message across a network resembles the I/O syscalls with which we are already familiar because it was designed for UNIX systems. See Figure 3 for some example messages. Note that the RPCs closely match syscalls, with the exception of open(). open() for NFS is requires opening (LOOKUPing) all parent directories to get their directory file handles (dirfh).

Figure 3: Example NFS messages.
\begin{figure}\begin{verbatim}name(parameters, ...) -> response + ...LOOKUP(...
...MDIR(dirfh, name) -> status
READ...
WRITE...\end{verbatim}
\center\end{figure}

The file handle (fh) is perhaps the most important component of NFS. It uniquely identifies a file, somewhat like a file descriptor, but is persistent across server crashes and reboots -- essentially for the life of the file. Typically, the easiest way to provide such a number is to combine the inode of the file (which is constant for its life) and the device/filesystem number (since inodes are specific to one filesystem). However, the operating system provides no easy way to access files based on these two numbers, since doing so would enable bypassing directory permissions by going straight to the file. Therefore, most implementations require some kernel-space code; however, it is possible to grant the ability to access files in this manner to root only and then to run the server as root in user-space.

However, NFS has one key difference between local disk filesystems: having a file handle does not guarantee that the file will continue to exist. For an example, considering the following interactions between two clients:

  1. Client A opens (LOOKUP) and continually reads (READ) a NFS file.
  2. Client B, while A is reading, removes (REMOVE) the file. The server promptly deletes the file.
Client A still has a file handle; however it has become stale since the file it points to has been removed. The read() syscall would return ESTALE, an error code created especially for this problem. Normally, the operating system does not actually delete a file until all processes have released it. However, the server cannot know when a client is finished. We could implement a CLOSE message, but a client could crash without closing a file; the file would then remain unlinked but otherwise immortal. Since NFS is supposed to be robust, this would not be acceptable, so it breaks this promise.

An NFS server is a stateless, meaning that no data is kept in RAM; everything is written to disk immediately. This is to ensure that the server does not, for example, return success for a write but then promptly lose it when it crashes. However, the lack of state also prevents locking.

/Open Consistency However, NFS can provide close/open consistency. Consider the following interactions between two clients:

  1. Client A opens a file, changes it, and closes it.
  2. Client B opens file after A finishes and sees the changes.
Because a close command flushes the buffers and an open command disregards prior buffering, NFS can guarantee that a client will see changes of files closed before it opened them.

NFS RPC Performance

A lot of individual calls takes a lot of time.

Client Synchronicity

NFS clients typically read-ahead (see Figure 4), cache read blocks on client, and dally for writes (in which it returns immediately but sends later).

Figure 4: Reading normally (left) vs. with reads-ahead (right).
Image readahead

These cheats can harm reliability. Caching ignores intermediary changes. Dallying could prevent other clients from receiving updates in a timely fashion or, in the event the writer crashes before actually updating the server, ever. These methods give up transaction consistency for performance.

Server Caching for Performance

Server caches writes to RAM. Not allowed for consistency unless the server has reliable RAM (battery-backed, for example, as found in high-end servers).

Example of High-End Server

This benchmark is taken from spec.org, which hosts various benchmark data.

The HP BL860c i2 4-node HA-NFS (Highly Available: tolerates single component failure with speed penalty -- Fault Tolerant does so without the penalty) has backup systems for everything, for a total of:

With a cost of hundreds of thousands, although it has a performance sweet spot of 2ms response time at 300k OPS (operations per second). In fact, the speed is significantly faster than any local hard disk.

NFS Security

Authentication
Traditionally, it is assumed the server is on a trusted network.
UID
UIDs may not match on different machines. Traditionally, a standard passwd file is copied among files so they all share the same account information.

The new solution is to use the Kerberos authentication system with UID remapping. However, it has a performance hit, since each RPC request must be authenticated.

Security in General

\begin{figure}''Not just a warm and fuzzy feeling.''
\begin{flushright}
- Prof. Paul Eggert on security
\end{flushright}\end{figure}

In the real world, must attacks are by force or fraud. OS security deals mainly with countering fraud. Most forms of fraud are against:

Privacy
Unauthorized access to data.
Integrity
Tampering with data.
Service
Denial of service.

There are two goals in defense:

The latter gets more tested in practice, because users complain if they cannot use their software. A is often poorly tested, sometimes even outsourced. (Tip: put deliberate errors in code to judge the testers' ability to find them.)

Typically, the first system in securing a system is to build a threat model, like the following: