I have “recently” written
How would tools like “paster” work with SELinux?
blog about projects which use/used a tool like the paster to create own application servers. Now let’s talk about other specific example – Thin (Ruby web server).
Previously, we had all services using Thin running in the thin_t SELinux domain type. But we did not realize there are many projects which use Thin in different ways and we could not treat all of them just by one SELinux domain type. So we re-wrote the thin policy to provide
thin_domain_template()
But how to use this interface? I will show you on AEOLUS-CONFIGSERVER example.
AEOLUS-CONFIGSERVER scenario
# yum install aeolus-configserver
# rpm -qi aeolus-configserver
Description :
The Aeolus Config Server, a service for storing and retrieving VM configurations
At First (read the previous blog for more details) we need to add a helper script which calls the thin binary from this helper script instead of either an init script or a systemd unit file.
# rpm -ql aeolus-configserver |grep wrapper
/usr/bin/aeolus-configserver-thinwrapper
# cat /usr/bin/aeolus-configserver-thinwrapper
#!/bin/bash
/usr/bin/thin start -c $CONFIG_SERVER_DIR -l $THIN_LOG \
-P $THIN_PID -a $THIN_IP -e $RACK_ENV \
–user $AEOLUS_USER –group $AEOLUS_GROUP \
-d –prefix=${PREFIX} -R $CONFIG_SERVER_RACKUP -p $CONFIG_SERVER_PORT
Now the following steps are needed to get an initial policy.
1. Create a new policy file.
# echo “policy_module(aeolus_configserver,1.0)” > aeolus_configserver.te
2. Use the template.
# echo “thin_domain_template(aeolus_configserver)” >> aeolus_configserver.te
You can check the thin_domain_template() on
http://git.fedorahosted.org/cgit/selinux-policy.git/tree/thin.if?h=master_contrib
3. Compile/load it.
# make -f /usr/share/selinux/devel/Makefile aeolus_configserver.pp
# semodule -i aeolus_configserver.pp
4. What will we get?
The following basic types will be created
# seinfo -t |grep configserver
thin_aeolus_configserver_exec_t
thin_aeolus_configserver_t
by “thin_domain_template(aeolus_configserver)“.
* thin_aeolus_configserver_exec_t type is an executable type for the aeolus-configserver-thinwrapper helper script.
# ls -Z /usr/bin/aeolus-configserver-thinwrapper
-rwxr-xr-x. root root system_u:object_r:thin_aeolus_configserver_exec_t:s0 /usr/bin/aeolus-configserver-thinwrapper
* thin_aeolus_configserver_t domain type is a domain type for an aeolus-configserver thin process.
With these types we can see the following transition
initrc_t @ thin_aeolus_configserver_exec_t –> thin_aeolus_configserver_t @ thin_exec_t –> thin_aeolus_configserver_t
But without this helper script you would see
initrc_t @ thin_exec_t –> thin_t
which means your project runs in the thin domain type defaultly and probably would not work. We have limited rules for the thin_t domain type because we want to know about this fact.
Now back to the thin_aeolus_configserver_t domain type. This type also got the thin_domain attribute. Basic rules are defined by the policy for all thin domains which are covered by this attribute.
FOREMAN start point
If you call
thin_domain_template(foreman)
you will get
# seinfo -t |grep foreman
thin_foreman_exec_t
thin_foreman_t
and thin_foreman_t will have the thin_domain attribute with the following basic rules
http://git.fedorahosted.org/cgit/selinux-policy.git/tree/thin.te?h=master_contrib
Check the thin_domain local policy section.
Note: You can also see the policy which has been created for aeolus-configserver.
From time to time I get questions how to write|compile|load own local policy module. We wrote a lot of blogs about using ausearch, audit2allow tools to generate a policy from AVC messages. We also mentioned the sepolgen tool to generate a new policy for services, applications, roles.
Note: there is a new tool for this called “sepolicy”.
But sometimes either you or I need to create a local test policy without these tools. Basically we start with
# vim mypol.te
and add needed declarations for mypol.te policy file. For example
# cat mypol.te
policy_module(mypol,1.0)
require{
type ping_t;
type openshift_initrc_t;
}
dontaudit ping_t openshift_initrc_t:fifo_file write;
Then we just compile/load this policy module.
# make -f /usr/share/selinux/devel/Makefile mypol.pp
# semodule -i mypol.pp
Based on this example we came up with a new VIM template for *.te policy file which would help us so we do not have to remember all these statements nor write them over and over. You can download this template from
http://mgrepl.fedorapeople.org/template.te
and execute
# cp template.te /usr/share/vim/vimfiles/
After that just add the line to your /etc/vimrc file.
autocmd BufNewFile *.spec 0r /usr/share/vim/vimfiles/template.spec
+ autocmd BufNewFile *.te 0r /usr/share/vim/vimfiles/template.te
augroup END
Now you can try to create a new local policy.
# vim mypolicy.te
What do you think? Would you like to add some additional examples or comments?
There are many projects which use/used a tool like the paster to create own application servers. For example, piranha, TurboGears2 and others. But what is a problem here?
The paster tool is a python script. The script is called either from an init script or a systemd unit file by a project. This means we can have multiple calling of this script to create running daemons. We have the following transition
initrc_t @ bin_t -> initrc_t
Probably you know this is wrong. We should confine daemons running in the initrc_t domain type. We had this issue with the piranha package. I did not know everything about the paster tool. I added the piranha_web_exec_t label for the paster python script and we got the following transition
initrc_t @ piranha_web_exec_t -> piranha_web_t
which is what we wanted. But this change caused TurboGears2 application servers running with piranha_web_t domain type. It was obviously wrong.
What happened?
turbogears init script @ paster -> TurboGears2 application server
initrc_t @ piranha_web_exec_t -> piranha_web_t
You see the problem. How could we solve issues like this? The solution is pretty easy because we know how SELinux and transitions work. We can just do
initrc_t @ $1_exec_t ->$1_t @ bin_t -> $1_t
What is “?_exec_t” in this case? It can be a script which is called from either an init script or a systemd unit file and this script then calls the pastor python script with arguments. Then we get what we want
initrc_t @ piranha_web_exec_t -> piranha_web_t @ bin_t -> piranha_web_t
initrc_t @ turbogears_exec_t -> turbogears_t @ bin_t -> turbogears_t
and we can leave the pastor python script labeled as bin_t.
We can apply this solution for other projects.
Sometimes I get questions how we do selinux-policy updates. How does the process go?
We go through all new bugs every day in the morning. So there are two periods per day because Dan is from USA and I am from Czech Republic.
We add appropriate fixes to the selinux-policy repo on fedorahosted.org first and then we commit changes also to the selinux-policy git repo on fedoraproject.org. But this does not mean we do a new build immediately. The main reason is we want to cover as much bugs by a build as possible. So we do a new build either at the end of the day or the next day. Of course if a build is required we do it when is needed.
Also we are not able to do a new update with an each build. In this case, you can easily download new builds from koji and install them. So if you see
“Fixed in selinux-policy-<version>”
as a comment in a bug, you get a new build very soon. If not, then I have overslept and just ping me. I would like to thank all for testing/using a new builds from koji.
After writing the blog on optional policy, I received the following question:
“Ho do I know whether I need to use the optional_policy block or not?”
When we write policy we can choose to have the policy either shipped within the base policy or as as module.
We use the modules config files to specify whether a policy is in the base or module.
We have a different modules config file for each policy type that we ship
modules-targeted.conf
modules-mls.conf
modules-minimum.conf
which contain statements like
# Layer: kernel
# Module: domain
# Required in base
#
# Core policy for domains.
#
domain = base
# Layer: system
# Module: init
#
# System initialization programs (init and init scripts).
#
init = module
What does either “base” or “module” mean? The note says
# For modular policies, modules set to “base” will be
# included in the base module. “module” will be compiled
# as individual loadable modules.
The base.pp policy module contains core policy modules which are needed for the basic confinement of your system focused on the core operating system. The base.pp module can not be removed from your policy.
But what about “module”. This is different. Theoretically these modules are all optional. Meaning you should be able to remove them from the policy if you wanted to.
You can list the modules using:
$ semodule -l
There are some exceptions for modules like miscfiles, authlogin, init,
systemd, sysnetwork. We define them as “module” in the modules-*.conf
file but they can not be removed.
Another way of looking at this would be to examine the directories in
which we ship the interfaces. We ship interface files in /usr/share/selinux/devel/include/ subdirs
system kernel roles services admin apps
Interfaces in system and kernel are core to the system, while interfaces in the other directories as optional.
Let’s say you write a policy and you need to use a rpm interface because your application execute rpm -qi. First, we need to examine that the rpm module is not a part of the base.pp policy module
$ semodule -l | grep rpm
rpm 1.12.0
Ok, I see it is not. So I will call a rpm interface with the optional_policy block.
optional_policy(`
rpm_exec(mydomain_t)
‘)
Sometimes new services, which are a part of a project, are added. For example we could talk about cluster, cloudform, MRG services and so on. Yesterday I got a new bug with the following description
“matahari-qmf-rpcd runs as initrc_t”
It means that our policy does not cover the matahari-qmf-rpcd service which probably has been shipped recently. I know nothing about this service and I want to confine it. I am not sure if we have a policy for Matahari project so I use the selinux-policy-doc package to check it.
$ rpm -q selinux-policy-doc
selinux-policy-doc-3.10.0-96.fc17.noarch
The package is installed and I am able to find whether a policy exists
$ grep -r matahari /usr/share/selinux/devel/include/ |wc -l
256
There is a policy which I could use. We are interested in the /usr/share/selinux/devel/include/services/matahari.if policy file and the matahari_domain_template() interface. A template interface is a special interface to create a basic set of rules for services – template interfaces generate domain, executable and file types for a service.
In our case I declare the following local policy.
$ cat mymatahari.te
policy_module(mymatahari,1.0)
matahari_domain_template(rpcd)
Then I load and install it.
$ make -f /usr/share/selinux/devel/Makefile mymatahari.pp
$ semodule -i mymatahari.pp
You can see what types were added.
$ seinfo -t |grep matahari_rpc
matahari_rpcd_unit_file_t
matahari_rpcd_exec_t
matahari_rpcd_t
These three types were created by the matahari_domain_template() interface and we use them for an initial confinement. Now we need to tell SELinux that the matahari-qmf-rpcd service started by systemd should end up in the matahari_rpcd_t domain.
$ chcon -t matahari_rpcd_exec_t `which matahari-qmf-rpcd`
$ systemctl restart matahari-rpc.service
$ ps -eZ |grep matahari
system_u:system_r:matahari_rpcd_t:s0 3338 ? 00:00:00 matahari-qmf-rp
You are done and you have this new service running in the proper domain. Now you can use ausearch/audit2allow tools to check AVC messages
$ ausearch -m avc -ts recent |audit2allow
#============= matahari_rpcd_t ==============
allow matahari_rpcd_t bin_t:file getattr;
allow matahari_rpcd_t passwd_file_t:file { read getattr open };
allow matahari_rpcd_t usr_t:file { read getattr open };
The next step would be either file a new bug with these AVC messages and with the local policy or use the audit2allow tool to generate rules for the mymatahari.te policy file.
$ ausearch -m avc -ts recent |audit2allow -R |grep \(matahari >> mymatahari.te
$ make -f /usr/share/selinux/devel/Makefile mymatahari.pp
$ semodule -i mymatahari.pp
Petr Lautrbach (openssh package maintainer) wrote a great blog about cool stuff – sftp+chroot+SELinux