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?
The Actual Problem
To translate the second line of the log you provided:
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: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 thepolicycoreutils-python
package, to help you write one.To fix SELinux permission denials when running rotatelogs, you could run the command
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: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, soaudit2allow
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:
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
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
As mentioned here:
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
andclass 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 directorygetattr
- Get file attributes for file, such as access mode. (e.g. stat, some ioctls. ...)open
- Open a directoryread
- Read file contentsremove_name
- Remove a file from the directorysearch
- Search accesswrite
- General write access; required for adding or removingclass file:
create
-getattr
- Get file attributes for file, such as access mode. (e.g. stat, some ioctls. ...)open
- Open a fileread
- Read file contentsrename
- Rename a fileunlink
- Remove hard link (delete)write
- Write to a fileThe rules section
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:
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:
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 theselinux-policy-devel
package:yum install selinux-policy-devel
Now the file will exist and you can go on with the rest.