Skip to content

Conversation

@ca-hu
Copy link

@ca-hu ca-hu commented Sep 25, 2025

This PR is more to start a discussion if that would be a good way to handle the problem or not.

Why?

Currently, there is no proper way to specify in the SELinux policy if the target chroot path of a rpm installation should be labelled the same as the main root dir, or if it should use the label that is defined in the policy.

For example, if we install a package with /var/lib/machines/openqa1 as chroot directory, rpm will set the labels as if it was the topmost root directory. If the user runs restorecon -Rv /var/lib/machines/openqa1, the labels get reset to the label defined for that path, instead of the chroot environment it was set to by rpm.

This can lead to issues, as the labels on the file systems set by rpm and that of policy mismatch. Issues include things like permission being denied, transactions not being complete, etc.

Quick example of the problem

User wants to install packages to /var/lib/machines/openqa1 and run systemd machined from that chroot afterwards. This example uses zypper, and zypper uses rpm --root /var/lib/machines/openqa1 underneath.

In the selinux policy we have the rule that says that files in /var/lib/machines should be systemd_machined_var_lib_t:

$ semanage fcontext -l | grep "/var/lib/machines"
/var/lib/machines(/.*)?                            all files          system_u:object_r:systemd_machined_var_lib_t:s0

The user executes:

$ zypper -n --root /var/lib/machines/openqa1 addrepo http://download.opensuse.org/tumbleweed/repo/oss/ defaultrepo
$ zypper -n --root /var/lib/machines/openqa1 --gpg-auto-import-keys refresh
$ zypper -n --root /var/lib/machines/openqa1 install --no-recommends -ly aaa_base systemd shadow zypper openSUSE-release vim iproute2 iputils openQA-single-instance sudo net-tools curl wget ca-certificates-mozilla qemu-arm qemu-ppc qemu-x86 openQA-bootstrap

The labels that are generated are inconsistent, since the rpm selinux plugin only sets the labels of the files that are in the database:

$ ls -alZ /var/lib/machines/openqa1/
total 20
drwxr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0 122 Nov  5 16:10 .
drwxr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0  28 Nov  5 16:29 ..
lrwxrwxrwx. 1 root root system_u:object_r:bin_t:s0                            7 Sep 24 12:51 bin -> usr/bin
dr-xr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 boot
drwxr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 dev
drwxr-xr-x. 1 root root system_u:object_r:etc_t:s0                          432 Nov  5 16:10 etc
dr-xr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 home
lrwxrwxrwx. 1 root root system_u:object_r:lib_t:s0                            7 Sep 24 12:51 lib -> usr/lib
lrwxrwxrwx. 1 root root system_u:object_r:lib_t:s0                            9 Sep 24 12:51 lib64 -> usr/lib64
dr-xr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 mnt
dr-xr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 opt
drwxr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 proc
drwx------. 1 root root system_u:object_r:admin_home_t:s0                    18 Nov  5 16:10 root
drwxr-xr-x. 1 root root system_u:object_r:var_run_t:s0                       24 Sep 24 12:51 run
lrwxrwxrwx. 1 root root system_u:object_r:bin_t:s0                            8 Sep 24 12:51 sbin -> usr/sbin
dr-xr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 srv
dr-xr-xr-x. 1 root root unconfined_u:object_r:systemd_machined_var_lib_t:s0   0 Nov  5 16:10 sys
drwxrwxrwt. 1 root root system_u:object_r:tmp_t:s0                            0 Sep 24 12:51 tmp
drwxr-xr-x. 1 root root system_u:object_r:usr_t:s0                          124 Nov  5 16:10 usr
drwxr-xr-x. 1 root root system_u:object_r:var_t:s0                           74 Nov  5 16:10 var

This means, that as soon as something runs restorecon the labels will be set to systemd_machined_var_lib_t. Which means that after the first install the labels are not consistent with the policy.

Additionally, some package installations fail due to the label mismatch, which can be observed in this bugreport: https://bugzilla.suse.com/show_bug.cgi?id=1248857

What we propose as solution is (for chroot scenarios only!) a way in the selinux policy to control if it is okay that rpm sets the labels inconsistently, or uses the label that is defined in the policy.

Proposed solution

Add chroot handling to the SELinux plugin as follows:

  • If there is a label in the policy for the full non-chroot path, apply it.
  • If the policy specifies <<none>> for the full non-chroot-path, apply the labels that are in the policy assuming the chroot is like the main root dir. This is what the rpm plugin did before this change.

Non-chroot transactions should stay exactly the same.

What is in this PR

With this commit it is now possible to specify in the SELinux policy, if the target path shall be SELinux confined as usual (e.g. regular chroot environment on the host), or if the target path confinement shall be handled by another user space application (e.g. containers, systemd-machined)

To do that, users or policy writers can set <<none>> in the policy for a chroot path to specify they want to have the same labels as in the rootdir, and if they don't want it, they can set the label to a specific one.

Example:

This would tell the rpm plugin to use the same labels in the chroot /var/lib/machines/openqa1 as it would in the rootdir.

semanage fcontext -a '/var/lib/machines/openqa1(/.*)?' -t '<<none>>'
// afterwards, run rpm with the path as chroot

With that, restorecon will also skip this directory, since there is no default label for it defined.

While setting a fixed label, would result in the plugin to use the fixed label:

semanage fcontext -a '/var/lib/machines/openqa1(/.*)?' -t 'systemd_machined_var_lib_t'
// afterwards, run rpm with the path as chroot, it will set the
// labels of the files as systemd_machined_var_lib_t

@ca-hu ca-hu changed the title If existent, apply SELinux label from full non-chroot path RFC: If existent, apply SELinux label from full non-chroot path Sep 25, 2025
@ca-hu ca-hu force-pushed the selinux-chroot-handling branch 3 times, most recently from 5e5c526 to d20cca2 Compare September 25, 2025 19:42
@ca-hu ca-hu force-pushed the selinux-chroot-handling branch from d20cca2 to c0dcfac Compare November 5, 2025 14:32
What?

Add chroot handling to the SELinux plugin as follows:
- If there is a label in the policy for the full non-chroot path, apply it.
- If the policy specifies <<none>> for the full non-chroot-path, apply the
  labels that are in the policy assuming the chroot is like the main root dir.
  This is what the rpm plugin did before this change.

Non-chroot transactions should stay exactly the same.

Why?

Currently, there is no proper way to specify in the SELinux policy
if the target chroot path of a rpm installation should be labelled
the same as the main root dir, or if it should use the label that
is defined in the policy.

For example, if we install a package with `/var/lib/machines/openqa1`
as chroot directory, rpm will set the labels as if it was the topmost root
directory. If the user runs `restorecon -Rv /var/lib/machines/openqa1`,
the labels get reset to the label defined for that path, instead of the
chroot environment it was set to by rpm.

This can lead to issues, as the labels on the file systems set by rpm
and that of policy mismatch. Issues include things like permission being
denied, transactions not being complete, etc.
For example: https://bugzilla.suse.com/show_bug.cgi?id=1248857

With this commit it is now possible to specify in the SELinux policy, if the
target path shall be SELinux confined as usual (e.g. regular chroot
environment on the host), or if the target path confinement shall be
handled by another user space application (e.g. containers,
systemd-machined)

To do that, users or policy writers can set '<<none>>' in the policy
for a chroot path to specify they want to have the same labels as in the
rootdir, and if they don't want it, they can set the label to a specific
one.

Example:

This would tell the rpm plugin to use the same labels
in the chroot `/var/lib/machines/openqa1` as it would in the rootdir.
```
semanage fcontext -a '/var/lib/machines/openqa1(/.*)?' -t '<<none>>'
// afterwards, run rpm with the path as chroot
```
With that, `restorecon` will also skip this directory, since there
is no default label for it defined.

While setting a fixed label, would result in the plugin to use
the fixed label:
```
semanage fcontext -a '/var/lib/machines/openqa1(/.*)?' -t 'systemd_machined_var_lib_t'
// afterwards, run rpm with the path as chroot, it will set the
// labels of the files as systemd_machined_var_lib_t
```
@ca-hu ca-hu force-pushed the selinux-chroot-handling branch from c0dcfac to c33fc14 Compare November 5, 2025 14:36
@ca-hu ca-hu marked this pull request as ready for review November 5, 2025 15:21
@ca-hu ca-hu requested a review from a team as a code owner November 5, 2025 15:21
@ca-hu ca-hu requested review from dmnks and removed request for a team November 5, 2025 15:21
@ca-hu
Copy link
Author

ca-hu commented Nov 5, 2025

also RFC for @zpytela and @vmojzis since i assume fedora has the same problem

please let me know if i am missing something or it is a really bad idea, thanks :)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant