3

Our development machine has multiple users, and their various sites are stored in /home/username/apache. In these folders are sub-folders like conf containing the virtual host config, logs containing logs, public containing the actual web files, etc.

I want to change the logs so that instead of single log files:

CustomLog "/home/user/apache/domain.tld/logs/web.log" combined

We have logs seperated into daily logs:

CustomLog "|/usr/sbin/rotatelogs -l /home/user/apache/domain.tld/logs/%Y%m%d_web.log 86400" combined

However, when I put this configuration in, restart apache, then reload a page, while tailing audit.log in to ausearch, I'm seeing errors like this:

----
type=SYSCALL msg=audit(06/06/2014 14:16:51.401:406272) : arch=x86_64 syscall=open success=no exit=-13(Permission denied) a0=7fff70a92460 a1=80441 a2=1b6 a3=7fff70a92110 items=0 ppid=64542 pid=64617 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=1193 comm=rotatelogs exe=/usr/sbin/rotatelogs subj=unconfined_u:system_r:httpd_rotatelogs_t:s0 key=(null)
type=AVC msg=audit(06/06/2014 14:16:51.401:406272) : avc:  denied  { search } for  pid=64617 comm=rotatelogs name=home dev=md2 ino=7208961 scontext=unconfined_u:system_r:httpd_rotatelogs_t:s0 tcontext=system_u:object_r:home_root_t:s0 tclass=dir

I thought I'd be able to fix this like I have fixed other SELinux issues (such as allowing apache to write to files) by changing the type, but after trying a few, I still get the same error above. I've tried httpd_log_t, httpd_rotatelogs_exec_t and var_log_t to no avail.

I was doing this with the chcon command.

What am I missing?

Leonard Challis
  • 23
  • 3
  • 12
  • 26

2 Answers2

12

The Actual Problem

To translate the second line of the log you provided:

The rotatelogs process with PID 64617 tried to search in the home directory, which is on device md2 and on inode 7208961. Access was denied.

The security context of the rotatelogs process is: unconfined_u:system_r:httpd_rotatelogs_t:s0

The security context of the home directory is: system_u:object_r:home_root_t:s0

You need to allow rotatelogs to read the home directory. Allowing httpd to do so is easy enough (setsebool -P httpd_enable_homedirs=1), but unfortunately rotatelogs does not inherit its type from httpd. To make matters worse, there is no setsebool parameter for rotatelogs.

And that's not all! You also need to allow rotatelogs to search all the way down to the desired directory, as well as allow it to perform all the operations necessary for its task -- both on the files themselves and the directories that contain them.

In short, you need to write a local policy.

Step-by-Step Solution

Create a type enforcement file anywhere, named homelogs.te, with the following contents:

module homelogs 1.0;

require {
        type httpd_rotatelogs_t;
        type home_root_t;
        type user_home_t;
        type user_home_dir_t;
        class dir { add_name getattr open read remove_name search write };
        class file { create getattr open read rename unlink write };
}

#============= httpd_rotatelogs_t =============
allow httpd_rotatelogs_t home_root_t:dir search;
allow httpd_rotatelogs_t user_home_dir_t:dir search;
allow httpd_rotatelogs_t user_home_t:dir { add_name getattr open read remove_name search write };
allow httpd_rotatelogs_t user_home_t:file { create getattr open read rename unlink write };

Now compile it:

make -f /usr/share/selinux/devel/Makefile homelogs.pp

And install it:

semodule -i homelogs.pp

Now rotatelogs should have all the permissions needed to perform its task in the subdirectories of user homes.


How to Write a Local Policy

This is for those who want more details on how I wrote the local policy. You have two options to write the policy.

audit2allow

If you're completely new to writing local policies, one option is to use the audit2allow tool, provided in the policycoreutils-python package, to help you write one.

To fix SELinux permission denials when running rotatelogs, you could run the command

grep rotatelogs /var/log/audit/audit.log | audit2allow -M homelogs

to feed the audit logs that include 'rotatelogs' into audit2allow. The tool will automatically analyze the SELinux permission denials and create a Type Enforcement file, as well as the compiled Policy Package (.pp) file. At that point, you could simply install the created Policy Package by running:

semodule -i  homelogs.pp

And just like that, you've fixed all the SELinux problems related to rotatelogs... right?

The problem with this approach is that audit2allow cannot predict what permissions are needed; it can only analyze what permissions have been denied already.

So far, only a search denial in the home (literally /home) directory has been encountered, so audit2allow will only fix that. Think how many times you will have to re-run the tool each time you encounter the next denied permission. And depending on the rotatelogs configuration, it could take weeks for a particular permission to be denied and logged. For example, how long would it take for rotatelogs to attempt to delete an old log file and hit an unlink permission denial?

Michael Hampton pointed out in the comments that this problem can be mitigated by changing SELinux to Permissive Mode:

setenforce 0

