Thursday, April 16, 2009

Understanding and managing NFSv4 ACLs

Using EON/Opensolaris and ZFS for storage will at some point cause you to cross paths with NFSv4 Access Control Lists. The control available through ACLs are really granular and powerful but they are also hard to manage and a bit confusing. Here i'll share my methods of handling ACLs which requires some pre-requisite reading to help understand the Compact Access codes:
add_file w, add_subdirectory p, append_data p, delete d , delete_child D , execute x , list_directory r , read_acl c , read_attributes a , read_data r , read_xattr R , write_xattr W , write_data w , write_attributes A , write_acl C , write_owner o
Inheritance compact codes:(remember i on a directory causes a recursive inheritance)
file_inherit f , dir_inherit d , inherit_only i , no_propagate n
ACL _set codes:
full_set = rwxpdDaARWcCos = all permissions
modify_set = rwxpdDaARWc--s = all permissions except write_acl, write_owner
read_set = r-----a-R-c--- = read_data, read_attributes, read_xattr, read_acl
write_set = -w-p---A-W---- = write_data, append_data, write_attributes, write_xattr
NFSv4 ACL legend (read from top, down and exit on first match)
                 owner@:--------------:-------:deny
                 owner@:rwxp---A-W-Co-:-------:allow
                 group@:-w-p----------:-------:deny
                 group@:r-x-----------:-------:allow
              everyone@:-w-p---A-W-Co-:-------:deny
              everyone@:r-x---a-R-c--s:-------:allow
                        ||||||||||||||:|||||||
           (r)read data +|||||||||||||:||||||+ (I)nherited
           (w)rite data -+||||||||||||:|||||+- (F)ailed access (audit)
              e(x)ecute --+|||||||||||:||||+-- (S)uccess access (audit)
               a(p)pend ---+||||||||||:|||+--- (n)o propagate
               (d)elete ----+|||||||||:||+---- (i)nherit only
         (D)elete child -----+||||||||:|+----- (d)irectory inherit
          read (a)ttrib ------+|||||||:+------ (f)ile inherit
         write (A)ttrib -------+||||||
           (R)ead xattr --------+|||||
          (W)rite xattr ---------+||||
             read a(c)l ----------+|||
            write a(C)l -----------+||
         change (o)wner ------------+|
                   sync -------------+
If I create a file/folder (foo) via a windows client on a SMB/CIFS share the permissions typically resemble.
eon:/deep/tank#ls -Vd foo
d---------+  2 admin    stor           2 Apr 20 14:12 foo
user:admin:rwxpdDaARWcCos:-------:allow
group:2147483648:rwxpdDaARWcCos:-------:allow
This works fine for the owner (admin) but in a case where multiple people (family) use the storage, adding user access and more control over sharing is usually required. So how do I simply add the capability needed? If I wish to modify this(above), I always start by going back to default values
eon:/deep/tank#chmod A- foo
eon:/deep/tank#ls -Vd foo
d---------   2 admin    stor           2 Apr 20 14:12 foo
owner@:rwxp----------:-------:deny
owner@:-------A-W-Co-:-------:allow
group@:rwxp----------:-------:deny
group@:--------------:-------:allow
everyone@:rwxp---A-W-Co-:-------:deny
everyone@:------a-R-c--s:-------:allow
I then copy and paste them directly into a terminal or script (vi /tmp/bar) for trial and error and simply flip the bits I wish to test on or off. Note I'm using A= which will wipe and replace with whatever I define. With A+ or A-, it adds or removes the matched values. So my script will look like this after the above is copied
chmod -R A=\
owner@:rwxp----------:-------:deny,\
owner@:-------A-W-Co-:-------:allow,\
group@:rwxp----------:-------:deny,\
group@:--------------:-------:allow,\
everyone@:rwxp---A-W-Co-:-------:deny,\
everyone@:------a-R-c--s:-------:allow \
foo
Let's modify group:allow to have write_set = -w-p---A-W----
chmod -R A=\
owner@:rwxp----------:-------:deny,\
owner@:-------A-W-Co-:-------:allow,\
group@:--------------:-------:deny,\
group@:-w-p---A-W----:-------:allow,\
everyone@:rwxp---A-W-Co-:-------:deny,\
everyone@:------a-R-c--s:-------:allow \
foo
Running the above
eon:/deep/tank#sh -x /tmp/bar
+ chmod -R A=owner@:rwxp----------:-------:deny,owner@:-------A-W-Co-:-------:allow,group@:--------------:-------:deny,group@:-w-p---A-W----:-------:allow,everyone@:rwxp---A-W-Co-:-------:deny,everyone@:------a-R-c--s:-------:allow foo
eon:/deep/tank#ls -Vd foo/
d----w----+  2 admin    stor           2 Apr 20 14:12 foo/
owner@:rwxp----------:-------:deny
owner@:-------A-W-Co-:-------:allow
group@:--------------:-------:deny
group@:-w-p---A-W----:-------:allow
everyone@:rwxp---A-W-Co-:-------:deny
everyone@:------a-R-c--s:-------:allow
Adding a user (webservd) at layer 5, 6 with full_set permissions
eon:/deep/tank#eon:/deep/tank#chmod A+user:webservd:full_set:d:allow,user:webservd:full_set:f:allow foo
eon:/deep/tank#ls -Vd foo
d----w----+  2 admin    stor           2 Apr 20 14:12 foo
user:webservd:rwxpdDaARWcCos:-d-----:allow
user:webservd:rwxpdDaARWcCos:f------:allow
owner@:rwxp----------:-------:deny
owner@:-------A-W-Co-:-------:allow
group@:--------------:-------:deny
group@:-w-p---A-W----:-------:allow
everyone@:rwxp---A-W-Co-:-------:deny
everyone@:------a-R-c--s:-------:allow
Ooops, that's level 1, 2 so let's undo this by simply repeating the command with A- instead of A+.
eon:/deep/tank#chmod A-user:webservd:full_set:d:allow,user:webservd:full_set:f:allow foo
eon:/deep/tank#ls -Vd foo
d----w----+  2 admin    stor           2 Apr 20 14:12 foo
owner@:rwxp----------:-------:deny
owner@:-------A-W-Co-:-------:allow
group@:--------------:-------:deny
group@:-w-p---A-W----:-------:allow
everyone@:rwxp---A-W-Co-:-------:deny
everyone@:------a-R-c--s:-------:allow
Then lets fix it by repeating the command with A5+ instead of A-
eon:/deep/tank#chmod A5+user:webservd:full_set:d:allow,user:webservd:full_set:f:allow foo
eon:/deep/tank#ls -Vd foo
d----w----+  2 admin    stor           2 Apr 20 14:12 foo
owner@:rwxp----------:-------:deny
owner@:-------A-W-Co-:-------:allow
group@:--------------:-------:deny
group@:-w-p---A-W----:-------:allow
everyone@:rwxp---A-W-Co-:-------:deny
user:webservd:rwxpdDaARWcCos:-d-----:allow
user:webservd:rwxpdDaARWcCos:f------:allow
everyone@:------a-R-c--s:-------:allow
This covers adding, deleting, modifying and replacing NFSv4 ACLs. Hope that provides some guidance in case you have to tangle with NFSv4 ACLs. The more exercise you get with NFSv4 ACLs the more familiar you'll be with getting it to do what you want.