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: