* [Discussion] systemd-run0 policy issue
@ 2026-08-13 13:09 Jan Janasek
2026-08-13 13:23 ` Stephen Smalley
2026-08-13 13:26 ` Dominick Grift
0 siblings, 2 replies; 6+ messages in thread
From: Jan Janasek @ 2026-08-13 13:09 UTC (permalink / raw)
To: selinux
Hi everyone,
(I originally posted this to the fedora selinux list, but was advised
that this is the proper ML for such architectural discussions.)
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
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Discussion] systemd-run0 policy issue
2026-08-13 13:09 [Discussion] systemd-run0 policy issue Jan Janasek
@ 2026-08-13 13:23 ` Stephen Smalley
2026-08-13 13:35 ` Dominick Grift
2026-08-13 13:26 ` Dominick Grift
1 sibling, 1 reply; 6+ messages in thread
From: Stephen Smalley @ 2026-08-13 13:23 UTC (permalink / raw)
To: Jan Janasek; +Cc: selinux
On Thu, Aug 13, 2026 at 9:11 AM Jan Janasek <jjanasek@redhat.com> wrote:
>
> Hi everyone,
>
> (I originally posted this to the fedora selinux list, but was advised
> that this is the proper ML for such architectural discussions.)
>
> 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.
Option 1 seems best to me, but this issue might also be related:
https://github.com/SELinuxProject/selinux/issues/485
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Discussion] systemd-run0 policy issue
2026-08-13 13:09 [Discussion] systemd-run0 policy issue Jan Janasek
2026-08-13 13:23 ` Stephen Smalley
@ 2026-08-13 13:26 ` Dominick Grift
1 sibling, 0 replies; 6+ messages in thread
From: Dominick Grift @ 2026-08-13 13:26 UTC (permalink / raw)
To: Jan Janasek; +Cc: selinux
Jan Janasek <jjanasek@redhat.com> writes:
> Hi everyone,
>
> (I originally posted this to the fedora selinux list, but was advised
> that this is the proper ML for such architectural discussions.)
>
> 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.
I brought this up before too:
https://github.com/SELinuxProject/selinux/issues/485
My idea was to extend pam_selinux context configuration with a optional
"pamname" field. As far as I know that can be retrieved by libpam and then
be associated with a context based on a combination of source, target and
now also optionally pamname.
system_u:system_r:init_t:s0 user_r:user_systemd_t:s0 "systemd-user"
system_u:system_r:init_t:s0 user_r:user_t:s0 "systemd-run0"
This could then also nicely work natively with systemd's PAMName=
functionality.
... In theory at least and that is all that is.
>
> Best regards,
> Jan Janasek
>
>
--
gpg --auto-key-locate clear,nodefault,wkd --locate-external-keys dominick.grift@defensec.nl
Key fingerprint = FCD2 3660 5D6B 9D27 7FC6 E0FF DA7E 521F 10F6 4098
Dominick Grift
Mastodon: @kcinimod@defensec.nl
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Discussion] systemd-run0 policy issue
2026-08-13 13:23 ` Stephen Smalley
@ 2026-08-13 13:35 ` Dominick Grift
2026-08-13 13:40 ` Stephen Smalley
0 siblings, 1 reply; 6+ messages in thread
From: Dominick Grift @ 2026-08-13 13:35 UTC (permalink / raw)
To: Stephen Smalley; +Cc: Jan Janasek, selinux
Stephen Smalley <stephen.smalley.work@gmail.com> writes:
> On Thu, Aug 13, 2026 at 9:11 AM Jan Janasek <jjanasek@redhat.com> wrote:
>>
>> Hi everyone,
>>
>> (I originally posted this to the fedora selinux list, but was advised
>> that this is the proper ML for such architectural discussions.)
>>
>> 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.
>
> Option 1 seems best to me, but this issue might also be related:
> https://github.com/SELinuxProject/selinux/issues/485
Disagree because this functionality is not that much more than some
abstraction/wrapper around systemd's PAMName= functionality. In my view it would
be more robust to make any solution to this issue also work for the
underlying PAMName= technology it is built on.
>
--
gpg --auto-key-locate clear,nodefault,wkd --locate-external-keys dominick.grift@defensec.nl
Key fingerprint = FCD2 3660 5D6B 9D27 7FC6 E0FF DA7E 521F 10F6 4098
Dominick Grift
Mastodon: @kcinimod@defensec.nl
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Discussion] systemd-run0 policy issue
2026-08-13 13:35 ` Dominick Grift
@ 2026-08-13 13:40 ` Stephen Smalley
2026-08-13 13:49 ` Dominick Grift
0 siblings, 1 reply; 6+ messages in thread
From: Stephen Smalley @ 2026-08-13 13:40 UTC (permalink / raw)
To: Dominick Grift; +Cc: Jan Janasek, selinux
On Thu, Aug 13, 2026 at 9:35 AM Dominick Grift
<dominick.grift@defensec.nl> wrote:
>
> Stephen Smalley <stephen.smalley.work@gmail.com> writes:
>
> > On Thu, Aug 13, 2026 at 9:11 AM Jan Janasek <jjanasek@redhat.com> wrote:
> >>
> >> Hi everyone,
> >>
> >> (I originally posted this to the fedora selinux list, but was advised
> >> that this is the proper ML for such architectural discussions.)
> >>
> >> 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.
> >
> > Option 1 seems best to me, but this issue might also be related:
> > https://github.com/SELinuxProject/selinux/issues/485
>
> Disagree because this functionality is not that much more than some
> abstraction/wrapper around systemd's PAMName= functionality. In my view it would
> be more robust to make any solution to this issue also work for the
> underlying PAMName= technology it is built on.
I'm perfectly willing to stand corrected by those who may know more
about this particular area.
I was merely basing my opinion on what the original poster provided as
context, so feel free to
ignore me ;)
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Discussion] systemd-run0 policy issue
2026-08-13 13:40 ` Stephen Smalley
@ 2026-08-13 13:49 ` Dominick Grift
0 siblings, 0 replies; 6+ messages in thread
From: Dominick Grift @ 2026-08-13 13:49 UTC (permalink / raw)
To: Stephen Smalley; +Cc: Jan Janasek, selinux
Stephen Smalley <stephen.smalley.work@gmail.com> writes:
> On Thu, Aug 13, 2026 at 9:35 AM Dominick Grift
> <dominick.grift@defensec.nl> wrote:
>>
>> Stephen Smalley <stephen.smalley.work@gmail.com> writes:
>>
>> > On Thu, Aug 13, 2026 at 9:11 AM Jan Janasek <jjanasek@redhat.com> wrote:
>> >>
>> >> Hi everyone,
>> >>
>> >> (I originally posted this to the fedora selinux list, but was advised
>> >> that this is the proper ML for such architectural discussions.)
>> >>
>> >> 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.
>> >
>> > Option 1 seems best to me, but this issue might also be related:
>> > https://github.com/SELinuxProject/selinux/issues/485
>>
>> Disagree because this functionality is not that much more than some
>> abstraction/wrapper around systemd's PAMName= functionality. In my view it would
>> be more robust to make any solution to this issue also work for the
>> underlying PAMName= technology it is built on.
>
> I'm perfectly willing to stand corrected by those who may know more
> about this particular area.
> I was merely basing my opinion on what the original poster provided as
> context, so feel free to
> ignore me ;)
The discussion has to start somewhere and this is a good opportunity to
clarify why this might be better fixed on a pam_selinux level. The above
argument is one but if we address this on a pam_selinux level then that
might also work out nicely for systemd-stdio-bridge.
I don't know if its technically feasible (Cow was looking into it but
went AWOL although he is still on the chat so I will ask him about it)
but otherwise it might be implemented similarly to name-based
transitions. These are pam-name-based transitions, optional too.
--
gpg --auto-key-locate clear,nodefault,wkd --locate-external-keys dominick.grift@defensec.nl
Key fingerprint = FCD2 3660 5D6B 9D27 7FC6 E0FF DA7E 521F 10F6 4098
Dominick Grift
Mastodon: @kcinimod@defensec.nl
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-13 13:49 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 13:09 [Discussion] systemd-run0 policy issue Jan Janasek
2026-08-13 13:23 ` Stephen Smalley
2026-08-13 13:35 ` Dominick Grift
2026-08-13 13:40 ` Stephen Smalley
2026-08-13 13:49 ` Dominick Grift
2026-08-13 13:26 ` Dominick Grift
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.