SELinux Security Module development
 help / color / mirror / Atom feed
From: Dominick Grift <dominick.grift@defensec.nl>
To: Stephen Smalley <stephen.smalley.work@gmail.com>
Cc: Jan Janasek <jjanasek@redhat.com>,  selinux@vger.kernel.org
Subject: Re: [Discussion] systemd-run0 policy issue
Date: Thu, 13 Aug 2026 15:49:23 +0200	[thread overview]
Message-ID: <87se4ip1uk.fsf@defensec.nl> (raw)
In-Reply-To: <CAEjxPJ6C8hcCr-WwZBehSnFRCL_5WPp5QnNWKz48Lq41wGO9uQ@mail.gmail.com> (Stephen Smalley's message of "Thu, 13 Aug 2026 09:40:49 -0400")

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

  reply	other threads:[~2026-08-13 13:49 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2026-08-13 13:26 ` Dominick Grift

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87se4ip1uk.fsf@defensec.nl \
    --to=dominick.grift@defensec.nl \
    --cc=jjanasek@redhat.com \
    --cc=selinux@vger.kernel.org \
    --cc=stephen.smalley.work@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox