ACLs
The ZFS file system uses a
pure ACL model,
that is compliant with the NFSv4 ACL model. What is meant by pure
ACL model, is that every file
always has an ACL, unlike file systems such as UFS that have either an
ACL or it has
permission bits. All access control decisions are governed by a
file's ACL. All file's
still have
permission bits,
but they are constructed by analyzing a file's ACL.
NFSv4 ACL Overview
The ACL model in NFSv4 is similar to the
Windows ACL model. The NFSv4
ACL model supports a rich set of access permissions and inheritance
controls. An ACL
in this model is composed of an array of access control entries
(ACE). Each ACE specifies the
permissions, access type, inheritance flags and to whom the entry
applies. In the NFSv4 model the "who" argument of each ACE, may
be either a username or groupname. There are also a set of
commonly know names, such as "owner@", "group@", "everyone@".
These abstractions are used by UNIX variant operating systems to
indicate if the ACE is for the file owner, file group owner or for the
world. The everyone@ entry is not equivalent to the POSIX "other"
class, it really is everyone. The complete description of the
NFSv4 ACL model is availabe in Section 5.11 of the NFSv4 protocol
specification.
NFSv4 Access Permissions
Permission
|
Description
|
read_data
|
Permission to read the data of
the file
|
list_data
|
Permission to list the contents
of a directory
|
write_data
|
Permission to modify the file's
data anywhere in the file's offset range. This includes the
ability to grow the file or write to an arbitrary offset.
|
add_file
|
Permission to add a new file to
a directory
|
append_data
|
The ability to modify the data,
but only starting at EOF.
|
add_subdirectory
|
Permission to create a
subdirectory to a directory
|
read_xattr
|
The ability to read the extended
attributes of a file or to do a lookup in the extended attributes
directory.
|
write_xattr
|
The ability to create extended
attributes or write to the extended attributes directory.
|
execute
|
Permission to execute a file
|
delete_child
|
Permission to delete a file
within a directory
|
read_attributes
|
The ability to read basic
attributes (non-ACLs) of a file. Basic attributes are considered
the stat(2) level attributes.
|
write_attributes
|
Permission to change the times
associated with a file or directory to an arbitrary value
|
delete
|
Permission to delete a file
|
read_acl
|
Permission to read the ACL
|
write_acl
|
Permission to write a file's ACL
|
write_owner
|
Permission to change the owner
or the ability to execute chown(1) or chgrp(1)
|
synchronize
|
Permission to access a file
locally at the server with synchronous reads and writes.
|
NFSv4 Inheritance flags
Inheritance Flag
|
Description
|
file_inherit
|
Can be place on a directory and
indicates that this ACE should be added to each new non-directory file
created.
|
dir_inherit
|
Can be placed on a directory and
indicates that this ACE should be added to each new directory created.
|
inherit_only
|
Placed on a directory, but does
not apply to the directory itself, only to newly created files and
directories. This flag requires file_inherit and or dir_inherit
to indicate what to inherit.
|
no_propagate
|
Placed on directories and
indicates that ACL entries should only be inherited to one level of the
tree. This flag requires file_inherit and or dir_inherit to
indicate what to inherit.
|
NFSv4 ACLs vs POSIX
The difficult part of using the NFSv4 ACL model was trying to still
preserve POSIX compliance in the file system. POSIX allows for
what it calls "additonal" and "alternate" access methods. An
additional access method is defined to be layered upon the file
permission bits, but they can only further restrict the standard access
control mechanism. The alternate file access control mechanism is
defined to be independent of the file permission bits and which if
enabled on a file may either restrict or extend the permissions of a
given user. Another major distinction between the additional and
alternate access control mechanisms is that, any alternate file access
control mechanism must be disabled after the file permission bits are
changed with a chmod(2). Additional mechanisms do not need to be
disabled when a chmod is done.
Most vendors
that have implemented NFSv4 ACLs have taken the approach of
"discarding" ACLs during a chmod(2). This is a bit heavy handed,
since a
user went through the trouble of crafting a bunch of ACLs, only to have
chmod(2) come through and destroy all of their hard
work. It was this single issue that was the biggest
hurdle to POSIX compliance with ZFS in implementing NFSv4
ACLs. In order to achieve this
Sam,
Lisa and I
spent far too long trying to come up with a model that would preserve
as much of the original ACL, while still being useful. What
we came up with is a model that retains
additional access methods,
and
disabled, but doesn't
delete
alternate access
controls.
Sam
and
Lisa have filed
an
internet
draft which has the details about the chmod(2) algorithm
and how to make NFSv4 ACLs POSIX complient.
So whats cool about this
Lets assume we have the following directory /sandbox/test.dir.
Its initial ACL looks like:
% ls
-dv test.dir
drwxr-xr-x 2
ongk
bin 2
Nov 15 14:11 test.dir
0:owner@::deny
1:owner@:list_directory/read_data/add_file/write_data/add_subdirectory
/append_data/write_xattr/execute/write_attributes/write_acl
/write_owner:allow
2:group@:add_file/write_data/add_subdirectory/append_data:deny
3:group@:list_directory/read_data/execute:allow
4:everyone@:add_file/write_data/add_subdirectory/append_data/write_xattr
/write_attributes/write_acl/write_owner:deny
5:everyone@:list_directory/read_data/read_xattr/execute/read_attributes
/read_acl/synchronize:allow
Now if I want to give "marks" the ability to create files, but not
subdirectories in this
directory then the following ACL would achieve this.
First lets make sure "marks" can't currently create
files/directories
$ mkdir /sandbox/bucket/test.dir/dir.1
mkdir: Failed to make directory
"/sandbox/test.dir/dir.1"; Permission denied
$ touch /sandbox/test.dir/file.1
touch: /sandbox/test.dir/file.1 cannot create
Now lets give marks add_file permission
% chmod
A+user:marks:add_file:allow
/sandbox/test.di
% ls
-dv test.dir
drwxr-xr-x+ 2 ongk
bin 2
Nov 15 14:11 test.dir
0:user:marks:add_file/write_data:allow
1:owner@::deny
2:owner@:list_directory/read_data/add_file/write_data/add_subdirectory
/append_data/write_xattr/execute/write_attributes/write_acl
/write_owner:allow
3:group@:add_file/write_data/add_subdirectory/append_data:deny
4:group@:list_directory/read_data/execute:allow
5:everyone@:add_file/write_data/add_subdirectory/append_data/write_xattr
/write_attributes/write_acl/write_owner:deny
6:everyone@:list_directory/read_data/read_xattr/execute/read_attributes
/read_acl/synchronize:allow
Now lets see if it works for user "marks"
$ id
uid=76928(marks) gid=10(staff)
$ touch file.1
$ ls
-v file.1
-rw-r--r-- 1 marks
staff 0 Nov 15
10:12 file.1
0:owner@:execute:deny
1:owner@:read_data/write_data/append_data/write_xattr/write_attributes
/write_acl/write_owner:allow
2:group@:write_data/append_data/execute:deny
3:group@:read_data:allow
4:everyone@:write_data/append_data/write_xattr/execute/write_attributes
/write_acl/write_owner:deny
5:everyone@:read_data/read_xattr/read_attributes/read_acl/synchronize
:allow
Now lets make sure "marks" can't create directories.
$ mkdir dir.1
mkdir: Failed to make directory "dir.1";
Permission denied
The write_owner permission is handled in a special way. It allows
for a user to "take" ownership of a file. The following example
will help illustrate this. With the write_owner a user can only
do a chown(2) to himself or to a group that he is a member of.
We will start out with the following file.
% ls
-v file.test
-rw-r--r-- 1
ongk
staff 0 Nov 15
14:22 file.test
0:owner@:execute:deny
1:owner@:read_data/write_data/append_data/write_xattr/write_attributes
/write_acl/write_owner:allow
2:group@:write_data/append_data/execute:deny
3:group@:read_data:allow
4:everyone@:write_data/append_data/write_xattr/execute/write_attributes
/write_acl/write_owner:deny
5:everyone@:read_data/read_xattr/read_attributes/read_acl/synchronize
:allow
Now if user "marks" tries to chown(2) the file to
himself he will get an error.
$ chown marks file.test
chown: file.test: Not owner
$ chgrp staff file.test
chgrp: file.test: Not owner
Now lets give "marks" explicit write_owner
permission.
% chmod
A+user:marks:write_owner:allow
file.test
% ls -v file.test
-rw-r--r--+ 1
ongk
staff 0 Nov 15
14:22 file.test
0:user:marks:write_owner:allow
1:owner@:execute:deny
2:owner@:read_data/write_data/append_data/write_xattr/write_attributes
/write_acl/write_owner:allow
3:group@:write_data/append_data/execute:deny
4:group@:read_data:allow
5:everyone@:write_data/append_data/write_xattr/execute/write_attributes
/write_acl/write_owner:deny
6:everyone@:read_data/read_xattr/read_attributes/read_acl/synchronize
:allow
Now lets see who "marks" can chown the file to.
$ id
uid=76928(marks) gid=10(staff)
$ groups
staff storage
$ chown bin file.test
chown: file.test: Not owner
So "marks" can't give the file away.
$ chown marks:staff file.test
Now lets look at an example to show how a user can be granted special
delete
permissions. ZFS doesn't create any delete permissions when a
file is created, instead it uses write_data/execute for permission to
write to a directory and execute to search the directory.
Lets first create a read-only directory and then
give "marks" the ability to delete files.
% ls
-dv test.dir
dr-xr-xr-x 2
ongk
bin 2
Nov 15 14:11 test.dir
0:owner@:add_file/write_data/add_subdirectory/append_data:deny
1:owner@:list_directory/read_data/write_xattr/execute/write_attributes
/write_acl/write_owner:allow
2:group@:add_file/write_data/add_subdirectory/append_data:deny
3:group@:list_directory/read_data/execute:allow
4:everyone@:add_file/write_data/add_subdirectory/append_data/write_xattr
/write_attributes/write_acl/write_owner:deny
5:everyone@:list_directory/read_data/read_xattr/execute/read_attributes
/read_acl/synchronize:allow
Now the directory has the following files:
% ls
-l
total 3
-r--r--r-- 1
ongk
bin 0
Nov 15 14:28 file.1
-r--r--r-- 1
ongk
bin 0
Nov 15 14:28 file.2
-r--r--r-- 1
ongk
bin 0
Nov 15 14:28 file.3
Now lets see if "marks" can delete any of the files?
$ rm file.1
rm: file.1: override protection 444 (yes/no)? y
rm: file.1 not removed: Permission denied
Now lets give "marks" delete permission on just
file.1
% chmod
A+user:marks:delete:allow file.1
% ls
-v file.1
-r--r--r--+ 1 ongk
bin 0
Nov 15 14:28 file.1
0:user:marks:delete:allow
1:owner@:write_data/append_data/execute:deny
2:owner@:read_data/write_xattr/write_attributes/write_acl/write_owner
:allow
3:group@:write_data/append_data/execute:deny
4:group@:read_data:allow
5:everyone@:write_data/append_data/write_xattr/execute/write_attributes
/write_acl/write_owner:deny
6:everyone@:read_data/read_xattr/read_attributes/read_acl/synchronize
:allow
$ rm file.1
rm: file.1: override protection 444 (yes/no)? y
Lets see what a chmod(1) that changes the mode would do to a file with
a ZFS ACL.
We will start out with the following ACL which gives user
bin read_data and write_data permission.
$ ls
-v file.1
-rw-r--r--+ 1 marks
staff 0 Nov 15
10:12 file.1
0:user:bin:read_data/write_data:allow
1:owner@:execute:deny
2:owner@:read_data/write_data/append_data/write_xattr/write_attributes
/write_acl/write_owner:allow
3:group@:write_data/append_data/execute:deny
4:group@:read_data:allow
5:everyone@:write_data/append_data/write_xattr/execute/write_attributes
/write_acl/write_owner:deny
6:everyone@:read_data/read_xattr/read_attributes/read_acl/synchronize
:allow
$ chmod
640 file.1
$ ls
-v file.1
-rw-r-----+ 1 marks
staff 0 Nov 15
10:12 file.1
0:user:bin:write_data:deny
1:user:bin:read_data/write_data:allow
2:owner@:execute:deny
3:owner@:read_data/write_data/append_data/write_xattr/write_attributes
/write_acl/write_owner:allow
4:group@:write_data/append_data/execute:deny
5:group@:read_data:allow
6:everyone@:read_data/write_data/append_data/write_xattr/execute
/write_attributes/write_acl/write_owner:deny
7:everyone@:read_xattr/read_attributes/read_acl/synchronize:allow
In this example ZFS has prepended a
deny ACE to take away write_data
permission. This
is an example of disabling "alternate" access
methods. More details about
how ACEs are disabled are described in
internet
draft.
The
ZFS
admin guide and the
chmod(1)
manpages have many more examples of setting ACLs and how the
inheritance model works.
With the ZFS ACL model access control is no longer limited to the
simple "rwx" model that UNIX has used since its inception.