OK, so I love the idea of SELinux. I like the idea of process permissions and quasi-sandboxing. What I hate about SELinux is:
- The lack of good, straightforward documentation
- The awful UI/UX
- The inconsistency and community split between AppArmor and SELinux
Let's see if I can help myself (and maybe you!) understand it a bit better.
Every process has a security context/label. This consists of a user, role, type, and optional range, separated by colons. Each type ends with a matching suffix, so an example of a unguarded process might be unconfined_u:unconfined_r:unconfined_t
. A kernel worker typically is system_u:system_r:kernel_t:s0
, while system daemons typically specify their own type, such as for NetworkManager system_u:system_r:NetworkManager_t:s0
.
Type Enforcement is used to see if a security context (scontext
) can access a resource (tcontext
). Primarily, this is done via Access Vector (AV) rules, which are stored in the Access Vector Cache (AVC). (Some SELinux-aware applications can provide a custom type, but this is uncommon.) Kernel types provide clean context for certain common kernel paths.
These will be type=AVC for system events or type=USER_AVC for userspace events, and may be followed by kernel types for additional context.
These are the primary The FAQ has a great example:
allow firefox_t user_home_t : file { read write };
| | | ^ Allow reading/writing
| | ^ Manage file operations
| ^ types labeled as being the user home directory (typically just /home/$USER/ and descendants)
^ types labeled as being firefox (ie, typically just the Firefox browser)
Exclude an event from auditing, which is useful to streamline the logs.
Log the event for auditing, but allow it.
Deny (don't allow), but prevent other rules from being added to allow.
There are a few generated by the kernel that you will often see. These provide useful context about events.
- PROCTITLE - the title of the process that caused an event
- PATH - a path that was passed as a parameter to a syscall
- CWD - any change to which directory is the current working directory
- EXECVE - The full command that caused an event; generally (but not always) more reliable than PROCTITLE
- SYSCALL - The actual kernel system call, including pid, success, and more
These have custom types, so documenting them here is difficult. Per the docs, one example is Pluggable Authentication Manager (PAM).
Per the SELinux docs, logs are in three locations overall, with the third being the one typically used.
- The SELinux kernel boot events are logged in the /var/log/dmesg log.
- The system log /var/log/messages contains messages generated by SELinux before the audit daemon has been loaded, although some kernel messages continue to be logged here as well.
- The audit log /var/log/audit/audit.log contains events that take place after the audit daemon (auditd) has been loaded. The AVC audit messages of interest are described in the AVC Audit Events section with others described in the General SELinux Audit Events section.
From a pracical perspective, many denials will end up in Journald journalctl -xe
for systemd units, and ausearch sudo ausearch -m AVC,USER_AVC -ts today
can also filter down events.
Some very useful tools are available. Let's use them - you'll want the following packages:
- bzip2
- checkpolicy
- setools-console
# For example, NetworkManager:
ps -fZ $(pgrep NetworkManager)
# You can also just pipe-grep, but then you get a grep entry
ps -efZ | grep NetworkManager
# For today:
sudo ausearch -m AVC,USER_AVC -ts today
Generally use -x to get more info. There are 6 main types of installed object:
- Attribute (-a)
- Boolean (-b)
- Class (-c)
- Role (-r)
- Type (-t)
- User (-u)
# Get a specific attribute
seinfo -a ATTRIBUTE_NAME -x
# Get all attributes
seinfo -a -x
# For example, here's an important object provided by container-selinux:
seinfo -a container_runtime_domain -x
# If it exists:
Type attributes: 1
attribute container_runtime_domain
container_runtime_t
kubelet_t
# If it doesn't:
Type attributes: 0
These are useful, but generally less flexible than packaged policies from OEMs.
# Build a policy package For rules failed today
sudo ausearch -m AVC,USER_AVC -ts today | audit2allow -a -M MY_POLICY_NAME
# Apply it
sudo semodule -i MY_POLICY_NAME.pp
First off, when installing, watch for script hook errors! Many packages will install files successfully but fail to actually install the policies. You'll get errors like Failed to resolve typeattributeset statement at /etc/selinux/targeted/tmp/modules/200/k3s/cli:21
These are often caused by a parent package (for k3s-selinux, container-selinux) not installing properly.
# Assuming you know the name of the package, look for .pp/.pp.bz/.cil files
# For example k3s-selinux:
rpm -ql k3s-selinux
# First, if the file is compressed (with a .pp.bz extension, of if commands below fail), decompress it:
bunzip2 MYPOLICY.pp.bz
# Next, use the interactive interpreter
sedismod MYPOLICY.pp
# You can redirect output to a file for easy searching via 'f' and giving it a filename
# (this can't be done via flags - it's a pretty rough tool!)
# A short 'one-liner' that would be handy is:
MYPOLICY=k3s sedismod MYPOLICY.pp <<EOF
f
${MYPOLICY}_cond_avtab.txt
2
f
${MYPOLICY}_uncond_avtab.txt
2
f
${MYPOLICY}_users.txt
3
f
${MYPOLICY}_bools.txt
4
f
${MYPOLICY}_roles.txt
5
f
${MYPOLICY}_types.txt
6
f
${MYPOLICY}_role_trans.txt
7
f
${MYPOLICY}_role_allows.txt
8
f
${MYPOLICY}_init_SIDs.txt
0
f
${MYPOLICY}_filename_trans_rules.txt
F
EOF
Some issues I've seen are:
- An RPM-provided policy may install the package successfully, but the RPM script hooks failed.
- As above, but with dependencies: An RPM may install a policy, and the RPM may depend on another RPM that provides another policy - but that parent policy was never hooked.
- Antivirus and endpoint security applications may prevent an SELinux policy from installing.
- An SELinux policy could prevent an SELinux policy from being installed.
Often, reinstalling RPM policies (including parents) or adding local policies can fix these failures.
For example, let's say an endpoint security product blocks the script trigger for container-selinux
via SELinux, which says it is installed, so k3s-selinux
fails. In this case, adding a local policy for the audited event of the initial denial, then reinstalling the packages can yield can fix issues.
Sometimes, the security log doesn't give you enough information and you need to dig a little deeper. One thing that can be useful is capturing a stack trace using '''perf''' on linux.
sudo perf record -a -g -e avc:selinux_audited
Simply press Ctrl-C after the failure has occurred.
sudo perf report -g perf.data