SELinux + FreePBX

After watching this awesome talk on SELinux, I realized that I should give SELinux another try.

Disclaimer: This is a learning exercise to me, not a guide on how to secure FreePBX

Most Linux How-To guides just say you should disable SELinux, for whatever particular reason they have. But you shouldn’t be disabling it just because you don’t understand what it’s doing and why it’s blocking your commands. So I’ll try to install FreePBX, a software for managing an Asterisk server, following the instructions from here, skipping the step of disabling SELinux. Doing that will show you that SELinux is blocking most of the actions from the web interface, and things are not working as it was supposed to.

So let’s change the enforcing policy to ‘Permissive‘, using the command setenforce. This allows everything to work, but it also logs everything that would be blocked by SELinux on /var/log/audit/audit.log. If you play around FreePBX for a while, you will see lots of entries on that log file, such as:

...
type=AVC msg=audit(1397677629.300:287): avc:  denied  { execute_no_trans } for  pid=13449 comm="sh" path="/var/lib/asterisk/bin/retrieve_conf" dev=dm-0 ino=12191 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:asterisk_var_lib_t:s0 tclass=file
...
type=AVC msg=audit(1397677629.615:294): avc:  denied  { write } for  pid=13449 comm="retrieve_conf" name="queue_devstate.agi" dev=dm-0 ino=13546 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:asterisk_var_lib_t:s0 tclass=file
...
type=AVC msg=audit(1397677629.778:303): avc:  denied  { open } for  pid=13452 comm="crontab" name="asterisk" dev=dm-0 ino=13629 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_cron_spool_t:s0 tclass=file
...
type=AVC msg=audit(1397677985.862:334): avc:  denied  { write } for  pid=11503 comm="httpd" name="amportal.conf" dev=dm-0 ino=271259 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:etc_t:s0 tclass=file
...
type=AVC msg=audit(1397678526.281:406): avc:  denied  { write } for  pid=14718 comm="retrieve_conf" name="indications.conf" dev=dm-0 ino=276961 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:asterisk_etc_t:s0 tclass=file
...

Every action that would be denied is listed here, how can we use this to allow SELinux to enforce its policies and FreePBX actually works? Another tool can help us: audit2allow. It scans the audit log and figures out what is the best policy to allow those actions to pass SELinux.

First try, I’ll filter only asterisk related logs and pipe it to audit2allow.

grep asterisk /var/log/audit/audit.log | audit2allow -m asterisklocal
module asterisklocal 1.0;

require {
	type asterisk_etc_t;
	type user_cron_spool_t;
	type httpd_t;
	type asterisk_var_lib_t;
	class lnk_file { read getattr };
	class dir { read search open getattr };
	class file { execute setattr read getattr execute_no_trans write ioctl unlink open };
}

#============= httpd_t ==============
allow httpd_t asterisk_etc_t:file write;
allow httpd_t asterisk_var_lib_t:dir { read search open getattr };
allow httpd_t asterisk_var_lib_t:file { execute setattr read ioctl execute_no_trans write getattr open };
allow httpd_t asterisk_var_lib_t:lnk_file { read getattr };
allow httpd_t user_cron_spool_t:file { unlink open };

All those actions comes from httpd_t, so maybe there are more rules that should allowed, let’s try again:

grep httpd /var/log/audit/audit.log | audit2allow -m asterisklocal

The output got a lot bigger, it might cover everything that should be allowed now.

module asterisklocal 1.0;

require {
	type ssh_port_t;
	type asterisk_var_lib_t;
	type httpd_t;
	type port_t;
	type etc_runtime_t;
	type user_cron_spool_t;
	type shadow_t;
	type sysctl_fs_t;
	type asterisk_etc_t;
	type etc_t;
	class capability audit_write;
	class tcp_socket name_connect;
	class file { rename execute setattr read create getattr execute_no_trans write ioctl unlink open };
	class netlink_audit_socket { nlmsg_relay create };
	class lnk_file { read getattr };
	class dir { search read write getattr remove_name open add_name };
}

