Back to the Overview

SELinux Configuration


ELinOS, Linux, Security

Available in ELinOS 7.0.2 or higher:

SELinux is one of the Linux Security Modules, used to enforce mandatory access control (MAC) over all processes. Like other LSM’s, it hooks into various Linux kernel calls and allows/disallows them according to the loaded policy rules. This is independent on the common Discretionary Access Control (DAC) system, thus even the root user must pass the SELinux permissions and doesn’t have unlimited control by default.

Note that while Android uses SELinux as well, the tooling, formats and especially rules differ.

The base operation principle is labeling all controlled system resources, such as files, sockets, users, processes etc. Afterwards, the loaded rules determine which resource types can access other resource types, type transitions when a file is executed etc.

Since setting up the rules for a new system is often a non-trivial task, especially for the initial bring-up it is helpful to not have all the rules in operation. This could mean for example locking yourself out from the system, because it would disallow login. For this, SELinux is able to operate in a permissive mode, which still uses all the rules specified by the current policy – but instead of denying the access, an audit log message is produced. The audit message usually contains the source and target contexts and the operation to be performed – which would fail in the enforcing mode of SELinux. This information can be used to find which rule is missing in the policy, or perhaps wrong labeling of some files. The permissive and enforcing modes can be also switched in runtime, however allowing this is not a good idea from the security standpoint.

SELinux Base Configuration

The base configuration file directory for selinux is /etc/selinux. Here, the file /etc/selinux/config determines, which policy is loaded (SELINUXTYPE) and whether the default mode is enforcing or permissive (SELINUX). The default mode may be overridden by kernel boot parameters as well.

After the SELinux-enabled init process (such as the BusyBox init in ELinOS provides) starts, one of the first steps it does it loading the policy from /etc/selinux/$SELINUXTYPE/policy/ directory. Note that there are several versions of SELinux policy files, differing in the various features they support – such as IPv6 support. The maximum policy version that the kernel supports can be determined e.g. using sestatus command, which also shows other current settings of SELinux:

SELinux status:                 enabled 
SELinuxfs mount:                /sys/fs/selinux 
SELinux root directory:         /etc/selinux 
Loaded policy name:             default 
Current mode:                   permissive 
Mode from config file:          permissive 
Policy MLS status:              enabled 
Policy deny_unknown status:     allowed 
Memory protection checking:     actual (secure) 
Max kernel policy version:      31

All recent kernels should support at least policy version 30.

Policy Types

In principle, the policy is always loaded from a single binary file – such as /etc/selinux/default/policy/policy.31. The format of the file is generated by  SELinux Common Intermediate Language (CIL) compiler (secilc). While it is possible to write the lisp-like CIL files manually, as shown e.g. with this OpenWRT policy, it is not too commonly used. The most commonly used refpolicy and Android approach is to use m4-macro-based higher level approach, which is then translated into the low-level language.

Implementation language aside, there are two approaches possible:

  • Monolithic policy: The whole policy is maintained as one piece, though it may be split in multiple source files. One example of such policy is the dummy policy generated by the kernel tool mdp. The policy uses a single resource type and single role, allowing all access to all processes. Technically, all policies eventually become monolithic, before being loaded into the kernel.
  • Module-based policy: To be able to dynamically extend the policy (depending e.g. on user or system requirements), these policies usually consist of a static base part and several modules, which can be dynamically loaded/unloaded. Technically, the userspace tools (such as semanage/semodule) combine the base and chosen modules and transform them into the monolithic policy to be loaded into kernel. Typically, the management tools keep intermediate files in /var/lib/selinux or similar.

Even the monolitic policies may be somewhat flexible thanks to booleans support by the kernel. This way, simple on/off switches (for example user_ping, alias ‘allow users use the ping command’, in the reference policy) can be adjusted in the policy without having to rebuild/reload the original policy.

Policy Tools

Filesystem Labelling

Once the proper policy is installed and prepared for use in kernel, it’s time to configure the rest of the system. Since the base of the SELinux operation are the labels, very probably the policy will require that the filesystem objects will be labelled properly. For example with refpolicy, the /sbin directory should labelled like this:

-sh-5.0# ls -lZ /sbin 
total 3340 
-rwxr-xr-x. 1 root root system_u:object_r:acpid_exec_t:s0             13472 Mar 17 12:43 acpid 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 adjtimex 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 arp 
-rwxr-xr-x. 1 root root system_u:object_r:audisp_remote_exec_t:s0     42320 Mar 19 15:46 audisp-remote 
-rwxr-xr-x. 1 root root system_u:object_r:audisp_exec_t:s0            46404 Mar 19 15:46 audispd 
-rwxr-xr-x. 1 root root system_u:object_r:zos_remote_exec_t:s0        34224 Mar 19 15:46 audispd-zos-remote 
-rwxr-xr-x. 1 root root system_u:object_r:auditctl_exec_t:s0          46416 Mar 19 15:46 auditctl 
-rwxr-xr-x. 1 root root system_u:object_r:auditd_exec_t:s0           124284 Mar 19 15:46 auditd 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                     3781 Mar 19 15:46 augenrules 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                   113184 Mar 19 15:46 aureport 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                   117656 Mar 19 15:46 ausearch 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    17828 Mar 19 15:46 autrace 
-rwxr-xr-x. 1 root root system_u:object_r:fsadm_exec_t:s0             13472 Mar 17 12:43 blockdev 
-rwxr-xr-x. 1 root root system_u:object_r:kmod_exec_t:s0              13472 Mar 17 12:43 depmod 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 devmem 
-rwxr-xr-x. 1 root root system_u:object_r:fsadm_exec_t:s0            313552 Dec  9 18:25 e2fsck 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 freeramdisk 
lrwxrwxrwx. 1 root root system_u:object_r:bin_t:s0                        6 Mar 25 13:41 fsck.ext4 -> e2fsck 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 fstrim 
-rwxr-xr-x. 1 root root system_u:object_r:getty_exec_t:s0             13472 Mar 17 12:43 getty 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 halt 
-rwxr-xr-x. 1 root root system_u:object_r:hwclock_exec_t:s0           13472 Mar 17 12:43 hwclock 
-rwxr-xr-x. 1 root root system_u:object_r:ifconfig_exec_t:s0          13472 Mar 17 12:43 ifconfig 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 ifdown 
-rwxr-xr-x. 1 root root system_u:object_r:bin_t:s0                    13472 Mar 17 12:43 ifup 
-rwxr-xr-x. 1 root root system_u:object_r:init_exec_t:s0              13472 Mar 17 12:43 init 
-rwxr-xr-x. 1 root root system_u:object_r:kmod_exec_t:s0              13472 Mar 17 12:43 insmod

The labels themselves are part of the policy and reside usually alongside the binary policy file, for example  /etc/selinux/default/contexts/files/file_contexts. There are further configurations in the same directory, such as file_contexts.subs_dirs – where usually it is specified that sub-paths in /lib and /usr/lib are equivalent with respect to labelling.

The go-to tool to handle the labeling is restorecon. Running for example ‘restorecon -R /‘ will apply the labels on the whole root filesystem according to current policy. Similarly, if new content is created or moved and the labels don’t match, this command can be used to fix it.

When the system is booted for the first time, the filesystem may be not labelled. Even after restoring the labels, there may be already various processes started (even init) with incorrect labels – usually the simplest solution is to reboot the system into now-correctly labelled filesystem. This of course requires that the filesystem changes are persistent, which rules out RAM-based or read-only filesystems usage.

The policy cannot describe all possible custom system modifications. For example mkswap tool, when used with reference policy, cannot just access any file. One of the solutions is labelling the file in question manually: chcon -t swapfile_t /swp.

SELinux Runtime Status

Especially during development of the system, it is usually needed to adjust the SELinux rules to match the system organization. The tool pair getenforce/setenforce can switch the current enforcement flag of SELinux. The setenforce tool requires the linux kernel option CONFIG_SECURITY_SELINUX_DEVELOP to be active.

Sample Configuration/Operation with the Reference Policy

Setting up basic SELinux operation in ELinOS is easily done using the features:

This is enough to boot into a system with SELinux rules being applied:

-sh-5.0# ps -eZ 
LABEL                               PID   USER     COMMAND 
system_u:system_r:init_t:s0             1 root     init 
system_u:system_r:kernel_t:s0           2 root     [kthreadd] 
system_u:system_r:kernel_t:s0           3 root     [rcu_gp] 
system_u:system_r:kernel_t:s0           4 root     [rcu_par_gp] 
system_u:system_r:kernel_t:s0           5 root     [kworker/0:0-mm_] 
system_u:system_r:kernel_t:s0          60 root     [ext4-rsv-conver] 
system_u:system_r:rpcbind_t:s0         77 root     /sbin/rpcbind 
system_u:system_r:auditd_t:s0          84 root     /sbin/auditd 
system_u:system_r:syslogd_t:s0         97 root     /usr/sbin/rsyslogd 
system_u:system_r:sshd_t:s0-s0:c0.c   100 root     /sbin/sshd -D

However, depending on he system complexity, this may be only the beginning. In the more complex cases, complete rebuild of the (reference) policy may be required – again, this is out of scope of this document. Some operations and adjustments can however be also performed even on a running target system. Also those can be automatized so that they are performed only once at initial boot, at the same time as initial relabelling of the filesystem.

The already mentioned setsebool tool may be used to modify boolean flags of the policy currently in effect, such as ” user_ping” allowing regular users to use the ping command.

Finally, the seinfo tool can be used to dump various information about the current policy:

# seinfo 
Statistics for policy file: /sys/fs/selinux/policy 
Policy Version:             31 (MLS enabled) 
Target Policy:              selinux 
Handle unknown classes:     allow 
 Classes:             129    Permissions:         454 
 Sensitivities:         1    Categories:         1024 
 Types:              4333    Attributes:          214 
 Users:                 6    Roles:                14 
 Booleans:            307    Cond. Expr.:         337 
 Allow:            117943    Neverallow:            0 
 Auditallow:           24    Dontaudit:             0 
 Type_trans:         9971    Type_change:          72 
 Type_member:          16    Range_trans:          42 
 Role allow:           28    Role_trans:          448 
 Constraints:         137    Validatetrans:         0 
 MLS Constrain:        57    MLS Val. Tran:         0 
 Permissives:           0    Polcap:                5 
 Defaults:              0    Typebounds:            0 
 Allowxperm:            0    Neverallowxperm:       0 
 Auditallowxperm:       0    Dontauditxperm:        0 
 Initial SIDs:         27    Fs_use:               26 
 Genfscon:             90    Portcon:             473 
 Netifcon:              0    Nodecon:               0

SELinux Module Management

The swiss-army-knife for manipulating modular policies is the semanage and semodule tool pair. The latter is actually a convenience wrapper for “semanage module” and is primarily used to load or unload modules.

Using semanage to manage SELinux Users

By default, the users map to SELinux user unconfined_u with relatively broad permissions:

# semanage login -l 
Login Name           SELinux User         MLS/MCS Range        Service 
__default__          unconfined_u         s0-s0:c0.c1023       * 
root                 unconfined_u         s0-s0:c0.c1023       *

To narrow them down, new users can be added. The reference policy contains the user “user_u”, which can be easily mapped to a linux user (here "setest"):

# semanage login -a -s user_u setest
# semanage login -l 
setest               user_u               s0                   *

After logging in, use e.g. the id command to show the current SELinux user:

ssh setest@IP
$ id -Z 

Right away, the consequences of this action are visible for example in the process list, where only handful of processes are visible:

$ ps -eZ
LABEL                               PID   USER     COMMAND 
unknown                                 1 root     init 
user_u:user_r:user_t:s0               145 setest   -sh 
user_u:user_r:user_t:s0               152 setest   -sh 
user_u:user_r:user_t:s0               154 setest   ps -eZ

Further can be added, such as guest users. Consult for example the extensive redhat manual pages.

semodule and audit2allow Utilities

The common case when a new module may be needed is when a command fails due to missing rules. Let’s create a simple example consisting of web page. As root, we’ll create /var/www/index.html as follows:

# mkdir -p /var/www
# echo hello > /var/www/index.html
# restorecon -R /var/www/
# ls -lZ /var/www
total 8 
-rw-r--r--. 1 root root system_u:object_r:httpd_sys_content_t:s0 6 Apr  2 03:34 index.html

After login as setest user, printing the file will fail in the enforcing mode even if the permissions allow “other” users to read the file:

$ cat /var/www/index.html 
cat: /var/www/index.html: Permission denied

The immediate cause of the issue can be seen in the audit log:

ausearch -m avc -ts recent
type=AVC msg=audit(1617334865.690:421): avc:  denied  { open } for  pid=379 comm="cat" path="/var/www/index.html" dev="
vda" ino=3841 scontext=user_u:user_r:user_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=file permissive

To allow this operation, the tool audit2allow can be used to generate a module that can be loaded afterwards:

# audit2allow -M www1
## paste the above audit log entry
# semodule -i www1.pp

However, this still doesn’t solve the issue, further similar audit errors would be generated . This would mean multiple iterations of the above audit2allow and semodule operations. Another useful command here is “semodule -DB“, which disables the “dontaudit” rules. These may silently deny some access in the enforcing mode, but without audit logs messages.

The shortcut path to generate the module to allow the access is to switch to permissive mode. This way, the denials are still logged, but not enforced – and the problem can be solved in one step:

# setenforce 0
# semodule -DB
$ # cat /var/www/index.html # from the setest user terminal
# audit2allow -e -M www
type=AVC msg=audit(1617334865.690:421): avc:  denied  { open } for  pid=379 comm="cat" path="/var/www/index.html" dev="
vda" ino=3841 scontext=user_u:user_r:user_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=file permissive
type=AVC msg=audit(1617334865.690:421): avc:  denied  { read } for  pid=379 comm="cat" name="index.html" dev="vda" ino=
3841 scontext=user_u:user_r:user_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=file permissive=1 
type=AVC msg=audit(1617334865.690:421): avc:  denied  { search } for  pid=379 comm="cat" name="www" dev="vda" ino=25911
scontext=user_u:user_r:user_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=dir permissive=1 
time->Fri Apr  2 03:41:05 2021 
type=PROCTITLE msg=audit(1617334865.694:422): proctitle=636174002F7661722F7777772F696E6465782E68746D6C 
type=SYSCALL msg=audit(1617334865.694:422): arch=40000003 syscall=197 success=yes exit=0 a0=3 a1=bfb4b830 a2=8000 a3=bf
b4bed1 items=0 ppid=289 pid=379 auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 fsgid=10
00 tty=pts0 ses=11 comm="cat" exe="/bin/cat" subj=user_u:user_r:user_t:s0 key=(null) 
type=AVC msg=audit(1617334865.694:422): avc:  denied  { getattr } for  pid=379 comm="cat" path="/var/www/index.html" de
v="vda" ino=3841 scontext=user_u:user_r:user_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=file permiss
# cat www.te
module www 1.0; 
require { 
       type user_t; 
       type httpd_sys_content_t; 
       class file { getattr open read }; 
       class dir search; 

# semodule -i www.pp 
SELinux:  policy capability network_peer_controls=1 
SELinux:  policy capability open_perms=1 
SELinux:  policy capability extended_socket_class=1 
SELinux:  policy capability always_check_network=0 
SELinux:  policy capability cgroup_seclabel=1 
SELinux:  policy capability nnp_nosuid_transition=1

# semodule -l | grep www 

# setenforce 1

Afterwards, the file can be finally read by the setest user:

$ cat /var/www/index.html 

The module file (.te) generated above may be manually modified and rebuilt into .pp file using the combination of checkmodule (.te→.mod) and semodule_package (.mod→.pp) tools, as shown for example here.

More information at

Further Reading