Secure Your Architecture with SELinux
Introduction
Security-Enhanced Linux, abbreviated as SELinux, is a LSM (Linux security module) that allows defining a MAC (mandatory access control) access policy to elements of a Linux-based system. Initiated by the NSA based on work conducted with SCC and the University of Utah in the USA (DTMach prototypes, DTOS, FLASK project), its architecture separates policy enforcement from its definition. It notably allows classifying applications of a system into different groups with finer access levels. It also allows assigning a confidentiality level for accessing system objects, such as file descriptors, according to an MLS (Multi Level Security) security model. SELinux uses the Bell LaPadula model with SCC Type enforcement for integrity. It is free software, with some parts licensed under GNU GPL or BSD.
In practice, the innovation is based on defining extended attributes in the UNIX filesystem. Beyond the concept of “read, write, execute rights” for a given user, SELinux defines for each file or process:
- A virtual user (or collection of roles);
- A role;
- A security context.
Usage
Defining the Mode
This is where you define the SELinux mode and its type. We’ll use the targeted mode which is single-user mode (no one above root). The mls mode is equivalent to RBAC which allows many more security groups and users (for very large companies):
# 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.
SELINUX=enforcing
# SELINUXTYPE= can take one of these two values:
# targeted - Targeted processes are protected,
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
Getting the Current Mode and Changing It
First, we need to know in which mode we are:
$ getenforce
Enforcing
Here we see I’m in enforcing mode. If I want to switch to permissive mode, I run this command with argument 0:
setenforce 0
And I set it to 1 to go back to enforcing mode.
We can verify that SELinux is properly enabled by listing processes. The SELinux attributes will display with the ‘Z’ argument:
$ ps axZ | grep cron
system_u:system_r:crond_t:s0-s0:c0.c1023 1417 ? Ss 0:00 crond
system_u:system_r:crond_t:s0-s0:c0.c1023 1428 ? Ss 0:00 /usr/sbin/atd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 2082 pts/0 S+ 0:00 grep cron
We can also do this on folders or files:
$ ls -Z /root
-rw-------. root root system_u:object_r:admin_home_t:s0 anaconda-ks.cfg
-rw-r--r--. root root system_u:object_r:admin_home_t:s0 install.log
-rw-r--r--. root root system_u:object_r:admin_home_t:s0 install.log.syslog
Disable Only One Domain
You also have the option to disable only one domain if desired. Let’s take Apache as an example:
semanage permissive -a httpd_t
This service is now in permissive state.
Analyzing Blocks
When SELinux decides to block certain access, there are several ways to analyze and accept certain false positives. First, there’s the ‘audit2allow’ command:
audit2allow -la
#============= httpd_t ==============
allow httpd_t admin_home_t:file { read getattr open };
You also have logs that provide information about blocks in /var/log/audit/audit.log
:
type=AVC msg=audit(1317855069.699:15772): avc: denied { getattr } for pid=2273 comm="httpd" path="/var/www/html/mon_fichier.txt" dev=dm-0 ino=30073 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
type=SYSCALL msg=audit(1317855069.699:15772): arch=c000003e syscall=4 success=yes exit=0 a0=7f300213e3c8 a1=7ffffd59c590 a2=7ffffd59c590 a3=0 items=0 ppid=2268 pid=2273 auid=0 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=2 comm="httpd" exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1317855069.700:15773): avc: denied { read } for pid=2273 comm="httpd" name="mon_fichier.txt" dev=dm-0 ino=30073 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
type=AVC msg=audit(1317855069.700:15773): avc: denied { open } for pid=2273 comm="httpd" name="mon_fichier.txt" dev=dm-0 ino=30073 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
type=SYSCALL msg=audit(1317855069.700:15773): arch=c000003e syscall=2 success=yes exit=11 a0=7f300213e480 a1=80000 a2=0 a3=2 items=0 ppid=2268 pid=2273 auid=0 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=2 comm="httpd" exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
I can see here that a file named mon_fichier.txt was blocked for httpd_t because the object is not correct.
The Contexts
As you’ve seen, there are some special attributes called contexts. We can display this list of contexts via the ‘semanage’ command which allows us to manage contexts:
$ semanage fcontext -l | more
SELinux fcontext type Context
/ directory system_u:object_r:root_t:s0
/.* all files system_u:object_r:default_t:s0
/HOME_DIR/\.Xdefaults all files system_u:object_r:config_home_t:s0
/HOME_DIR/\.xine(/.*)? all files system_u:object_r:config_home_t:s0
/[^/]+ regular file system_u:object_r:etc_runtime_t:s0
/\.autofsck regular file system_u:object_r:etc_runtime_t:s0
/\.autorelabel regular file system_u:object_r:etc_runtime_t:s0
/\.journal all files <<None>>
/\.suspended regular file system_u:object_r:etc_runtime_t:s0
/a?quota\.(user|group) regular file system_u:object_r:quota_db_t:s0
/afs directory system_u:object_r:mnt_t:s0
/bin directory system_u:object_r:bin_t:s0
/bin/.* all files system_u:object_r:bin_t:s0
/bin/alsaunmute regular file system_u:object_r:alsa_exec_t:s0
/bin/bash regular file system_u:object_r:shell_exec_t:s0
/bin/bash2 regular file system_u:object_r:shell_exec_t:s0
/bin/d?ash regular file system_u:object_r:shell_exec_t:s0
/bin/dbus-daemon regular file system_u:object_r:dbusd_exec_t:s0
/bin/dmesg regular file system_u:object_r:dmesg_exec_t:s0
/bin/fish regular file system_u:object_r:shell_exec_t:s0
/bin/fusermount regular file system_u:object_r:fusermount_exec_t:s0
...
Contexts match regexes and are authorized this way.
Context Modification
Let’s say for my website, I want to create an index file. I’m in /tmp
and create my index. At that moment, when the file is created on disk, SELinux will tag the index file and specify that it belongs to the /tmp
directory.
So when I move it to /var/www
, it will still keep those attributes and the Apache server won’t be able to use this file. To fix the issue, I have 2 choices:
- Restore the rights defined in the context database for the parent directory.
- Reassign the correct rights to the specific file
Context Restoration
To restore contexts, we’ll use the restcon command:
restcon -Rv /var/www
And now we have reset all SELinux rights in /var/www
.
Context Reassignment
To reassign the correct rights, I need to apply the security policy to this file:
chcon -v -t httpd_sys_content_t /var/www/index.html
httpd_sys_content_t: this is the desired context type for the /var/www
directory
To find the right context, use the semanage command as seen above:
$ semanage fcontext -l | grep "/var/www"
/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/var/www(/.*)?/logs(/.*)? all files system_u:object_r:httpd_log_t:s0
/var/www/[^/]*/cgi-bin(/.*)? all files system_u:object_r:httpd_sys_script_exec_t:s0
...
You can see in the last column that the context we’re looking for is “httpd_sys_content_t”.
Adding a Context
This is something you should avoid doing to solve problems, but rather use to improve security or customize it for your needs. Here we’ll add a context to fix the problem with the file listed above that belongs to admin and is located in /var/www/html
. We’ll add a context with the rights of the root directory:
semanage fcontext -a -t admin_home_t "/var/www(/.*)?"
Then you can verify your changes:
$ semanage fcontext -l | grep www
/srv/([^/]*/)?www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/usr/share/awstats/wwwroot(/.*)? all files system_u:object_r:httpd_awstats_content_t:s0
/usr/share/awstats/wwwroot/cgi-bin(/.*)? all files system_u:object_r:httpd_awstats_script_exec_t:s0
/var/www(/.*)? all files system_u:object_r:admin_home_t:s0
Now you just need to run “restcon” to fix the permissions.
Port Blocking
SELinux also allows only certain services to run on specific ports. Proof:
$ semanage port -l | grep http
http_cache_port_t tcp 3128, 8080, 8118, 10001-10010
http_cache_port_t udp 3130
http_port_t tcp 80, 443, 488, 8008, 8009, 8443
pegasus_http_port_t tcp 5988
pegasus_https_port_t tcp 5989
If you want to run Apache on another port, for example, you’ll need to add it to the contexts list:
semanage port -a -t http_port_t -p tcp 81
I chose port 81 in this example.
Booleans
Booleans are another type of blocking that SELinux implements, typically found on well-known services. To get this list:
$ getsebool -a
abrt_anon_write --> off
allow_console_login --> on
allow_corosync_rw_tmpfs --> off
allow_cvs_read_shadow --> off
allow_daemons_dump_core --> on
allow_daemons_use_tty --> on
allow_domain_fd_use --> on
allow_execheap --> off
allow_execmem --> on
allow_execmod --> on
allow_execstack --> on
allow_ftpd_anon_write --> off
allow_ftpd_full_access --> off
allow_ftpd_use_cifs --> off
...
To change a value, simply do:
setsebool allow_ftpd_full_access on
You can verify afterwards in two ways:
$ getsebool allow_ftpd_full_access
or
$ semanage boolean -l
FAQ
My System Refuses to Boot Because of SELinux
To fix this problem, at the grub boot, edit the kernel line and add this at the end:
enforcing=0
This will set permissive mode at machine boot so you can fix your problem.
How to Reapply All Security Policies to My System?
If you want to reset all your SELinux security policy on your machine, there are 2 solutions. The first is messier; it consists of checking and reapplying all changes on the fly:
restorecon -R /
I told you… it’s ugly! However, another cleaner solution that will correctly reapply all permissions at the next reboot is to create a file at the root:
touch /autorelabel
I Have a SELinux Problem and Nothing in My Logs
I encountered this with Samba which was causing problems, but neither audit2allow nor logs showed anything. To solve this problem and see the log messages, tell it to log everything:
semanage dontaudit off
Then you just need to check the logs (/var/log/audit/audit.log
and /var/log/messages
).
Resources
Last updated 06 Oct 2011, 12:51 CEST.