#============= httpd_t ==============
allow httpd_t asterisk_etc_t:file write;
allow httpd_t asterisk_var_lib_t:dir { read search open getattr };
allow httpd_t asterisk_var_lib_t:file { execute setattr read ioctl execute_no_trans write getattr open };
allow httpd_t asterisk_var_lib_t:lnk_file { read getattr };
allow httpd_t etc_runtime_t:file setattr;
allow httpd_t etc_t:file write;

#!!!! This avc can be allowed using one of the these booleans:
#     allow_ypbind, httpd_can_network_connect
allow httpd_t port_t:tcp_socket name_connect;

#!!!! This avc can be allowed using the boolean 'allow_httpd_mod_auth_pam'
allow httpd_t self:capability audit_write;

#!!!! This avc can be allowed using the boolean 'allow_httpd_mod_auth_pam'
allow httpd_t self:netlink_audit_socket { nlmsg_relay create };
allow httpd_t shadow_t:file { read getattr open };

#!!!! This avc can be allowed using one of the these booleans:
#     allow_ypbind, httpd_can_network_connect
allow httpd_t ssh_port_t:tcp_socket name_connect;
allow httpd_t sysctl_fs_t:dir search;
#!!!! The source type 'httpd_t' can write to a 'dir' of the following types:
# squirrelmail_spool_t, dirsrvadmin_config_t, var_lock_t, tmp_t, var_t, tmpfs_t, dirsrv_config_t, httpd_tmp_t, dirsrvadmin_tmp_t, httpd_cache_t, httpd_tmpfs_t, httpd_squirrelmail_t, var_lib_t, var_run_t, var_log_t, dirsrv_var_log_t, zarafa_var_lib_t, dirsrv_var_run_t, httpd_var_lib_t, httpd_var_run_t, httpd_nagios_rw_content_t, passenger_tmp_t, httpd_nutups_cgi_rw_content_t, httpd_apcupsd_cgi_rw_content_t, httpd_sys_content_t, httpd_dspam_rw_content_t, httpd_mediawiki_rw_content_t, httpd_squid_rw_content_t, httpd_prewikka_rw_content_t, httpd_smokeping_cgi_rw_content_t, passenger_var_run_t, httpd_openshift_rw_content_t, httpd_dirsrvadmin_rw_content_t, httpd_w3c_validator_rw_content_t, cluster_var_lib_t, cluster_var_run_t, httpd_user_rw_content_t, httpd_awstats_rw_content_t, root_t, httpdcontent, httpd_cobbler_rw_content_t, httpd_munin_rw_content_t, cluster_conf_t, httpd_bugzilla_rw_content_t, httpd_cvs_rw_content_t, httpd_git_rw_content_t, httpd_sys_rw_content_t, httpd_sys_rw_content_t

allow httpd_t user_cron_spool_t:dir { write remove_name getattr search add_name };
allow httpd_t user_cron_spool_t:file { rename create unlink open setattr };

But this command only shows what the modules ‘asterisklocal‘ will do, we must run the command with ‘-M‘ to generate the loadable policy file. This post, from Dan Walsh, explains how this work. After generating we need to load it, using semanage -i asterisklocal. Now we can set the SELinux back to enforcing mode and FreePBX should still be working.

That should cover the basics for running FreePBX using SELinux, but this is not supposed to be a complete guide on how to secure FreePBX

Reviewing the policies needed to run FreePBX makes me thing of all the possible exploits and problems that FreePBX hides inside itself. From a security point of view, FreePBX does not use the safest architecture around, it could definitely be improved – maybe splinting in a frontend / backend design. I think it’s safe to say that one should not run other sensitive services on the same server as FreePBX, specially if you disabled SELinux.

Leave a Reply

Your email address will not be published. Required fields are marked *