SELinux context, web-servers and moving files into place

SELinux is great for adding another layer of protection to your systems, but sometimes it can be a bit of a pain to work out why you get a particular effect from specific software and not from others where they look like they should be doing the same thing.

This post is because I have recently hit exactly that strange behaviour, in the following set of circumstances:

  • Apache as web-server, serving public_html directory contents from user homedirs
  • Samba to share those homedirs
  • Editing (or creating) HTML files with particular editors causes files in the public_html dir to get the wrong SELinux context

For example, lets create two files in our public_html directory:

  $ touch ~/public_html/foo.html ~/public_html/bar.html

If we use the -Z option to ls then we can see the context of the files:

  $ ls -Z public_html/
  -rw-r--r--. user group unconfined_u:object_r:httpd_user_content_t:s0 foo.html
  -rw-r--r--. user group unconfined_u:object_r:httpd_user_content_t:s0 bar.html

The context of httpd_user_content_t is correct for public_html directories, and is set up by default to allow the web-server processes to read these files. This all works as expected.

Now lets edit foo.html over samba from TextEdit on OSX and look at the context again:

  $ ls -Z public_html/
  -rw-r--r--. user group system_u:object_r:user_home_dir_t:s0 foo.html
  -rw-r--r--. user group unconfined_u:object_r:httpd_user_content_t:s0 bar.html

Now foo.html has the wrong context (user_home_dir_t) for the public_html directory and isn’t readable by the web-server, breaking serving of this personal page. Argh!

The reason for this is visible in the samba logs if we crank up the log level setting from the default of 1 to 4, when we see entries like this:

  [2016/08/19 11:15:07.619877,  3] ../source3/smbd/vfs.c:1322(check_reduced_name)
    check_reduced_name: .TemporaryItems/folders.502/TemporaryItems/(A Document Being Saved By TextEdit)/osx-blah9.html reduced to /home/user/.TemporaryItems/folders.502/TemporaryItems/(A Document Being Saved By TextEdit)/foo.html

So TextEdit is really saving files to ~/.TemporaryItems/... and then moving them into place when you say to save them!
This is nice in terms of it saving changes as you make them, but breaks the file context for SELinux!

Options for workarounds include:

  • Disable SELinux – No
  • Tell the editor in question not to move files into place – This doesn’t appear to be an option with TextEdit on OSX
  • Change the context of files after they are moved into place – I can’t currently think of a good way to do this, unfortunately
  • Allow the web-server access to files with context user_home_dir_tThis is shown below
  • Something I haven’t thought of yet – Please suggest alternative methods via the comments below 🙂

Creating a policy to grant the web-server access to these incorrectly-labelled files

The following set of commands can be used to display a policy created from the audit log, create a binary (loadable) module of it and then load that module.

This will just display the text version of the policy :

  # cat /var/log/audit/audit.log | grep httpd | audit2allow -m httpd-allow-userhomedir

If you’re happy with that (i.e. it doesn’t contain superfluous rules) then run this to create a binary policy and then load it:

  # cat /var/log/audit/audit.log | grep httpd | audit2allow -M httpd-allow-userhomedir
  # semodule -i httpd-allow-userhomedir.pp

..but the first time you run this it will only have allowed the httpd process to have got so far in serving the files. You’ll need to either temporarily set SELinux to “permissive” mode, or to go around this loop a few times to get the web-server to get far enough that your policy has all the required permissions in it.

I ended up with this policy :

  module httpd-allow-userhomedir 1.0;

  require {
  	type httpd_t;
  	type user_home_dir_t;
  	class file { read getattr open };

  #============= httpd_t ==============
  allow httpd_t user_home_dir_t:file { read getattr open };

..and then everything worked as expected, hooray! 🙂

Adding this to a Puppet manifest is left as an exercise for the reader 🙂
Hint: Look for selinux::module

Additional notes:

  • The boolean httpd_enable_homedirs only seems to affect allowing the public_html dir to be served, not the rest of the user’s homedir.
  • This probably isn’t the most secure way of achieving the desired effect. Please tell me if I’m doing it wrong 🙂