Hi all,
I am forwarding this to the upstream list for broader discussion. ---------- Forwarded message --------- From: Jan Janasek jjanasek@redhat.com Date: Tue, Aug 4, 2026 at 11:11 AM Subject: [Discussion] systemd-run0 policy issue To: selinux-internal-list selinux-internal-list@redhat.com
Hi everyone,
We’ve been analyzing the integration of systemd-run0 with SELinux and hit a fundamental architectural roadblock, related to bug https://bugzilla.redhat.com/show_bug.cgi?id=2359828. Unlike sudo, where the parent process is the user's shell (allowing PAM to easily calculate and transition the context), run0 delegates execution via D-Bus to PID 1 (init_t). This breaks standard SELinux PAM transitions and creates a severe design challenge for maintaining user isolation. (Currently, wrapping commands via run0 --via-shell acts as a temporary workaround, but we are looking for a robust architectural fix.)
When pam_selinux.so attempts to transition the user directly into their target domain over a specific binary, it immediately triggers strict policy conflicts. To demonstrate the exact mechanics of what happens when PAM forces this transition, I wrote a Python reproducer (labeled as bin_t and running in the unconfined_t domain) that uses setexeccon to simulate a transition (e.g., to staff_t) and then calls os.execl("/usr/bin/dnf5", ...). It triggers these distinct SELinux denials:
# 1. The Transition Denial: The kernel blocks the domain transition type=AVC msg=audit(07/23/2026 09:23:00.584:636) : avc: denied { transition } for pid=4945 comm=python3 path=/usr/bin/dnf5 dev="nvme0n1p2" ino=146474 scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=staff_u:staff_r:staff_t:s0-s0:c0.c1023 tclass=process permissive=1
# 2. The Entrypoint Denial: The target domain lacks entrypoint permissions on the binary type=AVC msg=audit(07/23/2026 09:23:00.584:637) : avc: denied { entrypoint } for pid=4945 comm=python3 path=/usr/bin/dnf5 dev="nvme0n1p2" ino=146474 scontext=staff_u:staff_r:staff_t:s0-s0:c0.c1023 tcontext=system_u:object_r:rpm_exec_t:s0 tclass=file permissive=1
Fixing this purely in the Base Policy would require writing transition and entrypoint rules for every single interactive tool a user might run. This is unmaintainable, massively expands the attack surface, and breaks the principle of least privilege.
We explored a few potential ways to handle this and would like the community's input:
1*. SELinux-aware run0 (Upstream fix). *Modify run0/systemd to be natively SELinux-aware. Instead of relying blindly on pam_selinux.so (which only sees init_t as the caller), run0 could dynamically calculate the correct target context based on the originating user's session and explicitly set it for the execution (e.g., via the SELinuxContext= property of the transient unit). *2. Leave pam_selinux.so commented out in /lib/pam.d/systemd-run0.* If we leave pam_selinux.so disabled in the run0 PAM stack, processes simply inherit init_t. This means we would have to write static type_transition rules into the base policy for every single interactive binary users might run. (For example, run0 dnf only works right now because an init_t -> rpm_exec_t transition historically exists). *3. Enable PAM + Explicit Default Contexts (A Flawed PoC).* We tried keeping pam_selinux.so enabled and explicitly defining the transition path in /etc/selinux/targeted/contexts/users/unconfined_u: system_r:init_t:s0 unconfined_r:systemd_run_t:s0 unconfined_r:unconfined_t:s0 While this correctly routes run0 payloads into an isolated domain (systemd_run_t) without polluting init_t, it causes unacceptable collateral damage. The SELinux get_default_context API only sees the caller (init_t) and the user (unconfined_u). Because run0 sessions and systemd --user sessions share these exact same inputs, this global mapping breaks systemd --user instances by throwing them into systemd_run_t instead of unconfined_t.
Given that we cannot safely rely on global policy rules (which expands PID 1's attack surface) or PAM default contexts (which breaks user sessions), we'd love to hear your thoughts. Are there other approaches or configuration options we might have missed? Or is pushing for run0 to become natively SELinux-aware the most viable path forward?
Any thoughts or guidance would be greatly appreciated.
Best regards, Jan Janasek
Jan Janasek wrote:
1*. SELinux-aware run0 (Upstream fix). *Modify run0/systemd to be natively SELinux-aware. Instead of relying blindly on pam_selinux.so (which only sees init_t as the caller), run0 could dynamically calculate the correct target context based on the originating user's session and explicitly set it for the execution (e.g., via the SELinuxContext= property of the transient unit).
I think this would be doable. We do have selinux integration in various places already. Has anyone started working on this?
Would this require the policy to allow transitions from init_t to the context of any interactive program, like described in the quote below?
This means we would have to write static type_transition rules into the base policy for every single interactive binary users might run. (For example, run0 dnf only works right now because an init_t -> rpm_exec_t transition historically exists).
Zbyszek
(posting from hyperkitty since I wasn't subscribed to this list yet)
This indeed does sound doable; as I understand it we 'only' need to ensure that we set the appropriate user context and the rest of the transitions would go automatically.
As I understand it it won't need transitions from init_t to the context of any interactive program, only to the appropriate user contexts which I believe are already in the policy. After that executables should be verified by the user context and then the entrypoints of those executables will transition them further.
If no one is working on this yet I can pick it up. I've done some previous SELinux work in systemd.
Regards, Simon
selinux@lists.fedoraproject.org