SELinux

From PyWPS
Jump to: navigation, search

SELinux uses a context approach allowing (or not) for file read/write/execute and network port/bind/connect operations, this extendeds the default linux security models of root/users file permissions. SELinux uses a 3 string context structure: role,user name and type (domain), each object (file, socket etc) is labeled to a specific context, and any action that violates its context will be denied and logged in audit.log


For tutorials and extra information:

  • Wikipedia [1]
  • Centos Doc[2] (generic covering the most important topics)
  • Blog [3] (more extensive)
  • Red Hat - Apache [4] (apache context related)
  • SELinux Booleans - [5]


Generic View

The SELinux wiki assumes no problems with PyWPS execution and apache httpd.conf settings. It's advisable to set SELinux to run in permissive mode first, fix any permission problems, have a full pywps running and then activate SELinux and determine what extra security features need to be implemented or changed.

SELinux can be run in "Enforcing" (SELinux applying rules) or "Permissive" (SELinux logging rules and problems rules) mode:

#~>setenforce 1 #Enforcing 
#~>setenforce 0 #Permissive

PyWPS runs as a CGI process from Apache, with access (Read/Write/Execute) to system files and logs.

SELinux Booleans

SELinux policies can be activated/deactivated according to our needs, the best strategy is to list all the policies related to HTTPD and then set their boolean values as necessary:

~# getsebool -a | grep httpd 
allow_httpd_anon_write --> off
allow_httpd_mod_auth_ntlm_winbind --> off
allow_httpd_mod_auth_pam --> off
allow_httpd_sys_script_anon_write --> off
httpd_builtin_scripting --> on
httpd_can_check_spam --> off
httpd_can_network_connect --> on
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
httpd_can_sendmail --> off
httpd_dbus_avahi --> on
httpd_enable_cgi --> off
httpd_enable_ftp_server --> off
httpd_enable_homedirs --> on
httpd_execmem --> off
httpd_read_user_content --> off
httpd_setrlimit --> off
httpd_ssi_exec --> off
httpd_tmp_exec --> on
httpd_tty_comm --> on
httpd_unified --> on
httpd_use_cifs --> off
httpd_use_gpg --> off
httpd_use_nfs --> off

In the list above we have the following policy httpd_enable_cgi --> off, therefore Apache will not run any CGI script, it is necessay to set it on using the setsebool. The P flag in the setsebool will make it permanent even after reboot.

setsebool -P httpd_enable_cgi on

If using the /tmp folder to execute PyWPS content/code it is necessary to set the bool httpd_tmp_exec to on. This boolean is normally off. Normally python code that uses ctypes (e.g: mod_wsgi) requires for this boolean to be set to on.

The SELinux policies change according to the version and linux distribution, it is up to the user to determine the necessary policies for his PyWPS installation to properly work.

SELinux Contexts

SELinux Context of a particular object can be viewed with -Z flag in ps or ls commands:

#~>ls -Z /usr/bin/wps.py
-rwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 /usr/bin/wps.py

The object context is indicated in the third part of the SELinux policy (in the case above: httpd_sys_content_t)

Object's context are set using the chon command:

#~>chcon -t httpd_sys_content_t /usr/bin/wps.py

To make it persistent it is advisable to use the semanage command as follows:

#~>semanage fcontext -a -t httpd_sys_content_t /usr/bin/wps.py

Considering a PyWPS install that has its wrapper script in a user folder (eg. user:pywps and folder:/home/pywps/www/html/wps.cgi), the following SELinux contexts should be set:

  • httpd_sys_content_t / httpd_sys_script_exec_t
  • httpd_log_t

Set files with httpd_sys_content_t if you want script execution and the daemon to read the file and disallow other non sys scripts from access. The context httpd_sys_script_exec_t allows for other sys script to run and access the files. The wps.py and the wrapper scripts should be set to this particular context, including the processes being served.

The PyWPS log (normally defined in the in configuration file as pywps.log) needs different contexts properties according to its location in the filesystem. For example if the file is locate in a user folder the httpd_sys_content_t should be sufficient, but if the file is located in /var/log/httpd its context should be httpd_log_t.

SELinux module

A faster and more efficient strategy to deal with context problems it to use SELinux tools that will check the audit logs for problems and will generate the proper rules necessary to run pywps, and then generate a module that can be uploaded into SELinux.

Error messages

Generic logs

The "normal" logs error_log and pywps.log will not report directally SElinux problems, for example the error_log will contain errors like:

