From SELinux Wiki
Revision as of 09:11, 13 March 2010 by MichalSvoboda (Talk | contribs)

Jump to: navigation, search

Use case: SVN server

How I built a SELinux based server that holds the SVN repos of all our projects. Same thought patterns can be applied to securing any other sharing technology, not just SVN.


  1. SVN should be confined to its own domain
  2. Access to SVN should be provided via SSH using public key authentication
  3. SVN data should be labeled by own type with only SVN having access to them
  4. Administration of the repos should be restricted only to the system administrator (sysadm_r)
  5. Various SVN repos should be restricted only to certain people (ie. the project members)
  6. Within this restriction, some people should be granted read-only access
  7. Regular backups!

The policy module

I based the server on Debian 5.0, therefore I was dealing with quite an old release of refpolicy 2:0.0.20080702-16 (even for the launch time of the distro). Much water has passed since then so some things might need adjusting for newer refpolicies (I'll indicate those I know about).

The SVN module is pretty straightforward once you match the requirements to known macros. I'll start with the interface because it makes a line to follow.

               type svn_t, svn_exec_t, $1;    
               role $2;                       
       role $2 types svn_t;                 
               type svnadmin_t, svnadmin_exec_t, $1;
               role $2;                             
       role $2 types svnadmin_t;

The first two macros are the classical 'allow-$1 to transition to another type and add that type to their $2 role'.

               type svndata_t, $1;
               class file { manage_file_perms };
               class dir { manage_dir_perms };
       allow $1 svndata_t : file { manage_file_perms };
       allow $1 svndata_t : dir { manage_dir_perms };

This macro will grant the $1 type access to manipulate our precious SVN repos.

The .fc file will also be relatively straightforward:

/usr/bin/svnserve               gen_context(system_u:object_r:svn_exec_t,s0)
/usr/bin/svnadmin               gen_context(system_u:object_r:svnadmin_exec_t,s0)
/data/svn(/.*)?                 gen_context(system_u:object_r:svndata_t,s0)

Finally, the actual policy looks like this:

require {
       type sshd_t;
       type initrc_t;
       type home_root_t;
       class fifo_file { read write };
       class process { sigchld };     
       class dir { search_dir_perms };
type svn_t;
type svn_exec_t;
type svnadmin_t;
type svnadmin_exec_t;
type svndata_t;

We start with requirements and declarations of the types we already have seen. Some say that a require block is an atrocity, but not always is everything available through macros.

type svnuser_home_dir_t;
type svnuser_home_ssh_t;
type svnuser_home_t;    

Following that are the types to support the public key authentication for SSH. That is /home/user (home_dir_t) and /home/user/.ssh (home_ssh_t). The home_t is supposed to be for other files inside the /home/user but that option is currently unused.

allow svnuser_t svnuser_home_dir_t : dir { search_dir_perms };
allow svnuser_t home_root_t : dir { search_dir_perms };

This block creates the svnuser_u, svnuser_r and svnuser_t with minimal privileges to act as an user that can execute his shell. Remember that SSH will drop you to a shell from which you can (or are forced to) run the svnserve program. (Making svnserve the shell is a no-go because it takes the '-t' parameter ;) The two allow rules are also to support this scheme, because SSH will try to chdir() the user before running his shell.

Note that under red hat based policy the userdom_base_user_template() macro will add a pesky permission allowing the user to execute any program in /bin, /usr/bin, etc. which makes the setup somewhat less secure. If need be you can always roll out your own user template.

svnadmin_domtrans(sysadm_t, sysadm_r)
svn_domtrans(svnuser_t, svnuser_r)   

Next we call the two macros from our .if file to allow the respective users run svn and svnadmin under our domains. Running svnserve without transitioning to svn_t won't help the svn user much because it won't have access to the repo files.

application_domain(svn_t, svn_exec_t)
allow svn_t sshd_t : fifo_file { read write getattr };

type svn_tmp_t;
manage_dirs_pattern(svn_t, svn_tmp_t, svn_tmp_t)
manage_files_pattern(svn_t, svn_tmp_t, svn_tmp_t)
files_tmp_filetrans(svn_t, svn_tmp_t, { file dir })


Now it's finally time to say what svn_t can do. The first three macros are there to make the program even run. On newer refpolicies it might be that they already are integrated into application_domain(). Then I allow the program to use nscd (as I use it), to read non-sensitive stuff from /etc, to read the .po files from localization. The domain_use_interactive_fds and term_use_all_terms support operations where SSH allocates a tty for the user, while the allow fifo_file supports the opposite, ie. non-interactive invocation. The ssh_sigchld makes the SSH daemon know when our session has finished.

The tmp_t block allows svnserve to create and use some own stuff in /tmp with its own type svn_tmp_t.

And the call to our svn_manage_data macro is the 'gross' of the policy, ie. we want svnserve to access the repos ;)

application_domain(svnadmin_t, svnadmin_exec_t)

Finally, I give some rights to the svnadmin command. The system administrator (sysadm_r) will have rights to mess the repository files directly, but will require a relabel of the messed up files afterwards. Plus this can lay foundation for having a dedicated svn administrator role.

Having this, it's time to create the repos, relabel files, and test!

Separation of users and repos

In theory this step should be pretty straightforward using MCS. I gave labels to various repos and users, such as

/data/svn/squeek(/.+)?                             all files          system_u:object_r:svndata_t:s0:c0

... and the same label to people who should access them.

The pitfall is with new files labels. Suppose the user has access to two repos, and thus his high level is ie. s0:c0,c4. His 'svn commit' to a c0 repo will result in wrongly leveled file, depending on his low level. If the user has the label s0-s0:c0,c4 his 'svn commit' will create all the new files with s0 (too low), if he has the label s0:c0,c4-s0:c0,c4 he will create files with s0:c0,c4 (too high). If his level is s0:c0-s0:c0,c4 he will create correct s0:c0 files, but also in the c4 repo.

This can be solved in three ways:

  1. Add support to svnserve so that it creates new files with the label of the directory they're contained in
  2. Add support to selinux to do the same
  3. Relabel the files periodically

Of course the least painful way is the #3, so that's the route I've taken. A 5-minute cron job that runs "restorecon -R /data/svn" does the trick.

Note that this problem is relevant only to creating files, not reading them. (But if a new file has incorrect label, the repository will either leak or break.)