12.4.3. Unknown user mapping
NFS handles requests that do not have valid credentials in them by mapping
them to the anonymous user. There are several
cases in which an NFS request has no valid credential structure in
it:
Note that this is somewhat different behavior from Solaris 8 NFS
servers. In Solaris 8 the default is that invalid credentials are
rejected. The philosophy is that allowing an NFS user with an invalid
credential is no different then allowing a user to log in as user
nobody if he has forgotten his password.
However, there is a way to override the default behavior:
share -o sec=sys:none,rw /export/home/engin
This says to export the filesystem, permitting AUTH_SYS credentials.
However if a user's NFS request comes in with invalid
credentials or non-AUTH_SYS security, treat and accept the user as
anonymous. You can also map all users to anonymous, whether they have
valid credentials or not:
share -o sec=none,rw /export/home/engin
By default, the anonymous user is
nobody, so
unknown users (making the credential-less requests) and superuser can
access only files with world permissions set. The
anon export option allows a server to change the
mapping of anonymous requests. By setting the anonymous user ID in
/etc/dfs/dfstab, the unknown user in an
anonymous request is mapped to a well-known local user:
share -o rw,anon=100 /export/home/engin
In this example, any request that arrives without user credentials
will be executed with UID 100. If
/export/home/engin is owned by UID 100, this
ensures that unknown users can access the directory once it is
mounted. The user ID mapping does not affect the real or effective
user ID of the process accessing the NFS-mounted file. The anonymous
user mapping just changes the user credentials used by the NFS server
for determining file access permissions.
The anonymous user mapping is valid only for the filesystem that is
exported with the
anon option. It is possible to
set up different mappings for each filesystem exported by specifying
a different anonymous user ID value in each line of the
/etc/dfs/dfstab file:
share -o rw,anon=100 /export/home/engin
share -o rw,anon=200 /export/home/admin
share -o rw,anon=300 /export/home/marketing
Anonymous users should almost
never be mapped to
root, as this would grant superuser access to filesystems to any user
without a valid password file entry on the server. An exception would
be when you are exporting read-only, and the data is not sensitive.
One application of this is exporting directories containing the
operating system installation. Since operating systems like Solaris
are often installed over the network, and superuser on the client
drives the installation, it would be tedious to list every possible
client that you want to install the operating system on.
Anonymous users should be thought of as transient or even unwanted
users, and should be given as few file access permissions as
possible. RPC calls with missing UIDs in the credential structures
are rejected out of hand on the server if the server exports its
filesystems with
anon=-1. Rather than mapping
anonymous users to
nobody, filesystems that
specify
anon=-1 return authentication errors for
RPC calls with no credentials in them.
Normally, with the anonymous user mapped to
nobody, anonymous requests are accepted but have
few, if any, permissions to access files on the server. Mapping
unknown users is a risky venture. Requests that are missing UIDs in
their credentials may be appearing from outside the local network, or
they may originate from machines on which security has been
compromised. Thus, if you must export filesystems with the anonymous
user mapped to a UID other than nobody, you should limit it to a
smaller set of hosts:
share -o rw=engineering,anon=100 /export/home/engin # a nergroup
share -o rw=admin1:admin2,anon=200 /export/home/admin # a pair of hosts
share -o rw=.marketing.widget.com,anon=300 /export/home/marketing # a domain
We discuss limiting exports to certain hosts in the
next section.
12.4.8. Access control lists
Some NFS servers exist in an operating environment that
supports Access Control Lists (ACLs). An ACL extends the basic set of
read, write, execute permissions beyond those of file owner, group
owner, and other. Let's say we have a set of users called
linus, charlie,
lucy, and sally, and these
users comprise the group peanuts. Suppose
lucy owns a file called
blockhead, with group ownership assigned to
peanuts. The permissions of this file are 0660
(in octal). Thus lucy can read and write to the
file, as can all the members of her group. However,
lucy decides she doesn't want
charlie to read the file, but still wants to
allow the other peanuts group members to access
the file. What lucy can do is change the
permissions to 0600, and then create an ACL that explicitly lists
only linus and sally as
being authorized to read and write the file, in addition to herself.
Most Unix systems, including Solaris 2.5 and higher, support a draft
standard of ACLs from the POSIX standards body. Under Solaris,
lucy would prevent charlie
from accessing her file by doing:
% chmod 0600 blockhead
% setfacl -m mask:rw-,user:linus:rw-,user:sally:rw- blockhead
To understand what setfacl did, let's read
back the ACL for blockhead:
% getfacl blockhead
# file: blockhead
# owner: lucy
# group: peanuts
user::rw-
user:linus:rw- #effective:rw-
user:sally:rw- #effective:rw-
group::--- #effective:---
mask:rw-
other:---
The user: entries for sally
and linus correspond to the
rw permissions lucy
requested. The user:: entry simply points out
that the owner of the file, lucy has
rw permissions. The group::
entry simply says that the group owner, peanuts,
has no access. The mask: entry says what the
maximum permissions are for any users (other than the file owner) and
groups. If lucy had not included mask
permissions in the setfacl command, then
linus and sally would be
denied access. The getfacl command would instead
have shown:
% getfacl blockhead
# file: blockhead
# owner: lucy
# group: peanuts
user::rw-
user:linus:rw- #effective:---
user:sally:rw- #effective:---
group::--- #effective:---
mask:---
other:---
Note the difference from the two sets of getfacl
output: the effective permissions granted to
linus and sally.
Once you have the ACL on a file the way you want it, you can take the
output of getfacl on one file and apply it to
another file:
% touch patty
% getfacl blockhead | setfacl -f /dev/stdin patty
% getfacl patty
# file: patty
# owner: lucy
# group: peanuts
user::rw-
user:linus:rw- #effective:rw-
user:sally:rw- #effective:rw-
group::--- #effective:---
mask:rw-
other:---
It would be hard to disagree if you think this is a pretty arcane way
to accomplish something that should be fairly simple. Nonetheless,
ACLs can be leveraged to solve the "too many groups"
problem described earlier in this chapter in Section 12.4.1, "RPC security". Rather than put users into lots of
groups, you can put lots of users into ACLs. The previous example
showed how to copy an ACL from one file to another. You can also set
a default ACL on a directory, such that any files or directories
created under the top-level directory are inherited. Any files or
directories created in a subdirectory inherit the default ACL. It is
easier to hand edit a file containing the ACL description than to
create one on the command line. User
lucy
creates the following file:
user::rwx
user:linus:rwx
user:sally:rwx
group::---
mask:rwx
other:---
default:user::rwx
default:user:linus:rwx
default:user:sally:rwx
default:group::---
default:mask:rwx
default:other:---
It is the
default: entries that result in
inherited ACLs. The reason why we add execution permissions is so
that directories have search permissions, i.e., so
lucy and her cohorts can change their current
working directories to her protected directories.
Once you've got default ACLs set up for various groups of
users, you then apply it to each top-level directory that you create:
% mkdir lucystuff
% setfacl -f /home/lucy/acl.default lucystuff
Note that you cannot apply an ACL file with
default: entries in it to nondirectories.
You'll have to create another file without the
default: entries to use
setfacl -f
on nondirectories:
% grep -v '^default:' | /home/lucy/acl.default > /home/lucy/acl.files
The preceding example strips out the
default:
entries. However it leaves the executable bit on in the entries:
% cat /home/lucy/acl.files
user::rwx
user:linus:rwx
user:sally:rwx
group::---
mask:rwx
other:---
This might not be desirable for setting an ACL on existing regular
files that don't have the executable bit. So we create a third
ACL file:
% sed 's/x$/-/' /home/lucy/acl.files | sed 's/^mask.*$/mask:rwx/' \
> /home/lucy/acl.noxfiles
This first turns off every execute permission bit, but then sets the
mask to allow execute permission should we later decide to enable
execute permission on a file:
% cat /home/lucy/acl.noxfiles
user::rw-
user:linus:rw-
user:sally:rw-
group::---
mask:rwx
other:---
With an ACL file with
default: entries, and the
two ACL files without
default: entries,
lucy can add protection to existing trees of
files. In the following example,
oldstuff is an
existing directory containing a hierarchy of files and
subdirectories:
fix the directories:
% find oldstuff -type d -exec setfacl -f /home/lucy/acl.default {} \;
fix the nonexecutable files:
% find oldstuff ! -type d ! ( -perm -u=x -o -perm -g=x -o -perm -o=x ) \
-exec setfacl -f /home/lucy/acl.noxfiles {} \;
fix the executable files:
% find oldstuff ! -type d ( -perm -u=x -o -perm -g=x -o -perm -o=x ) \
-exec setfacl -f /home/lucy/acl.noxfiles {} \;
In addition to solving the "too many groups in NFS"
problem, another advantage of ACLs versus groups is potential
decentralization. As the system administrator, you are called on
constantly to add groups, or to modify existing groups (add or delete
users from groups). With ACLs, users can effectively administer their
own groups. It is a shame that constructing ACLs is so arcane,
because it effectively eliminates a way to decentralize a security
access control for logical groups of users. You might want to create
template ACL files and scripts for setting them to make it easier for
your users to use them as a way to wean them off of groups. If you
succeed, you'll reduce your workload and deal with fewer issues
of "too many groups in NFS."
TIP:
In Solaris, ACLs are not preserved when copying a file from the local
ufs filesystem to a file in the
tmpfs (/tmp) filesystem.
This can be a problem if you later copy the file back from
/tmp to a ufs filesystem.
Also, in Solaris, ACLs are not, by default, preserved when generating
tar or cpio archives. You need to use the -p
option to tar to preserve ACLs when creating and
restoring a tar archive. You need to use the
-P option to cpio when
creating and restoring cpio archives. Be aware
that non-Solaris systems probably will not be able to read archives
with ACLs in them.
12.4.8.2. ACLs and NFS
ACLs are ultimately enforced by the local filesystem on the NFS server.
However, the NFS protocol has no way to pass ACLs back to the client.
This is a problem for NFS Version 2 clients, because they use the
nine basic permissions bits (read, write, execute for user, group,
and other) and the file owner and group to decide if a user should
have access to the file. For this reason, the Solaris NFS Version 2
server reports the minimum possible permissions in the nine
permission bits whenever an ACL is set on a file. For example,
let's suppose the permissions on a file are 0666 or
rw-rw-rw-. Now let's say an ACL is added
for user charlie that gives him permissions of
-- -, i.e., he is denied access. When an ACL
is set on a file, the Solaris NFS Version 2 server will see that
there is a user that has no access to the file. As a result, it will
report to most NFS Version 2 clients permissions of 0600, thereby
denying nearly everyone (those accessing from NFS clients) but
lucy access to the file. If it did not, then
what would happen is that the NFS client would see permissions of
0666 and allow charlie to access the file.
Usually charlie's application would
succeed in opening the file, but attempts to read or write the file
would fail in odd ways. This isn't desirable. Even less
desirable is that if the file were cached on the NFS client,
charlie would be allowed to read the
file.[21]
This is not the case for the NFS Version 3 server though. With the
NFS Version 3 protocol, there is an ACCESS
operation that the client sends to the server to see if the indicated
user has access to the file. Thus the exact, unmapped permissions are
rendered back to the NFS Version 3 client.
We said that the Solaris NFS server will report to most NFS Version 2
clients permissions of 0600. However, starting with Solaris 2.5 and
higher, a side band protocol to NFS was added, such that if the
protocol exists, the client can not only get the exact permissions,
but also use the sideband protocol's ACCESS procedure for
allowing the server to permissions the access checks. This then
prevents charlie or the superuser from gaining
unauthorized access to files.
What if you have NFS clients that are not running Solaris 2.5 or
higher, or are not running Solaris at all? In that situation you have
two choices: live with the fact that some users will be denied access
due to the minimal permissions behavior, or you can use the
aclok option of the Solaris
share command to allow maximal access. If the
filesystem is shared with aclok, then if anyone
has read access to the files, then everyone does. So,
charlie would then be allowed to access file
blockhead.
Another issue with NFS and ACLs is that the NFS protocol has no way
to set or retrieve ACLs, i.e., there is no protocol support for the
setfacl or getfacl command.
Once again, the sideband protocol in Solaris 2.5 and higher comes to
the rescue. The sideband protocol allows ACLs to be set and
retrieved, so setfacl and
getfacl work across NFS.
TIP:
IBM's AIX and Compaq's Tru64 Unix have sideband ACL
protocols for manipulating ACLs over NFS. Unfortunately, none of the
three protocols are compatible with each other.