SELinux Cheat Sheet

Reading SELinux status

Command Description Examples
cat /etc/selinux/config Get SELinux boot configuration # cat /etc/selinux/config

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
# See also:
# https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/using_selinux/changing-selinux-states-and-modes_using-selinux#changing-selinux-modes-at-boot-time_changing-selinux-states-and-modes
#
# NOTE: Up to RHEL 8 release included, SELINUX=disabled would also
# fully disable SELinux during boot. If you need a system with SELinux
# fully disabled instead of SELinux running with no policy loaded, you
# need to pass selinux=0 to the kernel command line. You can use grubby
# to persistently set the bootloader to boot with selinux=0:
#
# grubby --update-kernel ALL --args selinux=0
#
# To revert back to SELinux enabled:
#
# grubby --update-kernel ALL --remove-args selinux
#
SELINUX=enforcing
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
cat /proc/cmdline Check if SELinux is disabled on boot # cat /proc/cmdline
BOOT_IMAGE=(hd0,gpt3)/vmlinuz-5.14.0-427.26.1.el9_4.x86_64 root=UUID=aec1c1e8-3576-4eb2-ab62-f62984e655a2 console=tty0 console=ttyS0,115200n8 no_timer_check net.ifnames=0 crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M selinux=0
getenforce Get current enforcement status # getenforce
Enforcing
sestatus Get current SELinux status # sestatus
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Memory protection checking: actual (secure)
Max kernel policy version: 33

Enabling / disabling SELinux

Command Description Examples
setenforce Set current enforcement status # setenforce Enforcing
grubby --update-kernel ALL --args selinux=0 Disable SELinux permanently starting with next boot
grubby --update-kernel ALL --remove-args selinux Enable SELinux permanently starting with next boot (if if was disabled)

Finding SELinux violations

Command Description Examples
journalctl -t setroubleshoot Get journal logs for SELinux issues Jul 19 07:09:41 rhel9-training setroubleshoot[47738]: SELinux is preventing /usr/lib/systemd/systemd from execute access on the file cause-violation. For co>
Jul 19 07:09:41 rhel9-training setroubleshoot[47738]: SELinux is preventing /usr/lib/systemd/systemd from execute access on the file cause-violation.

* Plugin catchall (100. confidence) suggests ******

If you believe that systemd should be allowed execute access on the cause-violation file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c '(iolation)' --raw
grep denied /var/log/audit/audit.log Print raw SELinux denials # grep denied /var/log/audit/audit.log<br >type=AVC msg=audit(1703120795.400:102): avc: denied { execute } for pid=1586 comm="(iolation)" name="cause-violation" dev="vda4" ino=58946753 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=file permissive=0
ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts recent Find recent denials # ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts recent
----
time->Fri Jul 19 07:29:17 2024
type=PROCTITLE msg=audit(1721388557.563:621): proctitle="(iolation)"
type=SYSCALL msg=audit(1721388557.563:621): arch=c000003e syscall=21 success=no exit=-13 a0=7fff382a8ae0 a1=1 a2=0 a3=3 items=0 ppid=1 pid=47806 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="(iolation)" exe="/usr/lib/systemd/systemd" subj=system_u:system_r:init_t:s0 key=(null)
type=AVC msg=audit(1721388557.563:621): avc: denied { execute } for pid=47806 comm="(iolation)" name="cause-violation" dev="vda4" ino=58946753 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=file permissive=0
ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts today Find denials for today # ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts today
----
time->Fri Jul 19 07:29:17 2024
type=PROCTITLE msg=audit(1721388557.563:621): proctitle="(iolation)"
type=SYSCALL msg=audit(1721388557.563:621): arch=c000003e syscall=21 success=no exit=-13 a0=7fff382a8ae0 a1=1 a2=0 a3=3 items=0 ppid=1 pid=47806 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="(iolation)" exe="/usr/lib/systemd/systemd" subj=system_u:system_r:init_t:s0 key=(null)
type=AVC msg=audit(1721388557.563:621): avc: denied { execute } for pid=47806 comm="(iolation)" name="cause-violation" dev="vda4" ino=58946753 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=file permissive=0
sealert -l "*" sealert -a /var/log/audit/audit.log setroubleshoot client tool

