Passthrought USB TV tuner (DVB RTL2838) to unprivileged container inside Debian Bullseye host

I’m using lxc on a Debian Bullseye server.
I run all my containers (Debian Bullseye ones too) unprivileged with the lxc-unpriv-start command.

I’m trying to use a USB DVB TV tuner (RTL2838) inside my lxc container.
So I have to do some USB passthrough

on host:
lsusb
returns my USB DVB adapter:
Bus 001 Device 003: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T

ls -l /dev/bus/usb/001
returns:
total 0
crw-rw-r-- 1 root root 189, 0 16 mai 21:25 001
crw-rw-r-- 1 root root 189, 1 16 mai 21:25 002
crw-rw-r-- 1 root root 189, 2 16 mai 21:25 003

ls -l /dev/dvb
returns:
total 0
drwxr-xr-x 2 root root 120 16 mai 21:25 adapter0

ls -l /dev/dvb/adapter0
total 0
crw-rw----+ 1 root video 212, 0 16 mai 21:25 demux0
crw-rw----+ 1 root video 212, 1 16 mai 21:25 dvr0
crw-rw----+ 1 root video 212, 3 16 mai 21:25 frontend0
crw-rw----+ 1 root video 212, 2 16 mai 21:25 net0

On the host as non root user:
nano /home/mynonrootusername/.local/share/lxc/mycontainername/config
is set this way:
lxc.include = /usr/share/lxc/config/common.conf
lxc.include = /usr/share/lxc/config/userns.conf
lxc.arch = linux64
lxc.include = /etc/lxc/default.conf
lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.apparmor.profile = unconfined
lxc.rootfs.path = dir:/home/mynonrootusername/.local/share/lxc/mycontainername/rootfs
lxc.uts.name = mycontainername
lxc.cgroup2.devices.allow = c 189:* rwm
lxc.mount.entry = /dev/bus/usb/001 dev/bus/usb/001 none bind,optional,create=dir
lxc.cgroup2.devices.allow= c 212:* rwm
lxc.mount.entry = /dev/dvb/adapter0 dev/dvb/adapter0 none bind,optional,create=dir

On the host as root user:
nano /etc/lxc/default.conf
Is set this way:
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.apparmor.profile = generated

On the host as nonroot user I start my container like this:
lxc-unpriv-start -n mycontainername
lxc-unpriv-attach -n mycontainername
(Please care of the use of -unpriv part)

Inside the container as non root user:
ls -la /dev/bus/usb/001
returns
total 0
drwxr-xr-x 2 nobody nogroup 100 May 16 19:25 .
drwxr-xr-x 3 root root 60 May 16 20:36 …
crw-rw-r-- 1 nobody nogroup 189, 0 May 16 19:25 001
crw-rw-r-- 1 nobody nogroup 189, 1 May 16 19:25 002
crw-rw-r-- 1 nobody nogroup 189, 2 May 16 19:25 003

and
ls -la /dev/dvb
returns
total 0
drwxr-xr-x 3 root root 60 May 16 20:36 .
drwxr-xr-x 7 root root 540 May 16 20:36 …
drwxr-xr-x 2 nobody nogroup 120 May 16 19:25 adapter0

and
ls -la /dev/dvb/adapter0
returns
total 0
drwxr-xr-x 2 nobody nogroup 120 May 16 19:25 .
drwxr-xr-x 3 root root 60 May 16 20:36 …
crw-rw----+ 1 nobody nogroup 212, 0 May 16 19:25 demux0
crw-rw----+ 1 nobody nogroup 212, 1 May 16 19:25 dvr0
crw-rw----+ 1 nobody nogroup 212, 3 May 16 19:25 frontend0
crw-rw----+ 1 nobody nogroup 212, 2 May 16 19:25 net0

but when I launch vdr inside container as non root user:
vdr
I get
vdr: no primary device found - using first device!

What am I missing?

I guess I should do something with the host video group.

As non root user on host :
id
uid=1000(mynonrootuser) gid=1000(mynonrootuser) groupes=1000(mynonrootuser),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev)

So on the host as non root user, I should modify:
nano /home/mynonrootusername/.local/share/lxc/mycontainername/config
is set this way:
lxc.include = /usr/share/lxc/config/common.conf
lxc.include = /usr/share/lxc/config/userns.conf
lxc.arch = linux64
lxc.include = /etc/lxc/default.conf
#lxc.idmap = u 0 100000 65536
#lxc.idmap = g 0 100000 65536
???
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.apparmor.profile = unconfined
lxc.rootfs.path = dir:/home/mynonrootusername/.local/share/lxc/mycontainername/rootfs
lxc.uts.name = mycontainername
lxc.cgroup2.devices.allow = c 189:* rwm
lxc.mount.entry = /dev/bus/usb/001 dev/bus/usb/001 none bind,optional,create=dir
lxc.cgroup2.devices.allow= c 212:* rwm
lxc.mount.entry = /dev/dvb/adapter0 dev/dvb/adapter0 none bind,optional,create=dir

But don’t really understand this now

Because the container is unprivileged the device /dev/dvb/adapter0 being passed through is not writable because its owned by real root (0) on the host which doesn’t exist in the container due to the uid map being used.

