Overview
The purpose of this tutorial is to teach the basic concepts and operations of
administering SE Linux on a Red Hat Enterprise Linux or Fedora system. The
tutorial is based around RHEL4 as it is the most recently released. SE Linux
administration will be done in the same way in both Fedora and RHEL with
only minor differences due to different versions (Fedora and RHEL releases
are not synchronised and there are minor version differences of the packages
that comprise them). What
you learn today can be used on Fedora Core 4 when it is released (test
releases should be available soon), and can be used on Fedora Core 3.
Please note that Fedora Core 3 has earlier versions of some programs and will
have minor differences.
Objectives
Learn how to use all the administrative commands that relate to SE Linux or
which are modified for SE Linux.
Perform basic sys-admin operations such as adding a new user on a SE Linux
system and learn about the extra steps that may be required to take full
advantage of SE Linux features.
Learn how the interfaces between SE Linux aware applications and the kernel
work. This will include some of the low level interfaces between applications
and the kernel, the aim is that when strace and similar tools show system
programs performing SE Linux operations you will have a rough understanding
of what they are doing.
Learn the difference between targeted and strict policies. Most
of the tutorial will be concerned with the strict policy even though it is not
the default, this is because the strict policy is more technically demanding.
Once you have learned about the strict policy the targeted policy will be much
easier to understand.
Recover a system when the SE Linux configuration has been broken.
Learn the basics of writing SE Linux policy.
After completing this tutorial you should be able to install and administer
a SE Linux machine without any assistance.
Duration
This tutorial is designed to take more than three hours. Some delegates may
not complete all the material in the given time. This is not a problem, I have
intentionally put more material than I expect most people to complete. The
most important material is in the first half of the tutorial.
Introduction
You should have already logged in to a RHEL4 machine running SE Linux by the
time you read this. It was installed via a ghost image from a DVD, the image
is of an everything install of RHEL4-AS.
If you break the configuration of your machine in a way that can not easily be
fixed then it can be reinstalled in a small amount of time. Don't be afraid to
experiment, if you are going to break a machine then this is a good one to
break.
When I install such machines I use the Red Hat kickstart facility to do an
automatic network install. Contact me after the tutorial if you want a
copy of the kickstart configuration for installing such machines.
Go to the Applications menu, select System Tools and then select
Terminal to open a terminal. Almost everything you do will be text
based.
SE Linux basic concepts
SE Linux has at it's core a security model known as domain-type. Every
process has a domain and every object that a process may access has a
type. The domains are actually a sub-set of the type name-space (a
process can be the target of an operation such as sending a signal).
Every process or object in the system has a security context that has three
fields, an identity, a role and finally the domain or
type . They are represented as a text string with the three fields
separated by colons in the form identity:role:domain or
identity:role:type.
The main part of the SE Linux policy is a set of rules determining what
access each domain has to each type. The objects that may be accessed are
divided into various classes, file, directory, etc. It is possible (and
common) to grant a domain different access to the various object classes
that may have a given type. For example writing to a file of a particular
type may be permitted while writing to a directory of the same type is not.
A) System Information
- Run the command id and notice that the SE Linux context of the
process is displayed along with the UID/GID information. The context is
root:system_r:unconfined_t and is comprised of three parts, the
identity of root, the role of system_r and the
domain of unconfined_t.
Now run the command id -Z and note that only the SE Linux context is
displayed (suitable for scripts).
- Run the command ps axZ | less and note that the SE Linux context is
displayed for each process. Note that most processes run in domain
unconfined_t which means that SE Linux does not restrict any operations
that they perform.
The programs that are not running in unconfined_t are httpd,
named, portmap, and syslogd. They have their own
domains because they are daemons which accept data from the network and
therefore are vulnerable to attack.
- The ls program has an option -Z to display the SE Linux
security contexts of file system objects. Run the command ls -lZ / to
see the contexts of the subdirectories of the root directory.
Note that the directories /proc, /selinux, and /sys do
not have a security context listed. The file systems mounted on those
directories do not support the interface that is used by ls to query
the context. See section C for details.
Also you will notice that /lost+found has no label, to fix this run
restorecon -v /lost+found , note that the -v option is to
display verbose status information.
B) Domain transitions
Run the command ps axZ | grep portmap, then the command
/etc/init.d/portmap restart and then run ps axZ | grep portmap
again and note that the identity of the portmap process has changed from
user_u to root.
Run the command ls -lZ /sbin/portmap and observe that the type of the
file is portmap_exec_t.
When a process is executed the default action is that all parts of the
security context will remain the same. A key feature of the SE Linux is the
domain transition, in this case there is a policy rule specifying that
when the domain unconfined_t runs a program of type
portmap_exec_t the process should transition to domain portmap_t.
The original context was root:system_r:unconfined_t, so when the domain
changes to portmap_t we get the context
root:system_r:portmap_t.
The fact that the portmap program has a different identity when run by the
administrator than when it is run from the system boot scripts makes no
difference to the operation of the system, later sections of this tutorial
will deal with this issue in more detail.
C) Interfaces to the SE Linux kernel code
- Run the command cd /proc/self/attr and then the command
ls -l, the files that you see provide the main interface between an
application and the SE Linux kernel code.
- Run the command cat current and the command id -Z, observe
that the output of both commands is the same (apart from the fact that the
cat output is missing a new-line character). The algorithm for
id -Z is to check that SE Linux is enabled, if SE Linux is enabled it
displays the contents of /proc/self/attr/current.
- Run the command runcon user_u:system_r:unconfined_t id -- -Z and
observe that the identity that is reported is user_u as opposed to
running id -Z from the command-line which gives an identity of
root. The program runcon is used to run
a program in a different security context. It does this by writing the
desired new context to the file exec.
Run the command echo -n user_u:system_r:unconfined_t > exec and then
run the command id -Z, note that the identity is reported as being
user_u now. The -n parameter to echo is very
important, otherwise a newline character will be appended and the kernel will
refuse the operation.
Note that this setting is on a per-process basis, changing it has no affect
on the rest of the system or other processes run in the same context.
It is impossible to undo this operation because echo -n "" > file is
optimised by bash to be a no-op, so you must now close your terminal window
and open a new one to reverse this change.
In normal operation you will never need to directly access files under
/proc/self/attr, and even using runcon is not common.
The most common use of this interface is to allow cron, login,
sshd, and gdm to launch a session in the context of the user.
- Run the command echo -n > /tmp/test and the command
ls -lZ /tmp/test, observe that the context of the created file is
root:object_r:tmp_t. Files always have the role of object_r.
When a file is created it defaults to having the identity of the creating
process and the type of the directory that the file is being created in.
This can be changed, run the command
echo -n system_u:object_r:user_home_t > /proc/self/attr/fscreate to
set the context of created files to system_u:object_r:user_home_t,
then run echo -n > /tmp/test2. Run ls -lZ /tmp/test2 and
observe that the newly created file has the context you set.
Once again you have to exit the shell as bash will not let you undo the
operation.
- The final entry in the /proc/self/attr directory is the file
prev, this file has the context of the calling process. Run the
command cat /proc/$$/attr/prev and observe that it has the same
context as the shell (it will be the context of one of the GNOME processes
run for your session).
Use ssh localhost to start a new session and then run
cat /proc/$$/attr/prev, observe that the reported context is
user_u:system_r:unconfined_t, now run ps axZ | grep sshd and
observe that sshd also has the context user_u:system_r:unconfined_t.
Some programs need to check the context of their parent process to determine
whether certain operations are permitted.
- The security contexts of files on regular file systems (Ext2, Ext3, XFS and
tmpfs at the moment and ReiserFS in the near future) are stored using the XATTR
interface. The XATTR interface allows associating a number of name:value pairs
with a file system object. SE Linux uses the XATTR name
security.selinux to store it's security contexts. To see how this is
done compare the output of ls -lZ / with that of
getfattr -n security.selinux /* .
The devpts filesystem exports an XATTR interface to allow an SE Linux aware
login program to change the context of the terminal device node for a remote
login. Currently none of the other virtual file systems implement the XATTR
interface. All file system objects are labeled, but the labels can not be
queried by applications.
D) Introduction to Booleans
- Run the command system-config-securitylevel & (run it in the
background so we can do other things with the terminal window). Notice that
there are two tabs, one for Firewall Options and one for
SE Linux. Select the SE Linux tab. The box at the bottom of
the screen labeled Modify SE Linux Policy is used for changing the
value of booleans.
- Booleans can be used to change the configuration of the SE
Linux policy at run-time, for each boolean there are two sets of policy rules
and the value of the boolean will determine which one is active.
In the terminal window run the command getsebool -a to get a list of the
state of all booleans. Note that the httpd_disable_trans boolean is
inactive.
- Run the command cat /etc/selinux/targeted/booleans to see the
state of each boolean that will be used on the next system boot. Note that
httpd_disable_trans has the value 0 which corresponds to being
inactive.
- Go back to the Security Level Configuration window, expand the
HTTPD Service section and then select the check-box next to
Disable SE Linux protection for httpd daemon. Now click on OK
to apply the changes and close the window.
- Run the command getsebool httpd_disable_trans and note that it
is now active.
- Run the command
grep httpd_disable_trans /etc/selinux/targeted/booleans and note that
it is now configured to be active on the next boot as it's assigned the value
of 1.
- Run the command ps axZ | grep httpd and observe that the httpd
processes are running in the domain httpd_t.
Run the command /etc/init.d/httpd restart and then run
ps axZ | grep httpd. Observe that the httpd processes are now running
in the domain unconfined_t. This is the purpose of the
httpd_disable_trans variable, to make Apache processes run in the
unconfined_t domain to cater for the situation where the administrator can't
write policy to permit Apache to perform the necessary actions. This means of
course that a bug in Apache can do much more damage.
- Run the command setsebool httpd_disable_trans false and then run
getsebool httpd_disable_trans, note that you have now changed the state
of the boolean at the command line. But when you run
grep httpd_disable_trans /etc/selinux/targeted/booleans you will see
that if the machine was to be rebooted the boolean would be active.
- To apply a boolean setting that is to be active after the next boot use
the -P option to setsebol.
Run the command setsebool -P httpd_disable_trans false . Now run
grep httpd_disable_trans /etc/selinux/targeted/booleans and observe
that the boot setting has changed. Changing a boolean through the GUI tool
gives the same result as setsebool -P.
- Run the command ps axZ | grep httpd and observe that the httpd
processes are still running in the domain unconfined_t. This boolean affects
the domain that is chosen at execution time. After the process has started
running it has no affect. Run the command /etc/init.d/httpd restart
to make this change take affect. Now run ps axZ | grep httpd and
observe that the domain of the httpd processes is once again httpd_t.
E) Changing to the strict policy
The SE Linux policy is the most important part of SE Linux, it is the
rules which determine what actions are permitted and what are denied. There
are two policies for SE Linux in common use known as targeted and
strict. The targeted policy is the default policy for Fedora
and the only supported policy for Red Hat Enterprise Linux. The
strict policy restricts the actions of more programs but requires more work
to administer. There is always a trade-off between security and usability.
The targeted policy was developed to be easier to use, but this means that it
does not
restrict the actions of programs as much as you may desire.
The standard support agreement for Red Hat Enterprise Linux does not
cover the amount of work that is needed to support the strict policy.
Red Hat support for the strict policy is only provided through a
consulting contract, contact your sales representative for more information.
Understanding the strict policy is key to understanding SE Linux, so
the next sections of this tutorial will be based around installing and using
the strict policy. The targeted policy is a sub-set of the
strict policy, so once you have learnt the strict policy the targeted
policy will be easy.
- Run the command rpm -U /root/*.rpm to install the strict
policy. The rpms that are on the system for strict policy are from rawhide,
there are no strict policy packages built for RHEL4 at this time.
- Run cat /etc/selinux/config to see the boot time configuration of
SE Linux. Init will use this file to determine which policy to load and
whether to start the machine in enforcing or permissive mode.
In enforcing mode SE Linux prevents actions that are not permitted by the
security policy, in permissive mode the actions are allowed to proceed and
SE Linux merely audits the fact that it is configured to deny them.
- Run the command system-config-securitylevel. Select the
SE Linux tab again. Note that there is a drop-down list-box labeled
Policy Type, select strict, the system will ask you if you
really want to do this - select Yes. Click on OK to save the
settings and exit system-config-securitylevel.
- Run cat /etc/selinux/config to verify that the policy type has been
changed to strict.
- To change between strict and targeted policies the machine must be
rebooted. This is because the context of every process must be changed and
most of the files on the disk will also need to be relabeled.
Run ls -al / and notice that a file named .autorelabel has
appeared in the root directory. The system boot script
/etc/rc.d/rc.sysinit will see this file and know that it has to
relabel all files on the system before proceeding with a regular boot.
The way it does this is to run the command fixfiles relabel. If you
have something go drastically wrong with a SE Linux system you might want
to run that command manually, but ideally you should never need to do so.
- Reboot the machine now. It will take a bit longer than usual for the next
boot and will not be very exciting. Now might be a good time for a coffee
break.
F) Using the strict policy
- Login as root and then try to run dmesg. Note that you are not
permitted to run it. Run id -Z and note that your context is now
root:staff_r:staff_t. The staff_t domain has only limited access
to the system, it does not permit you to restart daemons or even see them in
the output of ps, run ps axZ to verify that you can only see processes
you own and not system proceses.
Play with the system, run reboot, run rm -rf /usr try some other
commands that might be expected to break a system. NB do not remove files
under your own home directory, that is permitted by the SE Linux policy and
will get in the way of the later exercises.
- The first thing you want to do is to get administrative access to the
system. You do this by running the command newrole -r sysadm_r. This
requests that a new shell be started on your behalf with role sysadm_r
(the role for system administration). This command requires that you
authenticate yourself with your password to prevent a user who gets a shell
in context root:staff_r:staff_t from getting immediate administrative
access. Enter your password to complete this operation.
- Run id -Z and note that your context is now
root:sysadm_r:sysadm_t, when you selected the role of sysadm_r
you were also given the default domain for that role which is sysadm_t.
Run dmesg, note that the command now works as you have sufficient
access. Also note that many audit messages are in the system message log
describing the actions you attempted in the previous section but which were
denied. Do not run any rm -rf commands, they will work and such
actions will force a reinstall of your machine and delay your tutorial!
- Run ps axZ and notice that there are many more domains used for the
daemons, almost every daemon gets it's own domain!
G) Configuring the strict policy
- Run system-config-securitylevel, select the SE Linux
tab, then explore the booleans that are available in the
Modify SE Linux Policy box. There is nothing that needs to be
changed at this time.
- From the sysadm_r:sysadm_r session open from the previous section
run the command useradd foo to create a new user and then run
passwd foo to change the password. Do the same to add another user
bar
- Run ssh foo@localhost to launch a shell for user foo, run
id -Z and observe that the context is user_u:user_r:user_t.
Now exit the ssh session.
- Change to the directory /etc/selinux/strict/src/policy. Edit the
file users and add the following lines:
user foo roles user_r;
user bar roles { staff_r user_r sysadm_r system_r };
When a user logs in the login program (/bin/login, sshd, or gdm) will query
the kernel to discover whether there is a SE Linux identity that matches the
account name. If there is then that identity will be used for the new
session, otherwise the default identity of user_u will be used. The
above lines are declaring identities for the users foo and bar which they
will be compelled to use when they login. The identity has a list of roles
associated with it, the login program and any other program that selects a
role for the user can only select from the list of permitted roles. So in
this case user foo is only permitted to enter the role user_r
and user bar is permitted to enter the roles user_r,
staff_r, sysadm_r, and system_r.
- Run the command make policy.conf, this concatenates all the files
that comprise the SE Linux policy and runs the M4 macro processing
system to expand macros in the policy source.
- Run make install, this uses checkpolicy to compile the
policy.conf file into a binary form that the kernel understands and then
copies it to the location on the file system that is used for policy load
during the boot process. It then creates and installs the file_contexts
file. This file has a series of regular expressions that match objects on the
file-system along with the SE Linux contexts that should be assigned to
those objects.
- Run make load to load the binary policy into the kernel. Note that
make install depends on make policy.conf and make load
depends on make install. In normal situations you would use
make install to compile the policy and install it to be used at the
next boot or make load to compile, install and load the policy. For
this exercise we run the steps separately to see exactly what they do.
- Run dmesg | tail and observe the messages about the policy load.
They tell you the number of users, roles, types, bools, classes, and
rules.
- Open a new window and run newrole -r sysadm_r, then change to
directory /etc/selinux/strict/src/policy.
Run grep ^user policy.conf to see the declarations of the users.
Run grep "^role " policy.conf to see role declaration statements, roles
are defined implicitely when types (domains) are assigned to them. To see a
list of unique role names run the command
grep "^role " policy.conf | cut -f2 "-d " | sort -u. Note that the
number of roles listed is one less than the count reported by the kernel when
you loaded the policy. The reason for this is that the checkpolicy
tool defines the role object_r for files on disk without it being
declared in the policy source.
This tutorial will not cover classes in any detail.
The number of rules in the binary policy differs from the number of rules in
the policy source as will be explained later.
- When you installed the new policy the build process generated a new
file_contexts file that specifies the contexts for the users foo
and bar. To apply this change you must relabel those home directories,
before doing this run ls -alZ ~foo ~bar to see the current contexts.
Run restorecon -R /home/foo /home/bar to relabel the home
directories and recursively relabel the files and directories under them. Now
run ls -alZ ~foo ~bar again to see the change.
- Open a new terminal window and run ssh foo@localhost, from that
session run id to see the context.
- Open another terminal window and run ssh bar@localhost, run
id and observe tht user bar is given the same role and domain as
user foo by default.
- Exit the session as user bar and run the command
ssh bar/staff_r@localhost, run id from this session and note
that you have a different context, the role is staff_r and the domain
is staff_t (the default for role staff_r). The SE Linux modifications
to sshd allow specifying user/role@host to permit logging in as a non-default
role.
- From account bar run the command ps axZ and observe that you
can't see processes from user foo. We want to allow the staff_t
domain to see processes from the user_t domain with ps, to do
this we have to change the policy.
From a session that runs as root:sysadm_r:sysadm_t change to directory
/etc/selinux/strict/src/policy. For local additions to policy it's
common to use a file named domains/misc/custom.te, this name is one
that is reserved to local customisations, a future policy package will never
install a file with that name. To allow programs in the staff_t domain
to see the existance of processes in the
user_t domain create the file domains/misc/custom.te and give
it the contents can_ps(staff_t, user_t) . After making that change
run the command make load to apply it.
Now run ps axZ from the session that's logged in as user bar
and you will see the processes of user foo that are running in
domain user_t.
H) Interfaces to the SE Linux kernel code part 2
- Change to directory /selinux and run ls -l, the files and
directories you see are used to control the global SE Linux state.
- The file enforce is used to switch SE Linux between permissive and
enforcing modes. At the moment the machine will be in enforcing mode, run
the command cat enforce and observe that it has the value 1.
Run the command setenforce 0 and then run cat enforce, note that
the value has changed to 0.
Go to the window that has a session for user foo and run ps axZ,
note that you can see all processes.
From a root window run setenforce 1 and then run ps axZ again
from the session for user foo, observe that the restrictions on the
user_t domain are in place again. It is not required that you use the sysadm_t
domain to run setenforce 1.
- The directory booleans contains a file for every boolean in the
policy. Run cat booleans/user_dmesg and note that the value is
0 0, this means that currently the boolean has the value false, and the
value to be applied is also false (IE no change). Run the command
getsebool user_dmesg which gives the same information in a different
way.
Now run the command echo 1 > booleans/user_dmesg followed by
cat booleans/user_dmesg and then getsebool user_dmesg. Note that
the value 0 1 on the boolean means that the current value is
false aka inactive and the value on the next update will be
true aka active.
Run the command echo 1 > commit_pending_bools, this commits the change
you just requested, run cat booleans/user_dmesg and
getsebool user_dmesg to see how this is reported. Now go to the
window for the <foo session and run the dmesg command and
observe that it now works.
- The avc directory has files that allow you to query the statistics
of the Access Vector Cache. SE Linux caches the most common accesses
to save time when it has to determine whether an operation should be permitted,
run cat avc/cache_stats to see the statistics on this. Now run
avcstat for the recommended way of doing this.
- The file mls contains 1 if the system is running MLS (not
supported in RHEL so will always have the value 0.
- The file load is used for loading a new policy.
- The device node null is equivalent to /dev/null, it exists
so that when a program inherits a file handle that it is not permitted to
access then after the file handle is closed it can be replaced with a
handle to /dev/null.
- Run cat policyvers and note that it has the value 18, this
is the version of the policy that is accepted by the kernel. When the system
boots init will read the file /etc/selinux/config and use the
SELINUXTYPE environment variable to determine whether it's strict or
targeted policy. The policy file to be loaded is in directory
/ec/selinux/$SELINUXTYPE/policy and is named policy.XX where XX
is the value contained in the policyvers file.