Listing SELinux labels

Command Description Examples
seinfo -b List all available SELinux booleans
seinfo -r List all available SELinux rules
seinfo -t List all available SELinux types
seinfo -u List all available SELinux users

Process operations

Command Description Examples
ps aux -Z List all processes and the SELinux label that they run with
ps -fZ --pid $(pgrep -f ) Show the current label of a process # ps -fZ --pid $(pgrep -f open-messages)
LABEL UID PID PPID C STIME TTY TIME CMD
system_u:system_r:unconfined_service_t:s0 root 2236 1 0 08:43 ? 00:00:00 python /opt/tutorial/bin/open-messages

File operations

Command Description Examples
ls -al -Z List all files in this directory and their SELinux label
chron -R -t tmp_t /test Change SELinux type temporarily for the directory, recursively
restorecon -v -R /test Reset labels to default recursively in this directory
touch /.autorelabel Force an auto relabel on system boot
semanage fcontext -l List default label configuration for the entire system
semanage fcontext -C -l List all local customizations to label configuration # semanage fcontext -C -l
SELinux fcontext type Context

/opt/tutorial/bin/cause-violation all files system_u:object_r:httpd_sys_content_t:s0
semanage fcontext -a -t etc_t '/test(/.*)?' Configure default label for a directory
semanage fcontext -d '/test(/.*)?' Delete default label configuration for a directory

SELinux booleans

Command Description Examples
getsebool -a List all SELinux booleans (effective state)
getsebool httpd_use_nfs List a specific SELinux boolean (effective state)
semanage boolean -l List all SELinux booleans (effective and permanent)
cat /sys/fs/selinux/booleans/httpd_use_nfs Query effective and permanent state of single variable from /sys # cat /sys/fs/selinux/booleans/httpd_use_nfs
1 1
setsebool httpd_use_nfs on Set SELinux boolean temporarily
semanage boolean -m --on httpd_use_nfs Set SELinux boolean permanently (effective on next reboot)

SELinux ports

Command Description Examples
semanage port -l List all port mappings (type label to allowed port) # semanage port -l
semanage port -a -t http_port_t -p tcp 3333 Allow an SELinux type to use a specific port

Creating custom SELinux types and policies

Custom service

We will create a custom SELinux type and policies for the following service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python

import time
import datetime
import pathlib

in_file='/var/log/messages'
out_file='/tmp/out'

pathlib.Path(out_file).unlink(missing_ok=True)

while True:
    with open(in_file) as log:
        for line in log:
            pass
        line = line.rstrip()
        now = datetime.datetime.now()
        with open(out_file, 'a') as out: 
            out.write(f"\033[0;32m{now}:\033[0m The last message of /var/log/messages is: '{line}'\n")
    time.sleep(5)
1
2
3
4
5
6
7
8
9
[Unit]
Description=Daemon that opens /var/log/messages and closes it

[Service]
Type=simple
ExecStart=/opt/tutorial/bin/open-messages

[Install]
WantedBy=multi-user.target

Creating a custom SELinux type

Create a policy for the executable:

1
2
3
4
5
6
7
8
# sepolicy generate --init /opt/tutorial/bin/open-messages
nm: /opt/tutorial/bin/open-messages: file format not recognized
Created the following files:
/root/open_messages.te # Type Enforcement file
/root/open_messages.if # Interface file
/root/open_messages.fc # File Contexts file
/root/open_messages_selinux.spec # Spec file
/root/open_messages.sh # Setup Script

By default, the new policy will be permissive, meaning that issues will be logged but not enforced:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# cat /root/open_messages.te
policy_module(open_messages, 1.0.0)

########################################
#
# Declarations
#

type open_messages_t;
type open_messages_exec_t;
init_daemon_domain(open_messages_t, open_messages_exec_t)

permissive open_messages_t;

########################################
#
# open_messages local policy
#
allow open_messages_t self:fifo_file rw_fifo_file_perms;
allow open_messages_t self:unix_stream_socket create_stream_socket_perms;

domain_use_interactive_fds(open_messages_t)

files_read_etc_files(open_messages_t)

miscfiles_read_localization(open_messages_t)

Remove the permissive line from open_messages.te to enforce rules:

