Using btrfs receive inside a container

I’m trying to reform how I do backups and the idea was to start using btrfs receive/send and to do everything from inside containers. It sounded straightforward enough, but I seem to be running into all kinds of issues…

(I’m using alpine 3.15 as the host and running lxd 4.22)

So far I have a priviledged container (idmap: both 0 0) and I’ve added the devices to it

lxc profile create privileged
lxc profile edit privileged <<'EOF'
description: "Map the root user to the host one (which is exactly as safe as it sounds)"
config:
  raw.idmap: both 0 0
EOF

lxc launch images:alpine/3.15/cloud test -p default -p privileged
lxc exec test -- apk add btrfs-progs

mkfs.btrfs -m raid1 -d raid1 -L 'Data' /dev/sdb1 /dev/sdc1
lxc config device add test btrfs-control unix-char  path=/dev/btrfs-control
lxc config device add test sdb           unix-block path=/dev/sdb
lxc config device add test sdb1          unix-block path=/dev/sdb1
lxc config device add test sdc           unix-block path=/dev/sdc
lxc config device add test sdc1          unix-block path=/dev/sdc1
lxc restart test

At this point I can read from (and I assume write to) from the devices

# lxc exec test -- sh -c 'dd if=/dev/sdb1 bs=1 count=32 | base64'
32+0 records in
32+0 records out
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

I thought I should be all set at this point, but it turns out I am still unable to mount or to run btrfs device scan

# lxc exec test -- su -c 'mount /dev/sdb1 /mnt'
mount: permission denied (are you root?)
# lxc exec test -- su -c 'btrfs device scan'
Scanning for Btrfs filesystems
ERROR: device scan failed on '/dev/sdc1': Operation not permitted
ERROR: device scan failed on '/dev/sdb1': Operation not permitted
ERROR: there were 2 errors while registering devices

I’m not sure whether my approach is wrong or whether what I plan to do is plain impossible… can you help me out?

I suspect there are kernel restrictions that apply as soon as you run inside of a user namespace, so having the real root user mapped through may not be enough.

You may need to actually use security.privileged=true along with raw.apparmor=mount, to allow for direct mounting.

Though an alternative here may be to:

  • Drop that privileged profile entirely
  • Set security.syscalls.intercept.mount=true
  • Set security.syscalls.intercept.mount.allowed=btrfs
  • Restart the instance

This may then allow for the mount call to work fine.

It seems to work with (just) security.privileged=true. Thanks!

The apparmor may be necessary if apparmor is instaled (tried installing it and adding raw.apparmor=mount and it worked - didn’t try the inverse).

The two security.syscalls settings don’t seem to have any effect one way or another.

If I wanted to understand more amout this topic, would just diving deeper into the lxd docs be enough or would you recommend I read on cgroups (or something else)?

In this case, it’s mostly a user namespace thing more than cgroups.
The user namespace comes with a lot of security restrictions as any user can set one up.

Privileged containers on the other hand have very little security restrictions. They can be quite dangerous as a result though.

The interception bits allow for unprivileged containers to get temporary elevated privileges mediated by LXD on the host so it can be a viable alternative to having to make the whole thing privileged.

And indeed, the apparmor bits wouldn’t apply here as your host distro and kernel doesn’t support it.