How can I determine what permissions my user is missing for receiving a ZFS dataset?

I have a FreeNAS (11.1-U1) and a FreeBSD (11.1-RELEASE-p6) machine. On the FreeNAS I'd like to zfs receive recursive snapshots as a non-root user with delegated privileges. This appears to work well for most of the child-datasets. But iocage's data datasets, which can be mounted into the jail and administered from there, they fail:

root@freebsd:~> zfs send -RI "dozer@2018-02-21" "dozer@2018-03-08"  | ssh -T -i /root/backup_key backupuser@freenas zfs receive -dvuF neo/backups/freebsd
receiving incremental stream of dozer@2018-03-03 into neo/backups/freebsd@2018-03-03
received 312B stream in 1 seconds (312B/sec)
receiving incremental stream of dozer@2018-03-07 into neo/backups/freebsd@2018-03-07
received 312B stream in 1 seconds (312B/sec)
receiving incremental stream of dozer@2018-03-08 into neo/backups/freebsd@2018-03-08
received 312B stream in 1 seconds (312B/sec)
receiving incremental stream of dozer/ROOT@2018-03-03 into neo/backups/freebsd/ROOT@2018-03-03
.
.
.
receiving incremental stream of dozer/iocage/jails/owncloud/root@2018-03-08 into neo/backups/freebsd/iocage/jails/owncloud/root@2018-03-08
received 578MB stream in 110 seconds (5.25MB/sec)
receiving incremental stream of dozer/iocage/jails/owncloud/root/data@2018-03-03 into neo/backups/freebsd/iocage/jails/owncloud/root/data@2018-03-03
cannot receive incremental stream: permission denied
warning: cannot send 'dozer/iocage/jails/owncloud/root/data@2018-03-03': signal received
warning: cannot send 'dozer/iocage/jails/owncloud/root/data@2018-03-07': Broken pipe
warning: cannot send 'dozer/iocage/jails/owncloud/root/data@2018-03-08': Broken pipe

The permissions of that particular child are exactly the same as the one of the parent dataset:

root@freenas:~ # zfs allow neo/backups/freebsd/iocage/jails/owncloud/root/data
---- Permissions on neo/backups/freebsd -----------------------------
Local+Descendent permissions:
        user backupuser atime,compression,create,dedup,exec,jailed,mount,mountpoint,quota,receive,rename,reservation,setuid,userprop

Running the zfs receive on the FreeNAS as root works as expected.

What delegated privileges does my user need to receive the jailed datasets of iocage and, more generally, is there a way to make zfs receive give out a more detailed error message which tells you what permission is missing?


Solution 1:

When troubleshooting permission problems arising from zfs commands, analyze the zfs operation in terms of its component steps.

The sample command of zfs receive -duvF unpacks into several steps. Two of those flags do not relate to any special permissions:

-d affects the naming of the new dataset (if any)
-v enables verbose output

The other two do.

-F means the filesystem will be rolled back to the initial snapshot of the incremental transfer before the receive starts
-u means the filesystem will not be mounted after the receive finishes

My hunch is that you're missing the rollback permission. The -F flag in your command implies that a zfs rollback will be performed, and your zfs allow does not list rollback.

In the general case, one can make deductive guesses about the permissions necessary for a given zfs command.

The man page for zfs points out:

Permission names are the same as ZFS subcommand and property names.

and ...

Permissions are generally the ability to use a ZFS subcommand or change a ZFS property. The following permissions are available:

   NAME              TYPE          NOTES
   allow             subcommand    Must also have the permission
                                   that is being allowed
   clone             subcommand    Must also have the 'create'
                                   ability and 'mount' ability in
                                   the origin file system
   create            subcommand    Must also have the 'mount'
                                   ability
   destroy           subcommand    Must also have the 'mount'
                                   ability
   diff              subcommand    Allows lookup of paths within a
                                   dataset given an object number,
                                   and the ability to create
                                   snapshots necessary to 'zfs diff'
   hold              subcommand    Allows adding a user hold to a
                                   snapshot
   mount             subcommand    Allows mount/umount of ZFS
                                   datasets
   promote           subcommand    Must also have the 'mount' and
                                   'promote' ability in the origin
                                   file system
   receive           subcommand    Must also have the 'mount' and
                                   'create' ability
   release           subcommand    Allows releasing a user hold
                                   which might destroy the snapshot
   rename            subcommand    Must also have the 'mount' and
                                   'create' ability in the new
                                   parent
   rollback          subcommand    Must also have the 'mount'
                                   ability
   send              subcommand
   share             subcommand    Allows sharing file systems over
                                   the NFS protocol
   snapshot          subcommand    Must also have the 'mount'
                                   ability
   groupquota        other         Allows accessing any
                                   groupquota@... property
   groupused         other         Allows reading any groupused@...
                                   property
   userprop          other         Allows changing any user property
   userquota         other         Allows accessing any
                                   userquota@... property
   userused          other         Allows reading any userused@...
                                   property
   aclinherit        property
   aclmode           property
   atime             property
   canmount          property
   casesensitivity   property
   checksum          property
   compression       property
   copies            property
   dedup             property
   devices           property
   exec              property
   filesystem_limit  property
   logbias           property
   jailed            property
   mlslabel          property
   mountpoint        property
   nbmand            property
   normalization     property
   primarycache      property
   quota             property
   readonly          property
   recordsize        property
   refquota          property
   refreservation    property
   reservation       property
   secondarycache    property
   setuid            property
   sharenfs          property
   sharesmb          property
   snapdir           property
   snapshot_limit    property
   sync              property
   utf8only          property
   version           property
   volblocksize      property
   volsize           property
   vscan             property
   xattr             property

The example at hand includes the -u flag, so the file system will not be mounted at the end of the receive operation. However, if -u were absent, the filesystem would be mounted at the end of the receive process. Tellingly, the receive permission requires the mount permission.

Because a zfs mount operation will auto-create any necessary mountpoints, it is possible for a user to have zfs permission to mount the dataset, but not have filesystem permissions to create the mountpoint. In the case of zfs mount, the mount will fail. In a zfs create or rename operation, the file system will be created or renamed, but it will remain unmounted if the user does not have sufficient filesystem permissions to create the mountpoint.

Similarly, a zfs rename command could fail for lack of permissions at several points within the rename operation. Loosely expressed, the component steps might be:

1) unmount the filesystem (mount permission)
2) create a new filesystem (create permission)
3) map the filesystem meta-data into the new name (rename permission)

A fourth step is to re-mount the newly-named filesystem at its new, possibly changed mountpoint, which again uses the mount permission, and possibly filesystem permissions to create the new mountpoint.

I have not tested such tricks, but it can be seen that zfs distinguishes between create and rename permissions, and also between mount and mountpoint permissions. One imagines it might be possible to allow a user to create new filesystems, but once created, the user cannot rename them. For filesystems with inherited mountpoints, renaming a filesystem often also will rename the filesystem's mountpoint, as when renaming tank/usr/local to tank/usr/local.OLD changes the mountpoint from /usr/local to /usr/local.OLD.

The separation of mount or rename from mountpoint permissions means that a user could be allowed to rename a filesystem but not allowed to change its mountpoint. Or vice versa, to be able to change where a filesystem is mounted, but not be able to change the filesystem's name.

The richness of its filesystem operations and delegation of those operations, coupled with the granularity of permissions, can make zfs somewhat challenging, but also very powerful.