We followed the instructions for installing Moodle from here: http://docs.moodle.org/24/en/Installing_Moodle with some small variations:
1. We created a “moodle” user and installed the software as that user:
useradd_faculty moodle su - moodle git clone -b MOODLE_24_STABLE git://git.moodle.org/moodle.git mv moodle/* public_html/
Since the files are owned by moodle, they won’t be writable by the web server (apache).
2. We created a MySQL user account:
mysql-useradd.sh moodle random moodle_db
3. We created the moodle data directory in the home directory of the moodle account, not in public_html, following their security guidelines:
mkdir ~/data
4. Now, we need that directory to be writable by apache, so, as root, we did the following:
# chgrp apache ~moodle/data # chmod g+rwx ~moodle/data
5. We then ran their installer:
# chown apache ~moodle/public_html # cd ~moodle/public_html/admin/cli # sudo -u apache /usr/bin/php install.php # chown -R moodle ~moodle/public_html
6. Unfortunately, that asked a lot of questions that we didn’t know the answers to, so we had to do some guessing. We wish their installation instructions had been a bit more comprehensive.
7. And after all that, failure! We get the following in our web browser:
Fatal error: /home/moodle/data is not writable, admin has to fix directory permissions! Exiting
8. We double-checked the data directory, and even opened them up to 777, and no go.
9. Could it be SELinux? Sure could. Do this and here are the new entries that arrive in /var/log/audit/audit.log:
> type=AVC msg=audit(1360875272.798:582224): avc: denied { write } for pid=3259 comm="httpd" name="data" dev=dm-4 ino=36455789 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=dir > type=SYSCALL msg=audit(1360875272.798:582224): arch=c000003e syscall=21 success=no exit=-13 a0=7f0dfb551e50 a1=2 a2=0 a3=7f0dfb5c6ce0 items=0 ppid=8066 pid=3259 auid=4678 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=21996 comm="httpd" exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null) [root@tempest ~]
I got that info by the following script, which looks like a generally useful technique to see what SElinux complaints are caused by a particular operation:
[root@tempest moodle] cat /tmp/new-audits #!/bin/bash before=`mktemp audit.before.XXX` after=`mktemp audit.after.XXX` tail -30 /var/log/audit/audit.log > $before GET http://cs.wellesley.edu/~moodle/ tail -30 /var/log/audit/audit.log > $after diff $before $after [root@tempest moodle]
Unfortunately, Moodle’s installation instructions are mute on the subject of SELinux. Still, there’s some promising information here:
http://beginlinux.com/server_training/web-server/976-apache-and-selinux
https://moodle.org/mod/forum/discuss.php?d=114665
http://wiki.centos.org/TipsAndTricks/SelinuxBooleans
I tried the following command, which lists Selinux booleans and a brief description:
[root@tempest moodle] semanage boolean -l | grep httpd httpd_can_network_relay (off , off) Allow httpd to act as a relay httpd_can_network_connect_db (off , off) Allow HTTPD scripts and modules to connect to databases over the network. httpd_use_gpg (off , off) Allow httpd to run gpg in gpg-web domain httpd_enable_cgi (on , on) Allow httpd cgi support httpd_verify_dns (off , off) Allow Apache to query NS records allow_httpd_mod_auth_pam (off , off) Allow Apache to use mod_auth_pam httpd_run_stickshift (off , off) Allow Apache to run in stickshift mode, not transition to passenger httpd_enable_homedirs (on , on) Allow httpd to read home directories allow_httpd_sys_script_anon_write (off , off) Allow apache scripts to write to public content. Directories/Files must be labeled public_rw_content_t. httpd_dbus_avahi (on , on) Allow Apache to communicate with avahi service via dbus httpd_use_cifs (off , off) Allow httpd to access cifs file systems httpd_unified (on , on) Unify HTTPD handling of all content files. httpd_builtin_scripting (on , on) Allow httpd to use built in scripting (usually php) httpd_can_network_connect (off , off) Allow HTTPD scripts and modules to connect to the network using TCP. httpd_tty_comm (on , on) Unify HTTPD to communicate with the terminal. Needed for entering the passphrase for certificates at the terminal. allow_httpd_anon_write (off , off) Allow Apache to modify public files used for public file transfer services. Directories/Files must be labeled public_rw_content_t. httpd_read_user_content (on , on) Allow httpd to read user content httpd_use_nfs (off , off) Allow httpd to access nfs file systems httpd_tmp_exec (off , off) Allow Apache to execute tmp content. httpd_execmem (on , on) Allow httpd scripts and modules execmem/execstack httpd_manage_ipa (off , off) Allow httpd processes to manage IPA content httpd_can_sendmail (on , on) Allow http daemon to send mail httpd_can_check_spam (off , off) Allow http daemon to check spam allow_httpd_mod_auth_ntlm_winbind (off , off) Allow Apache to use mod_auth_ntlm_winbind httpd_can_network_memcache (off , off) Allow httpd to connect to memcache server httpd_can_network_connect_cobbler (off , off) Allow HTTPD scripts and modules to connect to cobbler over the network. httpd_ssi_exec (on , on) Allow HTTPD to run SSI executables in the same domain as system CGI scripts. httpd_use_openstack (off , off) Allow httpd to access openstack ports httpd_enable_ftp_server (off , off) Allow httpd to act as a FTP server by listening on the ftp port. httpd_setrlimit (off , off) Allow httpd daemon to change system limits [root@tempest moodle]
Excellent. Even more focused is this:
[root@tempest moodle] semanage boolean -l | grep httpd | grep write allow_httpd_sys_script_anon_write (off , off) Allow apache scripts to write to public content. Directories/Files must be labeled public_rw_content_t. allow_httpd_anon_write (off , off) Allow Apache to modify public files used for public file transfer services. Directories/Files must be labeled public_rw_content_t. [root@tempest moodle]
Okay, the first of these seems to be worth trying. Let’s give it a go.
First, let’s use the script above to look at selinux alerts:
[root@tempest moodle] sealert -a audit.after.tvn found 1 alerts in audit.after.tvn -------------------------------------------------------------------------------- SELinux is preventing /usr/sbin/httpd from write access on the directory /home/moodle/data ***** Plugin catchall (100. confidence) suggests *************************** If you believe that httpd should be allowed write access on the data directory by default. Then you should report this as a bug. You can generate a local policy module to allow this access. Do allow this access for now by executing: # grep httpd /var/log/audit/audit.log | audit2allow -M mypol # semodule -i mypol.pp [root@tempest moodle] grep httpd /var/log/audit/audit.log | audit2allow -M mypol ******************** IMPORTANT *********************** To make this policy package active, execute: semodule -i mypol.pp
Okay, so I did the first half of that, grepping for httpd complaints and piping to audit2allow. But I didn’t want to immediately make it active.
[root@tempest moodle] file mypol.* mypol.pp: data mypol.te: ASCII C++ program text, with very long lines [root@tempest moodle] more mypol.te module mypol 1.0; require { type user_tmp_t; type proc_net_t; type user_home_dir_t; type httpd_t; type user_home_t; type sysfs_t; type httpd_suexec_t; type devlog_t; type gconf_home_t; type httpd_sys_script_t; class sock_file write; class lnk_file read; class file { read execute }; class dir { write read getattr search }; } #============= httpd_suexec_t ============== allow httpd_suexec_t user_home_t:file execute; #============= httpd_sys_script_t ============== allow httpd_sys_script_t devlog_t:sock_file write; allow httpd_sys_script_t gconf_home_t:dir search; allow httpd_sys_script_t proc_net_t:file read; allow httpd_sys_script_t sysfs_t:dir search; allow httpd_sys_script_t user_home_dir_t:lnk_file read; allow httpd_sys_script_t user_tmp_t:dir { read getattr }; #============= httpd_t ============== #!!!! The source type 'httpd_t' can write to a 'dir' of the following types: # tmp_t, var_t, tmpfs_t, httpd_log_t, dirsrv_config_t, httpd_tmp_t, dirsrvadmin_tmp_t, httpd_cache_t, httpd_tmpfs_t, var_lib_t, var_run_t, httpd_squirrelmail_t, var_log_t, httpd_mediawiki_tmp_t, dirsrv_var_log_t, zarafa_var_lib_t, dirsrv_var_run_t, httpd_var_lib_t, httpd_var_run_t, dirsrvadmin_config_t, squirrelmail_sp ool_t, var_lock_t, httpd_awstats_ra_content_t, httpd_awstats_rw_content_t, httpd_user_ra_content_t, httpd_user_rw_content_t, httpdcontent, httpd_cobbler_ra_cont ent_t, httpd_cobbler_rw_content_t, httpd_munin_ra_content_t, httpd_munin_rw_content_t, httpd_bugzilla_ra_content_t, httpd_bugzilla_rw_content_t, root_t, httpd_c vs_ra_content_t, httpd_cvs_rw_content_t, httpd_git_ra_content_t, httpd_git_rw_content_t, httpd_sys_ra_content_t, httpd_sys_rw_content_t, httpd_sys_rw_content_t, httpd_nagios_ra_content_t, httpd_nagios_rw_content_t, httpd_nutups_cgi_ra_content_t, httpd_nutups_cgi_rw_content_t, passenger_tmp_t, httpd_apcupsd_cgi_ra_conte nt_t, httpd_apcupsd_cgi_rw_content_t, httpd_mediawiki_ra_content_t, httpd_mediawiki_rw_content_t, httpd_sys_content_t, httpd_squid_ra_content_t, httpd_squid_rw_ content_t, httpd_smokeping_cgi_ra_content_t, httpd_smokeping_cgi_rw_content_t, httpd_prewikka_ra_content_t, httpd_prewikka_rw_content_t, httpd_openshift_ra_cont ent_t, httpd_openshift_rw_content_t, httpd_dirsrvadmin_ra_content_t, httpd_dirsrvadmin_rw_content_t, passenger_var_run_t, httpd_w3c_validator_ra_content_t, http d_w3c_validator_rw_content_t allow httpd_t user_home_t:dir write;
Ick, that’s a lot. Do I really want to do that? Can I be a bit more focussed? Let’s try just changing the selinux context of the data directory to the type suggested in the rule summary, above:
[root@tempest moodle] semanage fcontext -a -t httpd_sys_rw_content data/ libsepol.context_from_record: type httpd_sys_rw_content is not defined (No such file or directory). libsepol.context_from_record: could not create context structure (Invalid argument). libsemanage.validate_handler: invalid context system_u:object_r:httpd_sys_rw_content:s0 specified for data/ [all files] (Invalid argument). libsemanage.dbase_llist_iterate: could not iterate over records (Invalid argument). /usr/sbin/semanage: Could not commit semanage transaction
Darn. So much for following their advice! Let me try a different context:
[root@tempest moodle] semanage fcontext -a -t httpd_sys_rw_content_t data/ [root@tempest moodle] GET http://cs.wellesley.edu/~moodle/ Remote Addr is 149.130.136.40 Fatal error: /home/moodle/data is not writable, admin has to fix directory permissions! Exiting. [root@tempest moodle] ls -ldZ data drwxrwsrwx. apache apache unconfined_u:object_r:user_home_t:s0 data [root@tempest moodle] restorecon -R data [root@tempest moodle] ls -ldZ data drwxrwsrwx. apache apache unconfined_u:object_r:user_home_t:s0 data [root@tempest moodle] !seman semanage fcontext -a -t httpd_sys_rw_content_t data/ [root@tempest moodle] ls -ldZ data drwxrwsrwx. apache apache unconfined_u:object_r:user_home_t:s0 data
Okay, that was a miserable failure. (I later realized that, I think, the problem was that I neededto use an absolute pathname for the files and directories in the semanage command.) Meanwhile, I took another tack. (I’m showing the whole journey because we may have to un-do some of these steps.)
Okay, let’s try that sledgehammer policy file that audit2allow wrote:
[root@tempest moodle] semodule -i mypol.pp [root@tempest moodle] GET http://cs.wellesley.edu/~moodle/ Warning: mkdir(): Permission denied in /home/moodle/public_html/lib/setuplib.php on line 1213 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Error</title> </head><body><div style="margin-top: 6em; margin-left:auto; margin-right:auto; color:#990000; text-align:center; font-size:large; border-width:1px; border-color:black; background-color:#ffffee; border-style:solid; border-radius: 20px; border-collapse: collapse; width: 80%; -moz-border-radius: 20px; padding: 15px"> Unable to save the cache config to file. </div></body></html>[root@tempest moodle]
Okay, so that seems to have pretty much worked. I think.
Later, I realized that I already have a PHP script that is able to write to the filesystem, namely /home/anderson/public_html/cgi-bin/webapps/signup9/database.php. So, I wrote a little script to test writeability for three locations, /tmp, the moodle data directory and my signup9 directory. They are all writable by permissions, so the issue must be SELinux contexts. Here’s what I did:
First, here’s the PHP script:
[root@tempest moodle] cat public_html/testwrite.php <?php function writeable($dir) { if( is_writeable($dir) ) { print "<p>Directory $dir is writeable\n"; } else { print "<p>Directory $dir is <em>not</em> writeable\n"; } $file = "$dir/sda.tmp"; if( !$handle = fopen($file,'wb') ) { print "but couldn't open a file for writing.\n"; return; } if( fwrite($handle,"yes, writeable") === FALSE ) { print "but couldn't write any data to it.\n"; return; } fflush($handle); fclose($handle); } writeable("/tmp"); writeable("/home/moodle/data"); writeable("/home/anderson/public_html/cgi-bin/webapps/signup9"); ?> Let's try it: [root@tempest moodle] GET http://cs.wellesley.edu/~moodle/testwrite.php <p>Directory /tmp is writeable <p>Directory /home/moodle/data is writeable Warning: fopen(/home/moodle/data/sda.tmp): failed to open stream: Permission denied in /home/moodle/public_html/testwrite.php on line 9 but couldn't open a file for writing. <p>Directory /home/anderson/public_html/cgi-bin/webapps/signup9 is writeable Okay so that means the sledgehammer didn't quite work. [root@tempest moodle] ls -ldZ /tmp /home/moodle/data /home/anderson/public_html/cgi-bin/webapps/signup9 drwxrwxrwx. anderson faculty system_u:object_r:httpd_user_content_t:s0 /home/anderson/public_html/cgi-bin/webapps/signup9 drwxrwsr-x. apache apache unconfined_u:object_r:user_home_t:s0 /home/moodle/data drwxrwxrwt. root root system_u:object_r:tmp_t:s0 /tmp [root@tempest moodle] semanage fcontext -a -t httpd_user_content_t /home/moodle/data [root@tempest moodle] ls -ldZ /tmp /home/moodle/data /home/anderson/public_html/cgi-bin/webapps/signup9 drwxrwxrwx. anderson faculty system_u:object_r:httpd_user_content_t:s0 /home/anderson/public_html/cgi-bin/webapps/signup9 drwxrwsr-x. apache apache unconfined_u:object_r:user_home_t:s0 /home/moodle/data drwxrwxrwt. root root system_u:object_r:tmp_t:s0 /tmp [root@tempest moodle] restorecon -v /home/moodle/data restorecon reset /home/moodle/data context unconfined_u:object_r:user_home_t:s0->unconfined_u:object_r:httpd_user_content_t:s0 [root@tempest moodle] ls -ldZ /tmp /home/moodle/data /home/anderson/public_html/cgi-bin/webapps/signup9 drwxrwxrwx. anderson faculty system_u:object_r:httpd_user_content_t:s0 /home/anderson/public_html/cgi-bin/webapps/signup9 drwxrwsr-x. apache apache unconfined_u:object_r:httpd_user_content_t:s0 /home/moodle/data drwxrwxrwt. root root system_u:object_r:tmp_t:s0 /tmp [root@tempest moodle] GET http://cs.wellesley.edu/~moodle/testwrite.php <p>Directory /tmp is writeable <p>Directory /home/moodle/data is writeable <p>Directory /home/anderson/public_html/cgi-bin/webapps/signup9 is writeable [root@tempest moodle]
This is looking good!
Note that I learned that changing a file’s selinux context is a two-step proces from this page: https://access.redhat.com/knowledge/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Security-Enhanced_Linux/sect-Security-Enhanced_Linux-SELinux_Contexts_Labeling_Files-Persistent_Changes_semanage_fcontext.html
Let’s try the moodle script:
[root@tempest moodle] GET http://cs.wellesley.edu/~moodle/index.php
Warning: mkdir(): Permission denied in /home/moodle/public_html/lib/setuplib.php on line 1213
Warning: mkdir(): Permission denied in /home/moodle/public_html/lib/setuplib.php on line 1188
…
Okay, so there’s more to do. I think this is just an issue of recursively setting the SElinux context:
[root@tempest moodle] find data -exec echo semanage fcontext -a -t httpd_user_content_t /home/moodle/{} \;
semanage fcontext -a -t httpd_user_content_t /home/moodle/data
semanage fcontext -a -t httpd_user_content_t /home/moodle/data/temp
semanage fcontext -a -t httpd_user_content_t /home/moodle/data/cache
semanage fcontext -a -t httpd_user_content_t /home/moodle/data/muc
semanage fcontext -a -t httpd_user_content_t /home/moodle/data/lang
[root@tempest moodle] find data -exec semanage fcontext -a -t httpd_user_content_t /home/moodle/{} \;
[root@tempest moodle] restorecon -R -v /home/moodle/data/
restorecon reset /home/moodle/data/temp context unconfined_u:object_r:user_home_t:s0->unconfined_u:object_r:httpd_user_content_t:s0
restorecon reset /home/moodle/data/cache context unconfined_u:object_r:user_home_t:s0->unconfined_u:object_r:httpd_user_content_t:s0
restorecon reset /home/moodle/data/lang context unconfined_u:object_r:user_home_t:s0->unconfined_u:object_r:httpd_user_content_t:s0
[root@tempest moodle] ls -lRZ data/
data/:
drwxrwsrwx. apache apache unconfined_u:object_r:httpd_user_content_t:s0 cache
drwxrwsrwx. apache apache unconfined_u:object_r:httpd_user_content_t:s0 lang
drwxrwsrwx. apache apache unconfined_u:object_r:httpd_user_content_t:s0 muc
drwxrwsrwx. apache apache unconfined_u:object_r:httpd_user_content_t:s0 temp
data/cache:
data/lang:
data/muc:
data/temp:
[root@tempest moodle] GET http://cs.wellesley.edu/~moodle/index.php
<!DOCTYPE html>
<html dir=”ltr” lang=”en” xml:lang=”en”>
<head>
<title>Installation – Moodle 2.4.1+ (Build: 20130208)</title>
<link rel=”shortcut icon” href=”http://cs.wellesley.edu/~moodle/theme/image.php?theme=standard&component=theme&image=favicon” />
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<meta name=”keywords” content=”moodle, Installation – Moodle 2.4.1+ (Build: 20130208)” />
<meta http-equiv=”pragma” content=”no-cache” />
<meta http-equiv=”expires” content=”0″ />
Yay!
Scott