In this mode, SELinux will warn and log actions, but not enforce the policy. This allows rotatelogs to perform all of its tasks while SELinux still logs all of the permissions denials. One could then run audit2allow on the audit log, install the automatically created policy, setenforce 1 to return to Enforcing mode, and be done.

Having not known earlier that Permissiive mode can be used this way, I wrote the local policy manually, as described below. It's not necessary to do it by hand, but hopefully the read is somewhat educational.

Manual Creation

With some understanding of the Type Enforcement file, you can solve permissions problems before they arise by writing it by hand. I created a Type Enforcement file (with a .te extension). It doesn't matter where the Type Enforcement file is.

There are three sections to a Type Enforcement file.

The module section

module homelogs 1.0;

The module section lists the name and version of the module you will be creating with your local policy. The name should be unique, or else you will replace an already-existing module of the same name. You can check the names of installed modules with semodule -l.

The require section

require {
        type httpd_rotatelogs_t;
        type home_root_t;
        type user_home_t;
        type user_home_dir_t;
        class dir { add_name getattr open read remove_name search write };
        class file { create getattr open read rename unlink write };
}

As mentioned here:

This [section] informs the policy loader which types, classes and roles are required in the system policy before this module can be installed. If any of these fields are undefined, the semodule command will fail.

In your situation, rotatelogs will interact with several types. After running ls -Z on the rotatelogs executable file and the directories of your desired path, I found the following types would be required:

  • httpd_rotatelogs_t
  • home_root_t
  • user_home_t
  • user_home_dir_t

You also need to interact with some classes. The complete list of classes and their permissions is here. For your situation, we will only be working with class file and class dir. You also have to list the permissions of each class that you will be interacting with. Based on the complete list, I chose to require these classes and permissions (a couple probably are not necessary, but I erred on the side of allowing):

class dir:

  • add_name - Add a file to the directory
  • getattr - Get file attributes for file, such as access mode. (e.g. stat, some ioctls. ...)
  • open - Open a directory
  • read - Read file contents
  • remove_name - Remove a file from the directory
  • search - Search access
  • write - General write access; required for adding or removing

class file:

  • create -
  • getattr - Get file attributes for file, such as access mode. (e.g. stat, some ioctls. ...)
  • open - Open a file
  • read - Read file contents
  • rename - Rename a file
  • unlink - Remove hard link (delete)
  • write - Write to a file

The rules section

allow httpd_rotatelogs_t home_root_t:dir search;
allow httpd_rotatelogs_t user_home_dir_t:dir search;
allow httpd_rotatelogs_t user_home_t:dir { add_name getattr open read remove_name search write };
allow httpd_rotatelogs_t user_home_t:file { create getattr open read rename unlink write };

The rules section reads fairly easily. For example:

allow httpd_rotatelogs_t user_home_t:dir { add_name getattr open read remove_name search write };

roughly means:

Allow commands with type httpd_rotatelogs_t to perform the operations of add_name, geattr, open, read, remove_name, search, and write on directories with the type user_home_t.

Note that each class needs a separate rule, since different classes will have different permissions associated with them.

Compilation and Installation

This is redundant, but I'll mention it again. You need to compile the Type Enforcement file into a Policy Package. When running make, the Type Enforcement file needs to be in your current working directory.

make -f /usr/share/selinux/devel/Makefile homelogs.pp

Lastly, install the Policy Package as a module:

semodule -i homelogs.pp


Useful Resources on Writing Custom Policies:

billyw
  • 1,552
  • 15
  • 25
  • Wow, I really appreciate you taking the time to write this for me. Perfect instructions, worked flawlessly - can't thank you enough! Would you be kind enough to point me to your favourite place for me to learn how/why the above works? – Leonard Challis Jun 06 '14 at 18:50
  • @LeonardChallis I added links to all the resources I referenced while writing this. Also, I only tested the policy for successful log creation. You'll want to make sure that rotations and retention are working as expected. – billyw Jun 06 '14 at 19:32
  • I have added a 50 bounty (I don' have much reputation on this site yet, I usually use SO, sorry) to further award you for this great answer. I'll award it in 24 hours when I am allowed. – Leonard Challis Jun 14 '14 at 19:43
  • This answer would be even better if you explained _how_ you obtained that policy. – Michael Hampton Jun 16 '14 at 14:51
  • @MichaelHampton Took some time, but I added what I could recollect and regather from last week. – billyw Jun 16 '14 at 20:43
  • 2
    Nice. But don't you know about setting SELinux to permissive mode and then collecting all the denials at once? – Michael Hampton Jun 16 '14 at 20:45
  • Maybe you could share a decent link? – Leonard Challis Jul 25 '14 at 10:03
3

Couldn't add a comment in the excellent answer above so I'll have to add a new "answer":

If you can't compile the freshly made module because of a missing /usr/share/selinux/devel/Makefile file, make sure to install the selinux-policy-devel package:

yum install selinux-policy-devel

Now the file will exist and you can go on with the rest.

unreal4u
  • 31
  • 1