1
# sed -i 's/^permissive open_messages_t;$/#permissive open_messages_t;/' open_messages.te

Rebuild the synstem policy with the new configuration (make sure to check the output as this may fail with missing dependencies):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# ./open_messages.sh
Building and Loading Policy
+ make -f /usr/share/selinux/devel/Makefile open_messages.pp
make: 'open_messages.pp' is up to date.
+ /usr/sbin/semodule -i open_messages.pp
+ sepolicy manpage -p . -d open_messages_t
./open_messages_selinux.8
+ /sbin/restorecon -F -R -v /opt/tutorial/bin/open-messages
++ pwd
+ pwd=/root
+ rpmbuild --define '_sourcedir /root' --define '_specdir /root' --define '_builddir /root' --define '_srcrpmdir /root' --define '_rpmdir /root' --define '_buildrootdir /root/.build' -ba open_messages_selinux.spec
setting SOURCE_DATE_EPOCH=1721347200
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.a13gQC
+ umask 022
+ cd /root
+ '[' /root/.build/open_messages_selinux-1.0-1.el9.x86_64 '!=' / ']'
+ rm -rf /root/.build/open_messages_selinux-1.0-1.el9.x86_64
++ dirname /root/.build/open_messages_selinux-1.0-1.el9.x86_64
+ mkdir -p /root/.build
+ mkdir /root/.build/open_messages_selinux-1.0-1.el9.x86_64
+ install -d /root/.build/open_messages_selinux-1.0-1.el9.x86_64/usr/share/selinux/packages
+ install -m 644 /root/open_messages.pp /root/.build/open_messages_selinux-1.0-1.el9.x86_64/usr/share/selinux/packages
+ install -d /root/.build/open_messages_selinux-1.0-1.el9.x86_64/usr/share/selinux/devel/include/contrib
+ install -m 644 /root/open_messages.if /root/.build/open_messages_selinux-1.0-1.el9.x86_64/usr/share/selinux/devel/include/contrib/
+ install -d /root/.build/open_messages_selinux-1.0-1.el9.x86_64/usr/share/man/man8/
+ install -m 644 /root/open_messages_selinux.8 /root/.build/open_messages_selinux-1.0-1.el9.x86_64/usr/share/man/man8/open_messages_selinux.8
+ install -d /root/.build/open_messages_selinux-1.0-1.el9.x86_64/etc/selinux/targeted/contexts/users/
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-ldconfig
+ /usr/lib/rpm/brp-compress
+ /usr/lib/rpm/brp-strip /usr/bin/strip
+ /usr/lib/rpm/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump
+ /usr/lib/rpm/redhat/brp-strip-lto /usr/bin/strip
+ /usr/lib/rpm/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/redhat/brp-python-bytecompile '' 1 0
+ /usr/lib/rpm/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-mangle-shebangs
Processing files: open_messages_selinux-1.0-1.el9.noarch
Provides: open_messages_selinux = 1.0-1.el9
Requires(interp): /bin/sh /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/sh policycoreutils-python-utils selinux-policy-base >= 38.1.35-2
Requires(postun): /bin/sh policycoreutils-python-utils
Checking for unpackaged file(s): /usr/lib/rpm/check-files /root/.build/open_messages_selinux-1.0-1.el9.x86_64
Wrote: /root/open_messages_selinux-1.0-1.el9.src.rpm
Wrote: /root/noarch/open_messages_selinux-1.0-1.el9.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.K63ODh
+ umask 022
+ cd /root
+ /usr/bin/rm -rf /root/.build/open_messages_selinux-1.0-1.el9.x86_64
+ RPM_EC=0
++ jobs -p
+ exit 0

Verify that the executable now is labeled with the new custom type:

1
2
# ls -Z /opt/tutorial/bin/open-messages
system_u:object_r:open_messages_exec_t:s0 /opt/tutorial/bin/open-messages

Creating policies for the custom type

Rerun the service, but with permissive rules:

1
2
# sed -i 's/^#permissive open_messages_t;$/permissive open_messages_t;/' open_messages.te
# ./open_messages.sh

Restart the service and make sure that it's running:

1
2
# systemctl restart open-messages
# systemctl status open-messages

