Documentation for version: v0.2.0
To reduce the security thread that can generate from running workloads at the edge, flotta is configured by default to run the workloads in rootless containers. While this increases the device’s security and prevents pods from escaping containers and having full access to the device, it also impacts a few use cases where there is a need to access mounted host devices such as webcams, sensors or other plugged devices, since the containers are no longer able to access the mounted devices.
Even in a rootless container and with SELinux enabled, it is possible for workloads to have access to a mounted device, provided that the host and the workload are configured properly.
flotta
userTo run the workloads in a rootless mode, the flotta agent creates the flotta
user when installed. At the same time, the device agent leverages on this account to start the workloads in rootless mode. For a workload to mount a host mountpoint to the container, it first needs the flotta
host user to have access to the device.
This can be achieved by adding the device’s group as a supplementary group to the flotta
user. In case of a webcam, the group is video
:
[root@fedora ~]# ls -la /dev/video2
crw-rw----. 1 root video 81, 2 Jul 22 16:14 /dev/video2
And adding the supplementary group is a simple command:
[root@fedora ~]# usermod -a -G video flotta
Since we have changed the flotta group list, we will restart the systemd user service that hosts the podman instance running with the flotta
user. This change is required to propagate the changes to the running proceses under the flotta
account:
[root@device ~]# systemctl restart user@$(id flotta -g).service
Adding the supplementary group will allow the flotta
user to access the device from a file system perspective, but we need to enable the access to the containers. For that purpose we have to make additional adjustments to the workload.
Adding the supplementary group video
to the flotta
user will clear the access to the device from a filesystem perspective. But since the workloads run in a container, we also need to look at how to instruct the container runtime to use the same groups from the flotta
user in the container’s user so that the host file system allows access from the user running inside the container. For security reasons, the user id and group id running inside the container is different than the one in the host.
Propagating the groupIds enables container processes access to host files without being blocked by the host’s file system due to invalid access rights. To do so, Flotta leverages on a feature from the crun
runtime container that, when used, instructs the runtime to keep the same group Ids from the user’s host to the container. In Flotta this feature is eanbled by defining in your workload the following annotation podman/run.oci.keep_original_groups: "1"
:
apiVersion: management.project-flotta.io/v1alpha1
kind: EdgeWorkload
metadata:
name: webcam
annotations:
podman/run.oci.keep_original_groups: "1"
spec:
...
...
With SELinux enabled by default, rootless containers have further restrictions to access the host file system. For mounted devices this is no different. There are two approaches on how to allow a workload to access a mounted device with SELinux’s blessings:
seLinuxOptions
in the pod’s spec
field to instruct the runtime to define the type as spc_t
to what is in essence disabling SELinux in the container. Since we are running a rootless container, the security impact is limited to what the flotta
user is able to do. Here is a detailed explanation of what it is and can do for those initiated in the art of container runtime.Let’s see them in more detail:
The process is to run the workload and identify the missing SELinux rules that prevent the workload from operating the device and finally configure SELinux to add the new rules. The process relies on the audit2allow
utility to capture the denial attempts in /var/log/audit/audit.log
and generate the rules to allow the access.
The following rules are an example of the outcome of running the audit2allow
utility after various attempts to run a workload that interacts with a webcam:
module v4linux 1.0;
require {
type v4l_device_t;
type container_t;
class chr_file { getattr ioctl map open read write };
}
#============= container_t ==============
#!!!! This avc can be allowed using the boolean 'container_use_devices'
allow container_t v4l_device_t:chr_file { getattr ioctl open read write };
allow container_t v4l_device_t:chr_file map;
In some cases, setting the container_use_devices
boolean to true in SELinux (setsebool container_use_devices on
) might be sufficient to allow containers from interacting with the devices. As you can see in the example above, additionally to the operations allowed by the boolean of getattr
, ioctl
, open
, read
and write
, the map
action is also required in the case of the webcam.
The downside of this approach is that requires additional configuration changes to the device and also to run the workload a few times until the correct rules have been generated, but when achieved it can be used to configure all hosts with the same device as part of the onboarding and configuration process even before installing Flotta. From a security perspective it can be an appealing option.
For those who want a more straight forward approach and don’t want to mess with SELinux rules for each device, there is a special SELinux label type named Super Privilege Container
or spc_t
that in essence it disables SELinux in the container. Here is an example of a workload that accesses a webcam and uses this type to overcome the different SELinux types between the process running in the container and the device.
apiVersion: management.project-flotta.io/v1alpha1
kind: EdgeWorkload
metadata:
name: webcam
annotations:
podman/run.oci.keep_original_groups: "1"
spec:
deviceSelector:
matchLabels:
app: webcam
type: pod
pod:
spec:
containers:
- image: quay.io/jordigilh/ffmpeg:latest
name: fedora
volumeMounts:
- mountPath: /dev/video2
name: video
securityContext:
seLinuxOptions:
type: 'spc_t'
restartPolicy: Always
volumes:
- name: video
hostPath:
path: /dev/video2
type: File