Understanding SELinux context transitions

Hello,

I’m new to SELinux and there’s something I don’t understand.

I have created a systemd service that periodically runs a script which calls rsync to backup some files.

The script runs fine when called interactively, but fails with SELinux errors when called from the systemd unit.

Looking at the audit logs, the reason is that:

audit[47882]: AVC avc:  denied  { read } for  pid=47882 comm="rsync" name=".rustup" dev="zfs" ino=592561 scontext=system_u:system_r:rsync_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=lnk_file permissive=1`

setroubleshoot suggests to run either setsebool -P rsync_export_all_ro 1 or setsebool -P rsync_full_access 1, but neither of this seems right to me because if were to start an rsync daemon in the future, I do not want them daemon to have unlimited access.

So I think I need to create a new SELinux type for my backup script, assign a new context to the script, and modify my SELinux policy to allow transitions from init_t to this new script. Is that correct so far?

The part that I’m struggling with is this. If rsync has assigned rsync_exec_t as its role:

root@nlp ~ [4]# ls -Z /usr/bin/rsync
system_u:object_r:rsync_exec_t:s0 /usr/bin/rsync*

why is the context transitioning to this role when systemd exec’s rsync, but not when my shell exec’s rsync interactively? And how do I make sure that when rsync is called by my backup script, it does not transition to the rsync_exec_t type, but remains in my newly defined backup type?

Is there a way for me to start a process in “SE Linux debug mode” (or something similar), where I am getting audit messages for every SELinux check (rather than just those that would fail)? I would very much like to see when a role transition happens to better understand what’s going on…

It looks like type transitions to rsync_exec_t are permitted from the the following types:

$ sesearch -T -t rsync_exec_t
type_transition cluster_t rsync_exec_t:process rsync_t;
type_transition condor_startd_t rsync_exec_t:process rsync_t;
type_transition glusterd_t rsync_exec_t:process rsync_t;
type_transition inetd_t rsync_exec_t:process rsync_t;
type_transition init_t rsync_exec_t:process rsync_t;
type_transition initrc_t rsync_exec_t:process rsync_t;
type_transition kdumpctl_t rsync_exec_t:process rsync_t;
type_transition openshift_initrc_t rsync_exec_t:process rsync_t;
type_transition stunnel_t rsync_exec_t:process rsync_t;
type_transition svc_run_t rsync_exec_t:process rsync_t;

Your shell runs as unconfined_t, which is not in the above list, so the commands it spawns will not transition to rsync_t.

$ ps -o context=,command= $$
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 -bash

That sounds right to me. FWIW, someone did something somewhat similar here: SELinux is preventing python from name_connect access on the tcp_socket port 443 - #6 by nnaaaaaaaaaaaa

For systemd services the relevant rule is

type_transition init_t rsync_exec_t:process rsync_t;

which says that a process which is runing as init_t starts a process from an executable file with the rsync_exec_t label, it will let the new process run with rsync_t context.

The systemd process runs as


ps -Z 1
LABEL                               PID TTY      STAT   TIME COMMAND
system_u:system_r:init_t:s0           1 ?        Ss     0:01 /usr/lib/systemd/systemd --switched-root --system --deserialize=51 rh

ignore systemd_u and systemd_r part. They are not used in the targeted profile.

1 Like

It looks like type transitions to rsync_exec_t are permitted from the the following types:
[…]
Your shell runs as unconfined_t, which is not in the above list, so it will not transition to rsync_t.

Hmm. That does explain it, but it feels extremely odd to me.

If I understand correctly, the transition from high (unconfined) privilege to lower (rsync_exec_t) privilege is attempted, but fails because it is forbidden by default and not explicitly allowed.

Isn’t that a very risky default chance? Why would I want to prohibit a process from shedding privileges?

ignore systemd_u and systemd_r part. They are not used in the targeted profile.

Thanks! Is that just in context of my specific question, or can I totally disregard this whenever thinking about SELinux in Fedora?

Because it breaks things (a lot of things). :slightly_smiling_face: There have been attempts to get confined users to work, but as far as I know, they have not been successful in the general case. So Fedora Linux defaults to a “targeted” policy that confines a lot of system services, but not much else.

The label have four parts separated by colon: They are

  • user usually with a -u suffix
  • role usually with a -r suffix
  • type usually with a -t suffix
  • range

In the targeted profile only thetype part is used

It is not attempted because there is no transition rule for unrestricted_t process to make the transition from rsync_exec_t ( the label of the program file) to rsync_t (the selinux context of the process). If a type transition fails, there would be an AVC message in the journal.

In other words, the rsync process will run as unconfied_t if started by an unconfined process.

Nothing on your system should be labelled as unlabeled_t, so that’s likely the issue here. I think that the offending folder is ~/.rustup, so try running

$ restorecon -vR ~/.rustup

and see if that fixes anything.

Not quite, but running

$ sudo semodule --disable_dontaudit --build

will show you messages that would typically be blocked by a dont_audit rule, and

$ sudo setenforce Permissive

will temporarily disable SELinux enforcing, so you can see all rules that a program would violate, instead of just stopping at the first error (like most programs do).

Ah, maybe I’m getting confused between what SELinux does in general, and what the Fedora policy does.

So if I wanted to constrain what a binary /usr/bin/foo can do, and use the Fedora targeted policy, I would have to:

  1. Define a new type, say foo_t
  2. Assign foo_t as the context for /usr/bin/foo
  3. Determine all security contexts from which /usr/bin/foo might get called, and define a transition role to allow the transition
  4. If I have missed a transition, then I’ll get an AVC denial, but the process will still run, just in the original context rather than foo_t.

Is that correct?

If so, are there other SELinux policies where instead of (3) I can define something along the lines of “transitions to lower privileged types are always allowed”, and where instead of (4) the process does not run in the original security context, but instead fails to start?

It sounds about right to me,[1] but I’m not a SELinux expert. I’ve just dabbled with it enough to be dangerous. :slightly_smiling_face:

What you are describing is called Multi-Level Security (MLS). It is extremely complex and it really isn’t worth the trouble to maintain. I do not recommend trying it.

There is a compromise that does work with Fedora Linux’s default targeted mode. You can define categories for your processes without having to enable the full Multi-Level Security mode. There is a very brief introduction to using SELinux Categories on Fedora Linux in the “Mandatory Access Control” section of Kubernetes with CRI-O on Fedora Linux 39.


  1. From what I’ve observed, files tend to be labeled with <something>_exec_t, but the running processes end up with <something>_t. I think it is just a naming convention. ↩︎