Generate the list of rules needed to make the service run:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ausearch -m AVC -ts recent | audit2allow -R

require {
    type open_messages_t;
}

#============= open_messages_t ==============
auth_read_passwd_file(open_messages_t)
corecmd_exec_bin(open_messages_t)
corecmd_mmap_bin_files(open_messages_t)
files_manage_generic_tmp_files(open_messages_t)
insights_client_filetrans_tmp(open_messages_t)
logging_read_generic_logs(open_messages_t)
sssd_read_public_files(open_messages_t)
sssd_search_lib(open_messages_t)

Copy the list of rules from the output and append them to open_messages.te:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cat <<'EOF' >> open_messages.te
auth_read_passwd_file(open_messages_t)
corecmd_exec_bin(open_messages_t)
corecmd_mmap_bin_files(open_messages_t)
files_manage_generic_tmp_files(open_messages_t)
insights_client_filetrans_tmp(open_messages_t)
logging_read_generic_logs(open_messages_t)
sssd_read_public_files(open_messages_t)
sssd_search_lib(open_messages_t)
EOF

And enforce SELinux again for the policy and rebuild the policies:

1
2
# sed -i 's/^permissive open_messages_t;$/#permissive open_messages_t;/' open_messages.te
# ./open_messages.sh

Empty /var/log/audit/audit.log:

1
# > /var/log/audit/audit.log

Start the open-messages service and make sure that it's running correctly:

1
2
# systemctl restart open-messages
# systemctl status open-messages

Inspect the service, you will see that it runs with the new label:

1
2
3
# ps -fZ --pid $(pgrep -f open-messages)
LABEL                           UID          PID    PPID  C STIME TTY          TIME CMD
system_u:system_r:open_messages_t:s0 root   4185       1  0 08:59 ?        00:00:00 python /opt/tutorial/bin/open-messages

Check that there are no SELinux denied messages for open-messages:

1
# grep denied /var/log/audit/audit.log | grep open_messages_t

Disabling a custom SELinux policy

List the open_messages SELinux module:

1
2
# semodule --list-modules=full | grep open_messages
400 open_messages     pp

Disable the open_messages SELinux module (you can use -e to re-enable):

1
# semodule -d open_messages

List the module again:

1
2
# semodule --list-modules=full | grep open_messages 
400 open_messages     pp  disabled

Run restorecon to restore the SELinux context of the binary and list the labels after the relabel:

1
2
3
4
# restorecon -Rv /opt/tutorial/bin/open-messages
Relabeled /opt/tutorial/bin/open-messages from system_u:object_r:unlabeled_t:s0 to system_u:object_r:bin_t:s0
# ls -Z /opt/tutorial/bin/open-messages
system_u:object_r:bin_t:s0 /opt/tutorial/bin/open-messages

Re-enable the module with:

1
# semodule -e open_messages

And verify:

1
2
# semodule --list-modules=full | grep open_messages
400 open_messages     pp

Run restorecon to restore the SELinux context of the binary and list the labels after the relabel:

1
2
3
4
# restorecon -Rv /opt/tutorial/bin/open-messages
Relabeled /opt/tutorial/bin/open-messages from system_u:object_r:bin_t:s0 to system_u:object_r:open_messages_exec_t:s0
# ls -Z /opt/tutorial/bin/open-messages
system_u:object_r:open_messages_exec_t:s0 /opt/tutorial/bin/open-messages

Removing a custom SELinux policy

List the open_messages SELinux module:

1
2
# semodule --list-modules=full | grep open_messages
400 open_messages     pp

Remove the open_messages SELinux module:

1
2
# semodule -r open_messages 
libsemanage.semanage_direct_remove_key: Removing last open_messages module (no other open_messages module exists at another priority).

List the module again:

1
2
# semodule --list-modules=full | grep open_messages
#

Run restorecon to restore the SELinux context of the binary and list the labels after the relabel:

1
2
3
4
# restorecon -Rv /opt/tutorial/bin/open-messages
Relabeled /opt/tutorial/bin/open-messages from system_u:object_r:unlabeled_t:s0 to system_u:object_r:bin_t:s0
# ls -Z /opt/tutorial/bin/open-messages
system_u:object_r:bin_t:s0 /opt/tutorial/bin/open-messages