Thursday, April 24, 2008

Securing Subversion via LDAP -- A Followup

After some discussion of this on the Subversion mailing list we discovered that checking out the parent of a protected child will pull that child's contents to the local workspace. That probably isn't what you want so...

Let's say we have the repository paths .../parent and .../child and we want only a certain group of people to be able to checkout or update the child. In my initial pass at this I did the following:

<Location /svn/myRepo/parent>
Require valid-user
</Location>

<Location /svn/myRepo/parent/child>
Require group ...
</Location>

This will protect against the following:

svn ls http://myserver/svn/myRepo/parent/child
svn co http://myserver/svn/myRepo/parent/child

and commits against the child.

It will *not* protect against:

svn co http://myserver/svn/myRepo/parent

which will check out the child as it processes parent.

When we look in the apache logs we see a PROPFIND on the parent and some internal things but no mention of the child.

While digging through the mod_dav_svn source I discovered that, as it traverses the directory tree, it does an internal GET on each path to determine if the path is accessible. However, these GETs use the internal subversion URIs, not the ones that we naturally think of. So, to protect our child path, we can add another <Location/> tag thusly:

<Location ~ /svn/myRepo/!svn/ver/[0-9]+/parent/child>
Require group ...
</Location>

Note that this needs to come after:

<Location /svn/myRepo/!svn>
Require valid-user
</Location>

I wasted a couple of hours because I had that nugget tucked away in an included conf file following the one I was tweaking.

So, in order to protect the child the way we wanted to in the first place, our configuration would be:

<Location /svn/myRepo/parent>
Require valid-user
</Location>

<Location /svn/myRepo/parent/child>
Require group ...
</Location>

<Location ~ /svn/myRepo/!svn/ver/[0-9]+/parent/child>
Require group ...
</Location>

What's really nice about this is that a checkout of the parent silently ignores the now-protected child and populates the local workspace with everything that is unprotected.

I haven't tested it (yet) but it is reasonable to assume that the same technique will work with the more complicated <Limit/> and <LimitExcept/> tags.

7 comments:

Anonymous said...

So it means you cant do LDAP auth on svn withou Apache??????

James CE Johnson said...

Hi Benny,

It means I can't do LDAP without Apache. I'm sure that someone sufficiently motivated could. But the section of the SVN book labeled Choosing a Server Configuration implies that Apache is the way to get it done.

Setting up Apache is easy to do and, IMO, is worth the effort because then you have its full power available to your SVN server. If you're doing LDAP then you're probably in an Enterprise environment and, though I'm sure lots of people are successfully using svnserve in such a way, I wouldn't be comfortable without Apache.

Anonymous said...

Is this possible with One Repo and multiple projects/paths within?

IE:
/svn/Projects = repository
/svn/Projects/ProjectA = projectA/dir
/svn/Projects/ProjectB = projectB/dir
/svn/Projects/ProjectC = projectC/dir

James CE Johnson said...

Sure. That's exactly what we do. For various reasons we chose to implement a single Enterprise-level Subversion repository for all projects rather than a repository-per-project.

Our layout is something like this:
/svn
/svn/BusinessUnit
/svn/BusinessUnit/Product
/svn/BusinessUnit/Product/Project

It's under 'Project' where you get trunk / tags / branches though in some cases projects may have subprojects and even deeper before you get to that level.

Having the single repository was one of the driving factors for LDAP integration so that we could effectively isolate each Business Unit's subrepository from another's.

Markus Werle said...

Hi!

I have two questions:

1. You are using Directory instead of DirectoryMatch, but use regexps anyway. Why does this work?

2. What is the tilde in the regular expression you use?

James CE Johnson said...

Hi Markus,

When you're using the Directory tag you can use extended regular expressions with the addition of the ~ character. Without the tilde the path is expected to be an exact match. With the tilde it is treated as a regex. I don't know if there is some deep technical reason to prefer one over the other.

http://httpd.apache.org/docs/2.0/mod/core.html#directory

Markus Werle said...

So far I think that <LocationMatch regexp> is equivalent to <Location ~ regexp> or do you see any difference?

I still have problems with ACLs though (Satisfy Any required due to "Deny from all" directive in /svn): The following directives allow directory listings via web browser, but NOT via tortoise (403 error). Do you have any idea what goes wrong here?


<Location /svn/REPO/!svn>
Satisfy Any
Require valid-user
</Location>

<Location ~ (?i)^/svn/$>
Satisfy Any
<LimitExcept MERGE MKCOL POST PUT DELETE PATCH PROPPATCH>
Require valid-user
</LimitExcept>
#<Limit GET PROPFIND OPTIONS REPORT>
# Require valid-user
#</Limit>
</Location>

<Location ~ (?i)^/svn/REPO/$>
Satisfy Any
<LimitExcept MERGE MKCOL POST PUT DELETE PATCH PROPPATCH>
Require valid-user
</LimitExcept>
</Location>