Mounting a Loop Device in a LXD Container

I created a profile as follows:

lxc profile create nsmount
lxc profile set nsmount raw.lxc lxc.aa_profile=unconfined
lxc profile device add nsmount fuse unix-char path=/dev/fuse
lxc profile device add nsmount loop-control unix-char path=/dev/loop-control

I then created a container:

lxc launch ubuntu:22.04 test-lxd --profile default --profile untagged -c boot.autostart=true -c security.privileged=true -c limits.memory=2048MB -c limits.cpu.allowance=20%

After some reading, I tried to allow the creation of loop devices like this:

printf 'lxc.cgroup.devices.allow = c 10:237 rw\nlxc.cgroup.devices.allow = b 7:* rw' | lxc config set test-lxd raw.lxc -

I then applied the new nsmount profile:

lxc profile assign test-lxd default,nsmount,untagged

I connected to the container:

lxc exec test-lxd bash


Any idea what I need to change to get the loop device above to mount?

You are not allowed to run the mount syscall inside of an unprivileged container, as a malformed/malicious source disk could be used to crash the host kernel.

You could mount this on the host and then pass the resulting mount into the container using the disk device type.

Or if you’re happy to take the risk then look at using the syscall interception security.syscalls.intercept.mount.* feature:

I think you may have missed the point. I am trying to point virtual disks that are entirely inside the container or even ISO’s entirely inside the container, so they are not based on the host. I frequently do mount folder paths from outside the container from the host and that is easy.

Ok I see. Either way my point about mount syscall not being allowed stands because a malformed disk image could crash the host kernel. This limitation doesn’t apply to lxd vms though.

I did notice that. I have a couple LXD VMs and all is fine. I just wanted to find out if it was possible to “loop” mount a device in a regular LXD container. It seems many folks have gotten close. I think my example is close. I just thought you might know what I am missing. The concept is easy. The syntax is abstract.

Its not possible without using the mount syscall interception feature linked to above.

I read about the feature and added it to my test container created above and it gives me a permission error. It looks what you said should work, but apparently in what I provided you above, something else is wrong. Again:

lxc launch ubuntu:22.04 test-lxd --profile default --profile untagged -c boot.autostart=true -c security.privileged=true -c limits.memory=2048MB -c limits.cpu.allowance=20% 
printf 'lxc.cgroup.devices.allow = c 10:237 rw\nlxc.cgroup.devices.allow = b 7:* rw' | lxc config set test-lxd raw.lxc -
lxc profile create nsmount
lxc profile set nsmount raw.lxc lxc.aa_profile=unconfined
lxc profile device add nsmount fuse unix-char path=/dev/fuse
lxc profile device add nsmount loop-control unix-char path=/dev/loop-control
lxc profile assign test-lxd default,nsmount,untagged

I also added the security.syscalls.intercept.mount switch on the container when I created it:

lxc launch ubuntu:22.04 test-lxd --profile default --profile untagged -c boot.autostart=true -c security.privileged=true -c  security.syscalls.intercept.mount=true -c limits.memory=2048MB -c limits.cpu.allowance=20% ```

I got this working as follows:

lxc init images:ubuntu/jammy c1
lxc config device add c1 loop-control unix-char path=/dev/loop-control
lxc config device add c1 loop1 unix-block path=/dev/loop1 #May have to repeat this until you add a free device
lxc config set c1 \
    security.syscalls.intercept.mount=true \
    security.syscalls.intercept.mount.allowed=ext4
lxc start c1
lxc exec c1 -- truncate -s 1GiB loop.img
lxc exec c1 -- mkfs.ext4 loop.img
lxc exec c1 -- mount -t ext4 -o loop loop.img /mnt/
lxc exec c1 -- mount | grep /mnt
/root/loop.img on /mnt type ext4 (rw,relatime)
2 Likes

Thanks Thomas. It worked as you said. My missing part of the puzzle was in not understanding the security.syscalls.intercept.mount.allowed=ext4 was needed and also the loop device is squirrely. For me, this was mainly an exercise, since I generally mount data from the host which is preferred. I was able to use your instruction to mount both a virtual disk image file and also an ISO. I was unable to have more than one loop device mounted at once even if I added loop devices to the container. I am not sure why. The issue that got me interested in this was I ran into more than one self-hosted application that failed to run in a LXD container because they were trying to access a loop device.

You would need to pass in multiple loop devices, e.g.

ls /dev/loop*
/dev/loop0  /dev/loop2  /dev/loop4  /dev/loop6  /dev/loop-control
/dev/loop1  /dev/loop3  /dev/loop5  /dev/loop7

lxc config device add c1 loop1 unix-block path=/dev/loop1
lxc config device add c1 loop2 unix-block path=/dev/loop2
lxc config device add c1 loop3 unix-block path=/dev/loop3
...

I tried this to no avail. Note above: I was unable to have more than one loop device mounted at once even if I added loop devices to the container. I am not sure why.

Hrm, that approach worked for me when I tried it.