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.

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.

This way doesn’t work for me. Do you have any other suggestions?