Build SELinux Policy for Your Next Daemon. Part 1

These HOWTO series will guide you through creation, adjustment and deployment of the SELinux based policy for a typical system daemon running on RedHat 6 (Fedora 14) Linux 64-bit. The HOWTO is targeted at novice users and may seem too thorough and dull for advanced Linux gurus. I will use qlproxyd from QuintoLabs Content Security 1.2 for Squid Proxy Server as a sample daemon to be confined by this policy module.

Step 1. Install Tools and Target Applications

First we need to install additional tools for generating the SELinux policy modules as they do not come preinstalled by default. In order to do that type the following in the root terminal #yum install selinux-policy-devel policycoreutils-gui binutils. Then follow this HOWTO to get a sample virtual appliance with Fedora 14, Squid, Apache and qlproxy up and running.

QuintoLabs Content Security 1.2 for Squid Proxy Server is installed into /opt/quintolabs/qlproxy, variable part is installed into /var/opt/quintolabs/qlproxy. All directories and files get standard predefined SELinux file types upon installation. Typical layout of the installation directory looks like this:

Initially the qlproxyd daemon runs as unconfined process:

Step 2. Investigate

Description of the program on QuintoLabs web site and contents of RPM installation package (#rpm -qi qlproxy-1.2-1.2-276) provide initial information to think out the following key integration points:

Daemon

  • The qlproxyd daemon is started by the /etc/init.d/qlproxy rc script, binds to the ICAP TCP port 1344, reads configuration information from /opt/quintolabs/qlproxy/etc files and filtering settings from /var/opt/quintolabs/qlproxy/spool files, listens for inbound requests from Squid, performs content filtering, writes logs to /var/opt/quintolabs/qlproxy/log and sends replies back to Squid.
  • If qlproxyd is integrated with Squid as URL rewriter then /opt/quintolabs/qlproxy/sbin/qlproxyd_redirector process (started by Squid) connects to TCP socket 13444 and possibly writes /var/opt/quintolabs/qlproxy/log/redirector.log

Logrotate and Cron Daemons

  • Once a day the logrotated daemon rotates logs stored at /var/opt/quintolabs/qlproxy/log/*.log and creates *.tgz archives out of them. As post rotate step it runs Python script that uploads information from rotated logs into sqlite database stored at /var/opt/quintolabs/qlproxy/www/data/access_log.db.
  • Once a day the crond daemon runs support shell scripts to update the cache database files in /var/opt/quintolabs/qlproxy/spool/*, it deletes old copies from disk, downloads new versions from the Internet using wget and unpacks downloaded archives into new cache database files.
  • Once a day the crond daemon runs support Python script to generate reports *.json files out of log information stored in the sqlite database at /var/opt/quintolabs/qlproxy/www/data/access_log.db. It also deletes old records from the database.

Apache Web Server Daemon

  • Apache needs read only access to the following web files /var/opt/quintolabs/qlproxy/www/*. It also needs access to log files of qlproxyd to insert them into PHP based log viewer.
  • Apache needs access to system top utility to view current status of qlproxyd daemon (memory, CPU usage, thread cound and so on).

Step 3. Generate Template Policy Module Files

In order to generate initial template files for the policy module, type the following in the root command terminal:

# mkdir -p /opt/quintolabs/qlproxy/usr/share/selinux
# cd /opt/quintolabs/qlproxy/usr/share/selinux
# sepolgen /opt/quintolabs/qlproxy/sbin/qlproxyd

This fill give us the four template files we could start with:

Step 4. Adjust Generated Policy Module Files

In order to correctly integrate the qlproxyd daemon into the system we would need the following SELinux types (defined in qlproxyd.te):

policy_module(qlproxyd,1.0.0)

###############################################################################
#
# Type Declarations
#
###############################################################################

# policy domain
type qlproxyd_t;

# executable entry point
type qlproxyd_exec_t;

# mark qlproxyd_t as a domain and qlproxyd_exec_t as an entry point into it
init_daemon_domain(qlproxyd_t, qlproxyd_exec_t)

# for now mark the domain as permissive to ease policy development
# permissive qlproxyd_t;

# deamon init script
type qlproxyd_initrc_exec_t;
init_script_file(qlproxyd_initrc_exec_t);

# port the daemon listens
type qlproxyd_port_t;

# log files at /var/opt/quintolabs/qlproxy/log
type qlproxyd_log_t;
logging_log_file(qlproxyd_log_t);

# configuration files in /opt/quintolabs/qlproxy/etc
type qlproxyd_conf_t;
files_config_file(qlproxyd_conf_t);

# cache files in /var/opt/quintolabs/qlproxy/spool
type qlproxyd_cache_t;
files_type(qlproxyd_cache_t);

# www files in /var/opt/quintolabs/qlproxy/www
type qlproxyd_www_t;
files_type(qlproxyd_www_t);

# temporary files in /var/opt/quintolabs/qlproxy/tmp
type qlproxyd_tmp_t;
files_type(qlproxyd_tmp_t);

# PID file /var/opt/quintolabs/qlproxy/run/qlproxyd.pid
type qlproxyd_var_run_t;
files_pid_file(qlproxyd_var_run_t);

Having defined types we can grant various permissions for the domain type qlproxyd_t (also in qlproxyd.te file).

###############################################################################
#
# Local Policy
#
###############################################################################

# allow full manage permissions for log files
manage_dirs_pattern(qlproxyd_t, qlproxyd_log_t, qlproxyd_log_t)
manage_files_pattern(qlproxyd_t, qlproxyd_log_t, qlproxyd_log_t)
logging_log_filetrans(qlproxyd_t,qlproxyd_log_t, { file dir });

# allow full manage permissions for temporary files
manage_dirs_pattern(qlproxyd_t, qlproxyd_tmp_t, qlproxyd_tmp_t)
manage_files_pattern(qlproxyd_t, qlproxyd_tmp_t, qlproxyd_tmp_t)

# allow full manage permissions for PID file created in /var/opt/quintolabs/qlproxy/run
manage_dirs_pattern(qlproxyd_t, qlproxyd_var_run_t, qlproxyd_var_run_t)
manage_files_pattern(qlproxyd_t, qlproxyd_var_run_t, qlproxyd_var_run_t)
files_pid_filetrans(qlproxyd_t,qlproxyd_var_run_t, { file dir});

# allow read only acces to configuration files
allow qlproxyd_t qlproxyd_conf_t : dir r_dir_perms;
allow qlproxyd_t qlproxyd_conf_t : file r_file_perms;
allow qlproxyd_t qlproxyd_conf_t : lnk_file { getattr read };

# allow read only acces to cache files
allow qlproxyd_t qlproxyd_cache_t : dir r_dir_perms;
allow qlproxyd_t qlproxyd_cache_t : file r_file_perms;
allow qlproxyd_t qlproxyd_cache_t : lnk_file { getattr read };

# allow read only acces to www files (to display blocked page)
allow qlproxyd_t qlproxyd_www_t : dir r_dir_perms;
allow qlproxyd_t qlproxyd_www_t : file r_file_perms;
allow qlproxyd_t qlproxyd_www_t : lnk_file { getattr read };

# daemon and network access
allow qlproxyd_t self:capability { setuid };
allow qlproxyd_t self:process { fork signal };
allow qlproxyd_t self:fifo_file rw_fifo_file_perms;
allow qlproxyd_t self:unix_stream_socket create_stream_socket_perms;
allow qlproxyd_t self:tcp_socket { create_stream_socket_perms name_bind };

# allow access to system information
kernel_read_system_state(qlproxyd_t)

corenet_port(qlproxyd_port_t)
allow qlproxyd_t qlproxyd_port_t:tcp_socket { name_bind };

corenet_tcp_sendrecv_all_if(qlproxyd_t)
corenet_tcp_sendrecv_all_nodes(qlproxyd_t)
corenet_tcp_sendrecv_all_ports(qlproxyd_t)
corenet_non_ipsec_sendrecv(qlproxyd_t)
corenet_tcp_bind_all_nodes(qlproxyd_t)
domain_use_interactive_fds(qlproxyd_t)
files_read_etc_files(qlproxyd_t)
files_search_etc(qlproxyd_t)
auth_use_nsswitch(qlproxyd_t)
logging_send_syslog_msg(qlproxyd_t)
miscfiles_read_localization(qlproxyd_t)
sysnet_dns_name_resolve(qlproxyd_t)
 

After that we need to set default contexts for the directories/files mentioned in the policy. This is done in the qlproxyd.fc file:

/opt/quintolabs/qlproxy/sbin/qlproxyd		--	gen_context(system_u:object_r:qlproxyd_exec_t,s0)
/var/opt/quintolabs/qlproxy/log(/.*)?	gen_context(system_u:object_r:qlproxyd_log_t,s0)
/var/opt/quintolabs/qlproxy/spool(/.*)?	gen_context(system_u:object_r:qlproxyd_cache_t,s0)
/var/opt/quintolabs/qlproxy/www(/.*)?	gen_context(system_u:object_r:qlproxyd_www_t,s0)
/opt/quintolabs/qlproxy/etc(/.*)?	gen_context(system_u:object_r:qlproxyd_conf_t,s0)
/var/opt/quintolabs/qlproxy/tmp(/.*)?	gen_context(system_u:object_r:qlproxyd_tmp_t,s0)
/var/opt/quintolabs/qlproxy/run(/.*)?	gen_context(system_u:object_r:qlproxyd_var_run_t,s0)
/etc/init.d/qlproxy	--	gen_context(system_u:object_r:qlproxyd_initrc_exec_t,s0)

For this step we leave the qlproxyd.if interface file as it is…

The qlproxyd.sh script contains code to compile the module, reset file contexts to default values and initialize the qlproxyd_port_t type. It is mostly generated automatically, we just need to insert reset contexts calls to our directories:

#!/bin/sh -e

DIRNAME=`dirname $0`
cd $DIRNAME
USAGE="$0 [ --update ]"
if [ `id -u` != 0 ]; then
echo 'You must be root to run this script'
exit 1
fi

if [ $# -eq 1 ]; then
        ... I SKIPPED SOME GENERATED CODE HERE ...
fi

echo "Building and Loading Policy"
set -x
make -f /usr/share/selinux/devel/Makefile || exit
/usr/sbin/semodule -i qlproxyd.pp

# Fixing the file context on /opt/quintolabs/qlproxy/sbin/qlproxyd
/sbin/restorecon -F -R -v /etc/init.d/qlproxy
/sbin/restorecon -F -R -v /opt/quintolabs/qlproxy
/sbin/restorecon -F -R -v /var/opt/quintolabs/qlproxy

# add the 1344 as the port that qlproxyd listens to
/usr/sbin/semanage port -a -t qlproxyd_port_t -p tcp 1344

Step 5. Build and Install the Module

Now build the module by typing in the root terminal # ./qlproxyd.sh. The module gets compiled and loaded into the system. After compilation the script automatically restores the file contexts of the /opt/quintolabs/qlproxy and /var/opt/quintolabs/qlproxy as indicated in the qlproxyd.fc file.

The reboot of the system shows that qlproxyd now runs as a confined daemon.

Resume

We have successfully confined the qlproxyd daemon, it runs as expected and SELinux Troubleshooter does not show any SELinux denials. The next step would be to integrate the logrotate and cron based support scripts of the application into the policy… but this will be done in the Part 2 of this tutorial.

About sichent

sichent
This entry was posted in Linux, Network. Bookmark the permalink.

One Response to Build SELinux Policy for Your Next Daemon. Part 1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s