What you can do is to duplicate the /dev/dvb/adapter0 device file using the same major and minor numbers and then chown it to the first uid in the container’s uid map. Then update your config to pass that file through rather than the actual /dev/dvb/adapter0 file. This way it should so as being owned by root in the container.

When using LXD this process is automated using the unix-char device type (Instance configuration - LXD documentation)

Hi,
Thank you for your answers (and sorry for my standby time),

Because the container is unprivileged the device /dev/dvb/adapter0 being passed through is not writable because its owned by real root (0) on the host which doesn’t exist in the container due to the uid map being used.

On the test computer I can run vdr without root rights as normal user and it could successfully run/drive the dvb adapter.
I guess it is because of /dev/dvb/adapter0 being part of group “video” too? (see above)

What you can do is to duplicate the /dev/dvb/adapter0 device file using the same major and minor numbers and then chown it to the first uid in the container’s uid map. Then update your config to pass that file through rather than the actual /dev/dvb/adapter0 file. This way it should so as being owned by root in the container.

I tried to extend the mapping to add the “video” group (gid:44) to the container but must have do it wrong:

lxc.idmap = u 0 100000 65535
lxc.idmap = g 0 100000 44
lxc.idmap = g 44 44 1
lxc.idmap = g 45 100045 65490

But this is giving an error at container startup:

tools/lxc_start.c: main: 308 The container failed to start

confile - confile.c:set_config_idmaps:1942 - Read uid map: type u nsid 0 hostid 100000 range 65536
confile - confile.c:set_config_idmaps:1942 - Read uid map: type g nsid 0 hostid 100000 range 44
confile - confile.c:set_config_idmaps:1942 - Read uid map: type g nsid 44 hostid 44 range 1
confile - confile.c:set_config_idmaps:1942 - Read uid map: type g nsid 45 hostid 100045 range 65491
lxccontainer - lxccontainer.c:do_lxcapi_start:979 - Set process title to [lxc monitor] /home/mynonrootuser/.local/share/lxc mycontainername
lxccontainer - lxccontainer.c:wait_on_daemonized_start:840 - First child 5080 exited
apparmor - lsm/apparmor.c:lsm_apparmor_ops_init:1269 - Per-container AppArmor profiles are disabled because the mac_admin capability is missing
lsm - lsm/lsm.c:lsm_init:40 - Initialized LSM security driver AppArmor
conf - conf.c:add_idmap_entry:4462 - Adding id map: type u nsid 0 hostid 100000 range 1
conf - conf.c:add_idmap_entry:4462 - Adding id map: type u nsid 1000 hostid 1000 range 1
conf - conf.c:add_idmap_entry:4462 - Adding id map: type g nsid 0 hostid 100000 range 1
conf - conf.c:add_idmap_entry:4462 - Adding id map: type g nsid 1000 hostid 1000 range 1
conf - conf.c:add_idmap_entry:4462 - Adding id map: type g nsid 5 hostid 100005 range 1
conf - conf.c:idmaptool_on_path_and_privileged:2728 - The binary "/usr/bin/newuidmap" does have the setuid bit set
conf - conf.c:idmaptool_on_path_and_privileged:2728 - The binary "/usr/bin/newgidmap" does have the setuid bit set
conf - conf.c:lxc_map_ids:2796 - Functional newuidmap and newgidmap binary found
utils - utils.c:lxc_switch_uid_gid:1398 - Switched to gid 0
utils - utils.c:lxc_switch_uid_gid:1407 - Switched to uid 0
utils - utils.c:lxc_setgroups:1420 - Dropped additional groups
terminal - terminal.c:lxc_terminal_peer_default:672 - Using terminal "/dev/null" as proxy
terminal - terminal.c:lxc_terminal_peer_default:675 - File descriptor for "/dev/null" does not refer to a terminal
terminal - terminal.c:lxc_terminal_create_foreign:901 - Failed to allocate proxy terminal
start - start.c:lxc_init:813 - Failed to create console
start - start.c:__lxc_start:1945 - Failed to initialize container "mycontainername"
conf - conf.c:run_script_argv:330 - Executing script "/usr/share/lxcfs/lxc.reboot.hook" for container "mycontainername", config section "lxc"
lxccontainer - lxccontainer.c:wait_on_daemonized_start:851 - No such file or directory - Failed to receive the container state
lxc_start - tools/lxc_start.c:main:308 - The container failed to start
lxc_start - tools/lxc_start.c:main:311 - To get more details, run the container in foreground mode
lxc_start - tools/lxc_start.c:main:313 - Additional information can be obtained by setting the --logfile and --logpriority options

https://annuel2.framapad.org/p/a7n45800u6-9urs?lang=fr

Do you think there is a way to make this working, what could I be missing?
Is it because I’m trying to get my containers having different idmap rules?
Should I use the security.idmap.isolated=true variable somehow?

Maybe on the host I should modify the file to add the “video” group line in /etc/subgid file?:

nano /etc/subuid
myusername:100000:65536
nano /etc/subgid
myusername:100000:65536
video:44:1