As part of my transition from using a combination of Linux and FreeBSD for our home servers to being exclusively FreeBSD, I wanted to update how I did backups from my public server, bree, to the internal storage server, rivendell. Previously, I had done this with a home grown script which used rsync to transfer updates to the storage server overnight. This solution worked just fine, but was not the most efficient (see: rsync.net: ZFS Replication to the cloud is finally here-and it’s fast). While I didn’t intend to replicate to rsync.net I wanted to leverage ZFS since I am now going FreeBSD to FreeBSD.
zxfer -dFkPv -o copies=2,compression=lzjb -T firstname.lastname@example.org -R storage backup01/pools
Having to open up the
root account on my storage server, no matter how I restricted it to IP address, keys, whatever, makes me really uncomfortable and a show-stopper for me. But I thought I could do better. I have limited experience using restricted-shells to limit access to servers before and I knew that ZFS allows for delegating permissions to non-root users so I decided to give it a shot.
TL;DR: It can work.
The configuration had a few phases to it:
- Create a new restricted user account on my backup server and configure the commands that
zxferneeds access to in the restricted shell
- Create the destination zfs filesystem to receive the mirror and configure the delegated permissions for the backup user
- Set up access to the backup server from the source server via SSH
- Make slight modification to
zxferto allow it to run
zfscommand from the
PATHinstead of hardcoding the path in the script
Setting up the restricted user
I created a new user on the backup system named
zbackup that would be my restricted user for receiving the backups. The goal was for this user to be as limited as possible. It should only be allowed to run the commands necessary for
zxfer to do its job. I landed on using
rzsh as the restricted shell as it was the first one I got working with the correct environment. I set up a directory to hold binaries that the
zbackup user was allowed to use.
root@storage$ mkdir /usr/local/restricted_bin
root@storage$ ln -s /sbin/zfs /usr/local/restricted_bin/zfs
root@storage$ ln -s /usr/bin/uname /usr/local/restricted_bin/uname
I then set up the
.zshenv file for the
zbackup user to restrict the user to that directory for executables.
Setting up the destination zfs filesystem
I already had a zfs filesystem that was devoted to backups so I made a new zfs filesystem underneath it to hold these new backups and be a point where I could set delegation points for permissions. Then, through trial and error, I figured out all the permissions I had to delegate to the
zbackup user on the filesystem to allow
zxfer to work
root@storage$ zfs create nas/backup/bree-zxfer
root@storage$ chown zbackup:zbackup /nas/backup/bree-zxfer
root@storage$ zfs allow -u zbackup atime,canmount,casesensitivity,checksum,compression,copies,create,
(I figured out the list of actions and properties that I needed to delegate by having
zxfer dump the
zfs create command it was trying to run on the backup system when it failed.)
Update: I forgot 1 thing that is critical to making this work. You need to ensure that non-root users are allowed to mount filesystems. This can be accomplished by adding the following line to your
/etc/sysctl.conf and rebooting:
Remote access to the backup server
Nothing fancy here. On my source server, I created a new SSH keypair for the
root user (no problem with running the source
zfs command as
root). I then copied the public half of that key to the
authorized_keys file of the
zbackup user on the backup server. At this point, I could ssh from my source server to the backup server as the
zbackup user. But when logged in to the backup server, the only commands that could be run are those in the
/usr/local/restricted_bin directory (
Tweak zxfer script to remove hard coded path in zfs commands
One of the limitations (intentional) of a restricted shell is that the restricted user is not allowed to specify a full pathname for any commands. Only commands located in their
PATH can be run. Unfortunately, while the
zbackup user has the
zfs command in their
PATH, it is referenced as
/sbin/zfs in the
zxfer script. To work around this, I modified the
zxfer script to not use the path of
zfs directly and assume that
zfs will be in the path. This was only in 2 places of the script. If you do a quick search for
/sbin/zfs you will find them.
Moment of truth!
After all this, I was now able to run any number of commands to mirror my source servers
zfs filesystems (with snapshots) to my backup server.
root@source$ zxfer -dFPv -T zbackup@storage -N zroot/git nas/backup/bree-zxfer
root@source$ zxfer -dFPv -T zbackup@storage -R zroot/var nas/backup/bree-zxfer
And best of all, the storage server does not have SSH enabled for root. Success.