Friday, May 23, 2014

ZFS delegated permissions

Finally, here's my first topic on ZFS.
Actually, being didactic isn't my goal at this moment.
I'm just recording something that's very useful.

In general, it may be a good idea to delegate a few basic ZFS permissions to advanced users so that they can manage themselves their dataset tree.

The primary source of information is zfs_allow(1M).
I'm just going to present my own examples.

At first, consider the example of home directories.
As we know, Solaris 11 generally takes care of everything.
It usually creates a ZFS dataset for the user under rpool/export/home.
So for user admin1 there will be the ZFS dataset rpool/export/home/admin1.
This is the case for local accounts, but not remote ones (NIS for instance).
On the remote (networked) case the ZFS dataset tree will be elsewhere.
In this case it's necessary login to the remote host.
But everything else is analogous.

Hence, suppose I'm taking the remote (networked) case.
The NFS server is nfs-1 which has a separate export pool.
The ZFS dataset tree in question will be export/home/admin1.

It's better to group ZFS permissions for a more fine-grained control.
These groupings are called permission sets and their names begin with @.
There should be a "separation" to provide flexibility at no compromise.
Non-privileged users shall not impair their own home directories.
I propose something similar to the following:

nfs-1# zfs allow -s @generic                  \
    "                                         \
    create,mount,send,receive,hold,release,   \
    snapshot,rollback,diff,userprop           \
    "                                         \
    export/home/admin1

nfs-1# zfs allow -s @descendent               \
    "                                         \
    share,sharenfs,                           \
    rename,destroy,                           \
    readonly,compression,                     \
    quota,reservation,                        \
    clone,promote                             \
    "                                         \  
    !!$

nfs-1# zfs allow export/home/user1
---- Permissions on zone/nfs-1/export/home/user1 ----

Permission sets:
    @descendent
        clone,compression,destroy,promote,quota,
        readonly,rename,reservation,share,sharenfs 
    @generic
        create,diff,hold,mount,receive,release,
        rollback,send,snapshot,userprop

NOTE
The special sequence !!$ is particular to BASH. It's a combination of the event designator !! (referring to the previous command) with the word designator $ (referring to the last argument). I other words it means the "last argument of the previous command".
Once those permission sets are fully prepared, it suffices to apply them.
Note however the usage of the -l and -d options.

nfs-1# zfs allow -ldu user1 @generic export/home/user1
nfs-1# zfs allow -du user1 @descendent !!$

nfs-1# zfs allow rpool/export/home/user1
---- Permissions on zone/nfs-1/export/home/user1 ----

Permission sets:
    @descendent
        ...
    @generic
        ...

Descendent permissions:
    user user1 @descendent
 

Local+Descendent permissions:
    user user1 @generic


NOTE
It's important to set the appropriate directory ownership and mode at the mountpoint level as well, as usual. On the previous example that means setting the ownership of export/home/user dataset's mountpoint to user1 by a typical chown command.
But I made a small glitch on the above proposal.
I have defined the permission sets at each home directory level.
There may be thousands of home directories to repeat the task.
There ought to be a better way; and in fact there is!

I forgot to take advantage of ZFS inheritance.
I should have declared permission sets at an upper level.
The ideal place is at /export/home the closest common ancestor.

The final result should be similar to:

nfs-1# zfs allow rpool/export/home/user2
---- Permissions on zone/nfs-1/export/home/user2 ----

Descendent permissions:
    user user2 @descendent


Local+Descendent permissions:
    user user2 @generic


---- Permissions on zone/nfs-1/export/home ----

Permission sets:
    @generic

        ...
    @descendent

        ...

By the way, to remove the delegations and permission sets, it's very easy.
Just use the unallow counterpart of the allow command.

nfs-1# zfs unallow -s @generic export/home/user1
nfs-1# zfs unallow -s @descendent !!$

On the above case everything went fine because the parent dataset is propagating its own permission sets of the same name. Otherwise, the associations (if any) should have been removed beforehand as in:

nfs-1# zfs unallow -ldu user1 @generic export/home/user1
nfs-1# zfs unallow -du user1 @descendent !!$

Naturally I have offered a minimal example. It may be necessary to better distribute the delegated permissions in order to achieve better granularity. For instance, the @descendent is very comprehensive, but perhaps the compression, quota and reservation should belong to a different permission set such as @space. In this scenario, to keep all the delegated privileges of user1 as before I would have to change one of the commands to:

nfs-1# zfs allow -du user1 @descendent,@space export/home/user1