Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save marcosscriven/0d76f820feae933828dad0f0d99b57b4 to your computer and use it in GitHub Desktop.
Save marcosscriven/0d76f820feae933828dad0f0d99b57b4 to your computer and use it in GitHub Desktop.
Intel QuickSync passthrough to an unprivileged LXC container running plex.

Running Plex in an Unprivileged LXC with Intel QuickSync Passthrough

First setup an unprivileged Ubuntu container with Plex Media Server installed. Inside the container take note of the id of the plex group.

# Your Plex group's ID may be different
$ getent group plex | cut -d : -f3
998

Shutdown the container for now while we update its configuration. The /dev/dri/renderD128 is the device responsible for the Intel QuickSync VAAPI for hardware video encoding. Listing the /dev/dri directory you will see something like this:

$ ls -la /dev/dri
drwxr-xr-x  3 root root        100 Jul 10 19:23 .
drwxr-xr-x 22 root root       4.4K Jul 16 23:57 ..
drwxr-xr-x  2 root root         80 Jul 10 19:23 by-path
crw-rw----  1 root video  226,   0 Jul 10 19:23 card0
crw-rw----  1 root render 226, 128 Jul 10 19:23 renderD128

Take note that the renderD128 device is a character device denoted by the 'c' at the beginning of its permission attributes. The devices major and minor numbers are 226, 128 respectively, and is accessible by the render group. Take note of the render group's id as well.

# Again your render group ID may be different
$ getent group render | cut -d : -f3
108

Open the container's configuration file. Within a Proxmox installation it will be located in /etc/pve/lxc and have a name corresponding to the container's assigned ID.

Append the following to the configuration:

# Allow the container access to the renderD128 device identified by its type and major/minor numbers.
# The attributes 'rwm' allow the container to perform read, write and mknod operations on the device.
#
# For Proxmox 6.x (LXC 3.x):
lxc.cgroup.devices.allow: c 226:128 rwm
#
# For Proxmox 7.x (LXC 4.x uses CGroupV2):
lxc.cgroup2.devices.allow: c 226:128 rwm

# Bind mount the device from the host to the container
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file 0 0

At this point, if the container were running, the device would be visible in the container but belong to the user and group nobody and nogroup respectively. The Plex user would not be able to write to it.

LXC helps isolate containers by mapping a set of user and group ids on the host to a set of user and group ids in the container. Normally a set of ids outside the POSIX range of 0-65535 is used on the host to map container user and groups. By default, Proxmox has LXC configured to map host user and groups 100000-165535 to container user and groups 0-65535. In the event of a container escape exploit, the malicious user from the container would not have permissions to modify the host filesystem.

So now its clear that the render group (108) on the host does not fall within the range mapped to the container. This is why the device is mounted with nobody/nogroup. We can update the container configuration further to use a custom id mapping that will map the plex group in the container (998) to the render group on the host (108).

# /etc/pve/lxc/CONTAINER_ID.conf
# In older versions of LXC, the configuration was lxc.id_map
# Syntax:
# Column 1: u/g define map for user or group ids
# Column 2: Range start for container
# Column 3: Range start for host
# Column 4: Length of range
# i.e., g 0 100000 997 = Map gids 100000-100997 on host to 0-997 in container
lxc.idmap: u 0 100000 65536
lxc.idmap: g 0 100000 997
lxc.idmap: g 998 108 1
lxc.idmap: g 999 100999 64537

The key is to define a jump in the mapping that has only one group overlapping with the host range (render <-> plex).

Finally, we have to update the /etc/subgid file to allow us to apply this custom mapping. The subuid/subgid files define which id mappings a host user can make while using LXC. The first column is the host user, the second column is the start of the host id range to be mapped, and the third column is the number of ids that can be allocated.

# /etc/subgid
root:100000:65536
root:108:1  # Add this line

A Note on Security Implications

Files on the host filesystem that are accessible by the render group could now be modified in the event of a container escape vulnerability. Few files belong to that group and this configuration is much more constrained than a privileged container, so I am comfortable with this level of risk. Make sure you understand the potential consequences before mapping container uid/gids into the commonly-used range of host ids.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment