Tuning
Your SELinux Policy with Audit2allow
Kevin Fenzi
Fedora Core 3 Linux has been shipping with Security Enhanced Linux
(SELinux) enabled by default for about six months now. SELinux allows
privileges to be separated much more finely than the typical approach
of having users and groups and the all-powerful root "superuser".
The default SELinux configuration is fine for some uses, but the
SELinux configuration files make sendmail.cf look easy. In this
article, I will show you step-by-step how to tune your SELinux policy
to your specific needs using the audit2allow tool.
What Is SELinux?
SELinux is a kernel patch (which was merged into the main kernel.org
kernel in the 2.6.0-test series) that provides the hooks needed
to detect, log, and enforce Mandatory Access Controls on processes.
The rules that control what is allowed and disallowed constitute
a "policy". This policy includes rules specifying which things are
managed under the SELinux framework.
The traditional permissions model consists of users and groups
and Unix file permissions. Using this model, you can restrict which
users and groups of users can read, write, and execute files. SELinux
provides a richer set of permissions with users, roles, and types.
For example, under a traditional permissions model, you must give
root (superuser) access to processes that wish to listen on privileged
(less than 1024) ports. Once these processes have that access, they
can perform any of the actions that the root user can perform. Under
a SELinux model, you can grant the specific server permission to
open its specific port and nothing else.
Which Linux Versions Provide SELinux Support?
At least the following distributions now include support for SELinux:
- Fedora Core 2
- Fedora Core 3
- Red Hat Enterprise Linux 4
- CentOS 4
- Debian unstable (kernel support)
- Hardened Gentoo project
- SUSE 9.x (kernel support)
- Ubuntu
There may well be additional distributions/variants that contain
SELinux support. Consult your system documentation.
Why SELinux?
Using SELinux can enhance the security of your Linux server.
Being able to specify fine-grained controls on processes prevents
vulnerable software from being exploited in many cases. For
example, suppose you have a generic daemon process. It needs
to be able to listen on a specfic (privileged) tcp port, process
requests by saving the input to a file, read a configuration
file, then parse the input file and return a result to the socket.
Under the traditional permissions model, the daemon must be
started with root permissions to open the tcp port and listen
at it (although it can drop those privileges after the socket
is set up). It typically would then run as a user created only
to run the daemon. It would have a directory owned by the user,
where it could read or write any files, and a parsing process
running as that user. If there were issues with the setup as
root, the process could do anything the root user could do.
If there were problems with the read/write code, the process
might be able to leak information to the Internet about other
files the user could read, or the process might be tricked into
overwriting files from other processes.
If there were problems with the parsing process, the process
might be made to run a shell as the process user (allowing remote
access to the server) or run some other command that could be
used to compromise the server.
Now let's consider the same example under a SELinux model.
You would set up a policy for your generic daemon, which would
be allowed to open the specific tcp port needed and no others.
It would never need to run as root. It could only read and write
the specific files of the type allowed. The parsing process
could only read and write files of its specific type and could
never execute anything like a shell command. The server and
parser could never read or write files in /tmp or the like.
Thus, the exposure caused by a programming error or configuration
problem is reduced when using a SELinux model. Of course, even
with SELinux enabled, you should stay current with system updates
and other good security practices. SELinux is just one of many
things that can be done to help secure a system.
Is It Safe?
SELinux has been merged into the main Linux kernel and has
been reviewed by many people. The source is released under the
GPL, and all source code is available for review. Some users
have expressed concern that the NSA may have done this as a
way to provide backdoors into systems. Thus far, no backdoors
have been found.
Files, Processes, and Users
SELinux needs to associate a "security context" with files
and processes. It does this with files by writing an extended
filesystem attribute to the filesystem. This means that your
filesystem must support extended attributes. In the Fedora/Red
Hat distributions, the ls command has a new option --
-Z to show the security context of a file. For example:
ls -lZ /etc/selinux
-rw-r--r-- root root system_u:object_r:selinux_config_t config
drwxr-xr-x root root system_u:object_r:selinux_config_t targeted
Here we see a file and a directory in /etc/selinux. Their security
context is owned by the "system_u" user, the "object_r" role,
and they are type "selinux_config_t". By checking this user/role/type
against the current SELinux policy, the system determines what
is allowed or denied.
Processes are also assigned a security context when they are
started, which you can see using the -Z flag to the ps
command. In process rules, you can specify, for example, that
the squid Web proxy daemon can open a specific port but no others:
root@example# ps axZ | grep squid
user_u:system_r:squid_t 3912 ? Ss 0:00 squid -D
user_u:system_r:squid_t 3915 ? S 9:10 (squid) -D
user_u:system_r:squid_t 3916 ? Ss 0:01 (unlinkd)
In this example, we can see the security context of the squid
processes. They are user type "user_u", role "system_r", and their
type is "squid_t".
Users are assigned a "role", which defines what that user
can do. With a role, you can define things like whether a user
is allowed to run the ping command or execute a Web server
startup. For example, the id command for root on a targeted
SELinux system would be:
root@example# id
uid=0(root)
gid=0(root)groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
context=root:system_r:unconfined_t
Here we see that the root user has a context of "system_r" and
a type of "unconfined_t".
Policies
Red Hat and Fedora distributions use a /etc/sysconfig/selinux
file to control whether SELinux is enabled and the mode in which
it is started. Other distributions may use a different configuration.
The /etc/sysconfig/selinux file from Fedora Core 3:
# 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 - SELinux is fully disabled.
SELINUX=enforcing
# SELINUXTYPE= type of policy in use. Possible values are:
# targeted - Only targeted network daemons are protected.
# strict - Full SELinux protection.
SELINUXTYPE=targeted
The "SELINUX" setting is pretty simple. It controls whether SELinux
is enforcing policy decisions, is simply logging what it would
have done if it had enforced the policy, or is disabled. In this
example, the "enforcing" state is set, so the policy decisions
will be enforced. In other words, processes will be denied if
the policy denies them some action.
The "SELINUXTYPE" setting selects which policy is active.
Fedora Core 3 offers two choices of policies. The "targeted"
policy has policy decisions only on some key processes and allows
anything not listed as outside the scope of the policy. The
"strict" policy has policy setup for many more processes and
will deny anything not explicitly allowed. For servers running
few services or services that are not changed much from defaults,
the "strict" policy might be an option, otherwise the targeted
policy provides at least some of the benefits of SELinux. In
this example, the "targeted" mode is enabled, meaning that those
processes enumerated in the policy will be checked by SELinux,
and those not listed will be allowed.
You can change from "enforcing" to "permissive" mode via the
setenforce command; use "setenforce 0" for permissive
and "setenforce 1" for enforcing mode. You can also pass a "selinux=0"
in the kernel boot command to totally disable SELinux on boot.
To make changes to your policy, you must load the policy sources
packages. In Fedora Core 3, for example, those packages are
"selinux-policy-targeted-sources" and "selinux-policy-strict-sources".
These packages can be installed with the command:
yum install selinux-policy-targeted-sources selinux-policy-strict-sources
Logging
SELinux logs to the system log when it denies (and in some
cases when it allows) an action on the part of a process. You
can use these logs to modify your policy to allow some action
that the default policy is denying.
Here is an SELinux log entry (which is logged by default to
/var/log/messages):
kernel: audit(1114070701.193:0): avc: denied { read } for pid=24216
exe=/usr/libexec/mysqld name=mysql dev=cciss/c0d0p6 ino=16408
scontext=user_u:system_r:mysqld_t tcontext=root:object_r:var_lib_t
tclass=dir
This tells us the following information:
- A "read" request was denied.
- The process trying the read had a process ID of 24216.
- The process was /usr/libexec/mysqld.
- The target of the action was running from device /dev/cciss/c0d0p6.
- The inode of the target of the action was 16408.
- The SELinux context for the process was that of a user, mysqld
type.
- The file it was trying to read was a root-owned file of var_lib_t
type.
Let's take a closer look at the fields in a SELinux log entry:
audit(timestamp) -- This field states that it's an
audit message from SELinux and that it was logged at timestamp
time (in seconds since Jan. 1st, 1970).
avc -- This message was from the SELinux access vector
cache. Pretty much every message you are likely to see is from
this cache.
denied | accepted -- This field indicates whether the
action was denied or accepted. You may see logs of accepted
messages in some cases (like reloading the policy).
{ read | write | unlink | ... } -- This field shows
the type of action that was attempted, such as reading a file,
writing, unlinking, loading policy, etc.
for pid=<pid> -- This is the process ID that
attempted the action.
exe=<executable> -- This is the path to the executable
that started the process.
name=<name> -- This is the name of the target
on which the action was attempted.
dev=<device> -- This is the device on which the
target file is located.
ino=<inode-number> -- This is the inode of the
target of the action.
scontext=<security context> -- This is the process's
security context. This contains user, role, and type.
tcontext=<target context> -- This is the security
context of the target of this action, for example, the file,
directory, etc.
tclass=<target class> -- This is the class of
the target object, such as directory, file, device node, or
something else.
Here is another example:
kernel: audit(1114800386.039:0): avc: granted { load_policy } for
pid=12589 exe=/usr/sbin/load_policy
scontext=root:system_r:unconfined_t
tcontext=system_u:object_r:security_t tclass=security
In this example, we can see a SELinux log message showing that
the policy was reloaded by root (probably from a make load
command). Note that it shows the message as "granted". Here's
another example log message:
kernel: audit(1114800360.543:0): avc: denied { unlink } for
pid=12253 exe=/usr/sbin/named name=example.com dev=hda3 ino=505301
scontext=root:system_r:named_t tcontext=root:object_r:named_zone_t
tclass=file
Here we can see that SELinux denied named unlinking for a file
called "example.com". It was on a /dev/hda3 device and is inode
505301.
Audit2allow
So, in the event that we want mysqld to be able to read the
file it's trying to read, how do we add to the policy? There
is a wonderful tool called "audit2allow" that will take an audit
message as above and convert it into an allowed policy line.
Audit2allow is contained in the policycoreutils package in
Fedora or Red Hat-based systems. This package must be installed
before you can do the following steps in the article. You can
install this package using "yum install policycoreutils".
So, if we echo the above line into the audit2allow utility,
it prints:
allow mysqld_t var_lib_t:dir read;
This policy line says to allow any process with the "mysqld_t"
type to read directories of the "var_lib_t" type.
Now that we have a policy line, we need to load it into the
Linux kernel so it knows to allow that action. SELinux policy
sources are located under /etc/selinux/<policytype>/src/policy.
For example, if we are using the "targeted" policy, the policy
source would be located in /etc/selinux/targeted/src/policy/.
Under that directory, you can see various parts of the policy,
along with a Makefile. For local policy additions, edit /etc/selinux/targeted/src/policy/domains/misc/local.te
and add the line that audit2allow gave us above. Then, run make
load to compile, check, and load the policy into your kernel.
You should see a line in your system logs similar to the following:
Apr 21 14:34:29 linuxmachine kernel: audit(1114115669.205:0): avc:
granted { load_policy } for pid=7648 exe=/usr/sbin/load_policy
scontext=root:system_r:unconfined_t
tcontext=system_u:object_r:security_t tclass=security
Apr 21 14:34:29 linuxmachine kernel: security: 3 users, 4 roles,
320 types, 23 bools
Apr 21 14:34:29 linuxmachine kernel: security: 53 classes, 10952 rules
Other Audit2allow Tricks
Audit2allow has several other nice options. If you run audit2allow
as audit2allow -l -i /var/log/messages, it will look
for the last time your policy was reloaded and only look at
messages since that time. With audit2allow -d, the messages
will be read from your Linux kernel dmesg buffer.
If you have a complex application or configuration, you could
run in "permissive" SELinux mode and gather all the avc messages
and run audit2allow on them, or run in enforcing mode and check
the output of audit2allow -l after each run.
Upgraded Policies
When updates come out for your policy, you should check your
changes against the update to see whether adjustments are needed.
Sometimes that will result in a removal of a custom rule that
has been merged into the main policy.
If you have made your changes to the local.te file only, you
can check whether they are in your new policy by grep'ing
the policy.conf file just before your make load.
Cautionary Notes
Be careful to add rules only for those items you specifically
need. Blindly adding rules to allow anything that you see as
denied could result in allowing something that an attacker could
exploit down the road. As policies are further tuned for typical
uses, less customization will be needed.
Summary
Audit2allow is a useful tool for tuning your SELinux policy
to fit your specific needs. You can use it to generate new policy
lines from denied policy log messages without having to write
new policy lines.
References
Home of the SELinux project -- http://www.nsa.gov/selinux/
The Un-Official SELinux FAQ -- http://www.crypt.gen.nz/selinux/faq.html
SELinux link zoo -- http://www.crypt.gen.nz/selinux/links.html
Ubuntu Linux SELinux pages -- https://www.ubuntulinux.org/wiki/SELinux
Kevin Fenzi, co-author of the Linux Security HOWTO, is
a senior member of tummy.com's team. He has been working as
a Unix and Linux Systems Administrator for more than 15 years.
His passion is security and all of the steps needed to ensure
that systems are kept safe. |