:
[Thu Jun 07 14:21:15 2012] [error] [client 192.171.161.3] /home/pywps/www/html/raster.cgi: line 5:  7693 Segmentation fault      
(core dumped) /usr/bin/wps.py $1
:
:
[Fri Jun 08 10:45:10 2012] [error] [client 192.171.161.3] IOError: [Errno 13] Permission denied: '/home/pywps/www/logs/pywps.log'
[Fri Jun 08 10:45:10 2012] [error] [client 192.171.161.3] Premature end of script headers: wps.cgi
:
:
[Fri Jun 08 09:29:45 2012] [error] [client 192.171.161.3] ImportError: No module named t_topology
[Fri Jun 08 09:29:45 2012] [error] [client 192.171.161.3] PyWPS [2012-06-08 09:29:45,280] WARNING: Could not import processes 
from 'temporal': ImportError('No module named t_topo$
[Fri Jun 08 09:29:45 2012] [error] [client 192.171.161.3] Traceback (most recent call last):
:

Audit log

Understanding the log

In case of problems the /var/log/audit/audit.log will contain the reasons why SElinux blocked a certain action. Considering the following example:

type=AVC msg=audit(1339065882.473:793726): avc:  denied  { read } for  pid=6672 comm="wps.py" name="r_lake.py" 
dev=md1 ino=10498725 
scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 
tclass=lnk_file

The SElinux denined a read from wps.py to r_lake.py, basically SELinux denied wps.py from loading the r_lake.py module. The next information is the most important: scontext=unconfined_u:system_r:httpd_sys_script_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0

The scontext is the "source context" meaning, in which context wps.py tried to read the file, the tcontext is the "target context" or the current object context. Since scontext and tcontext are different SELinux denied access to the file. The source context was a http system script httpd_sys_script_t while the source context is user_home_t, a home directory user file.

The solution is to change the user_home_t into http_sys_script.

Writting modules

Intead of running chcon commands, a user can use SElinux tools to create modules what will be loaded at bootime and will define a set of rules to be applied, bypassing the problems reported in the audit logs. The module building procedure is straight forward and will be based on information logged in permissive mode.

It is convenient to rotate the audit log before building the module so that the log only contains specific information for a single error, also SElinux should be in permissive mode so that any restrictions are lifted but logged

#~>setenforce 0
#~>service auditd rotate

Then the user should perform the operation that was known to cause problems, for example a getCapabilities, Execute etc etc so that the audit.log is written.

Tools like audit2why will explain the audit log in detail for example:

#~> grep wps audit.log.1  | audit2why
type=AVC msg=audit(1339153077.621:810845): avc:  denied  { read } for  pid=19491 comm="wps.py" name="v_to_points.py" 
dev=md1 ino=10625340 scontext=unconfined_u:system_r:httpd_sys_script_t:s0 
tcontext=unconfined_u:object_r:user_home_t:s0 tclass=lnk_file

	Was caused by:
		Missing type enforcement (TE) allow rule.

		You can use audit2allow to generate a loadable module to allow this access.

A text readable module with proper rules can be generate with audit2allow:

#~>grep wps audit.log | audit2allow -m pywps
module pywps 1.0;

require {
	type unconfined_t;
	type fs_t;
	type httpd_sys_script_t;
	type user_home_t;
	class lnk_file read;
	class dir { write add_name };
	class file { write relabelto create append };
	class filesystem associate;
}

#============= httpd_sys_script_t ==============
allow httpd_sys_script_t fs_t:filesystem associate;
#!!!! The source type 'httpd_sys_script_t' can write to a 'dir' of the following types:
# httpd_tmp_t, httpd_sys_ra_content_t, httpd_sys_rw_content_t, tmp_t, httpdcontent, httpd_sys_content_t

allow httpd_sys_script_t user_home_t:dir { write add_name };
allow httpd_sys_script_t user_home_t:file { write create append };
allow httpd_sys_script_t user_home_t:lnk_file read;

#============= unconfined_t ==============
allow unconfined_t httpd_sys_script_t:file relabelto;

Permanent modules that will be loaded at boot time and are required to be in binary format (.pp format), therefore their generation has to use the -M flag, the package will permanentely loaded using the semodule command. To make a permament PyWPS SELinux:

#~>grep wps audit.log | audit2allow -M pywps
#~>semodule -i pywps.pp

When finished with module creation, remember to set SELinux into Enforced mode

#~>setenforce 1

Jmdj 14:41, 12 June 2012 (BST)