Wednesday, March 28, 2012

Restrict the gitolite user with SELinux

One of the things I have been working on is adding SELinux user profiles to all of our non-system users. Most recently, I wrote a custom SELinux role for the gitolite user, to further restrict what it is able to do. As I've had a hard time finding the right resources online, I figured I'll write it up here.

By default, all users are running as unconfined, meaning that things act pretty much as if SELinux was disabled. Included in the SELinux policy are 3 SELinux user profiles:
  • staff_u: can sudo
  • user_u: can do most things regular users can do
  • xguest_u: can run X and some applications, but not get on the network
  • guest_u: can't even run X, but can move files around
I'm not going into much detail -- see Fedora SELinux docs for more detailed distinctions between these roles. For the gitolite user, I wanted to put the same restrictions as guest_u, except allow it to transition to the gitosis_t domain (gitolite used to be known as "gitosis," so the policy name stuck).

Let's start by writing a new user policy for our gitolite user. I call it mygitoliteuser_u, and the policy will be in the file mygitoliteuser.te:
policy_module(mygitoliteuser, 1.0.0)

require {
    type system_mail_t;
    type postfix_postdrop_t;

role mygitoliteuser_r;

role mygitoliteuser_r types { system_mail_t postfix_postdrop_t };

gitosis_run(mygitoliteuser_t, mygitoliteuser_r)
gen_user(mygitoliteuser_u, user, mygitoliteuser_r, s0, s0)
A few things going on here:
  1. We base it off the userdom_restricted_user_template(), which is what guest_u uses.
  2. We allow it to run gitolite via the gitosis_run() interface.
  3. We additionally let it send email. Note, that theoretically this should be covered by the mta_role() interface, but it wasn't doing the right thing for me.
To compile and load the policy, run:
make -f /usr/share/selinux/devel/Makefile mygitoliteuser.pp
semodule -i mygitoliteuser.pp
Now set up the contexts for the new mygitoliteuser_u:
cd /etc/selinux/targeted/contexts/users
cat guest_u | sed 's/guest_/mygitoliteuser_/g' > mygitoliteuser_u
Now you need to assign this profile to the gitolite user:
usermod -Z mygitoliteuser_u gitolite
Now here is where things get annoying. Once you do this, don't try to run "restorecon /var/lib/gitolite", as this will screw up the labels on everything in that directory and label it as user_home_t. You see, all currently released versions of semanage assume that if a user has a real shell, its home directory needs to be labelled as user_home_t, which is sane reasoning, but doesn't work for things like gitolite user. There is a fix for this behaviour in libsemanage 2.1.5 -- you can set ignoredirs=/var/lib/gitolite in /etc/selinux/semanage.conf, but this is not helpful on RHEL6.

Anyway, the only real solution currently is to set up a cronjob that would make sure that everything in /var/lib/gitolite is labelled as gitosis_var_lib_t. I used puppet for this purpose:
file  { '/var/lib/gitolite':
  seltype => 'gitosis_var_lib_t',
  recurse => true,
That's about it. I may as well share my tweaks to the default gitosis policy here:
policy_module(mygitosis, 1.0.0)

require {
  type gitosis_t;
  type gitosis_exec_t;
  type tmp_t;
  type ssh_home_t;
  type bin_t;
  type fs_t;

# required by fork
allow gitosis_t gitosis_exec_t:file execute_no_trans;

# used by hooks (usually here-docs)
allow gitosis_t tmp_t:dir { write remove_name add_name };
allow gitosis_t tmp_t:file { write getattr read create unlink open };

# these appear bogus
dontaudit gitosis_t bin_t:file setattr;
dontaudit gitosis_t fs_t:filesystem getattr;



jokajak said...

Thanks for this write-up. Do you plan on submitting this policy change upstream, perhaps as a boolean?

Unknown said...

I have submitted a few patches to the upstream gitosis policy, but they haven't been applied yet, to my knowledge.