We do RPM things. Some of those RPM things need the /proc file system. Not forever, but for a short while. So we mount /proc, do something, and unmount. Which works fine.
Until we tried to do it in a container.
When we ran our umount code in the container, we got the following error:
umount: /meta/meta-rpm/build/tmp/work/qemux86_64-redhat-linux/rpmbased-minimal-image/1.0-r0/rootfs/.proc: block devices are not permitted on filesystem. |
SELinux. Running in permissive mode worked fine. In /var/log/messages We have the following message:
May 5 15:22:41 zygarde setroubleshoot[1733179]: SELinux is preventing /meta/meta-rpm/build/tmp/work/qemux86_64-redhat-linux/rpmbased-minimal-image/1.0-r0/recipe-sysroot-native/usr/bin/umount from unmount access on the filesystem .#012#012 Plugin catchall (100. confidence) suggests *******#012#012If you believe that umount should be allowed unmount access on the filesystem by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c ‘umount’ –raw | audit2allow -M my-umount#012# semodule -X 300 -i my-umount.pp
It seems somewhat unbalanced that a container can mount a filesystem, but not unmount one.
As the messages said, we can get by it by building and running a custom policy:
ausearch -c 'umount' --raw | audit2allow -M my-umount semodule -X 300 -i my-umount.pp |
Its overkill, but you can never have too much overkill.
Actually, yes you can. When your goal is to not violate security constraints, opening up a hole is counterproductive.
When we run a container, by default, it gets the labeled as container_runtime_t:
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 ayoung 1736123 1609658 0 18:04 pts/1 00:00:00 grep --color=auto metarpm |
To change the label, run with a different security context. For instancing running podman with –security-opt label=type:metarpm.process like this:
podman run -it --security-opt label=type:metarpm.process -v /dev/fuse:/dev/fuse -v metadata:/meta localhost/metarpm /usr/bin/bash |
Gives ends up with a process labeled system_u:system_r:metarpm_policy.process like this:
system_u:system_r:metarpm_policy.process:s0:c738,c1010 100999 1736385 1736373 0 18:08 pts/0 00:00:00 /usr/bin/bash |
So long as this matches policy, we can do new things. But if we don’t ,we can’t do anything. But writing SELinux policy is a non-trivial amount of learning curve. How can we simplify?
Udica. Pronounced you-DI-tza. Or something like that.
Now…I’m going to cut out some of my trial and error with Udica. Following the instructions alone did not solve my problem, mainly because Udica does not yet understand how to deal with the umount or ioctl erros caused by the container. Here is what worked
- set SELinux in permissive mode, so the operations succeeds, but the errors get caught in the audit log
- as a non-root user,
- start the container and run the bitbake function to generate the errors
- run the podman ps command to get the container id
- redirect the output a file
- stop the container
- as root
- re-enable SELinux
- cat the output of the file generated above into udica, add the flag to read the audit file
- load the generated policy
Here is what that looks like on the terminal. As root
setenforce permissive |
As ayoung
$ podman run -it --security-opt seccomp=unconfined -v /dev/fuse:/dev/fuse -v metadata:/meta localhost/metarpm /usr/bin/bash $ cd /meta/meta-rpm/ $ . ./oe-init-build-env $ bitbake rpmbased-minimal-image |
Leave that running, and go to another terminal as ayoung
$ podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c153bee4a84d localhost/metarpm /usr/bin/bash About a minute ago Up About a minute ago naughty_heyrovsky $ podman inspect c153bee4a84d > container.json <pre> <!-- /wp:html --> <!-- wp:paragraph --> <p>Now as <strong>root</strong></p> <!-- /wp:paragraph --> <!-- wp:html --> <pre lang="bash"> $ cat container.json | udica metarpm --append-rules /var/log/audit/audit.log $ setenforce enforcing $ semodule -i metarpm.cil /usr/share/udica/templates/base_container.cil $ semodule -l | grep meta metarpm |
As the ayoung user, rerun the container with the additional flag
$ podman run -it --security-opt label=type:metarpm.process --security-opt seccomp=unconfined -v /dev/fuse:/dev/fuse -v metadata:/meta localhost/metarpm /usr/bin/bash |
If you look in the process list, you can see the container running:
$ ps -efZ | grep meta unconfined_u:system_r:container_runtime_t:s0-s0:c0.c1023 ayoung 1977102 1609151 0 13:25 pts/0 00:00:00 podman run -it --security-opt label=type:meth unconfined_u:system_r:container_runtime_t:s0-s0:c0.c1023 ayoung 1977115 1977102 0 13:25 pts/0 00:00:00 podman run -it --security-opt label=type:meth system_u:system_r:metarpm.process:s0:c492,c842 100999 1977157 1977145 0 13:25 pts/0 00:00:00 /usr/bin/bash unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 ayoung 1977217 1939598 0 13:26 pts/1 00:00:00 grep --color=auto meta |
If I rerun the bitbake command now (after cleaning the output of the successful permissive run) it will succeed.
Here is the difference between the default output of udica and adding in the flag that pulls in the audit file:
# diff metarpm.cil.orig metarpm.cil 11a12,13 > (allow process proc_t ( file ( ioctl ))) > (allow process proc_t ( filesystem ( unmount ))) |