Authz and Anon Authn Agony
A recent first-time attempt at using Subversion's path-based authorization module turned out to be less trivial than I'd planned because I was trying to use it with a repository that allowed anonymous read access. Things went well at first — I did some copying and pasting of sample httpd.conf directives and authz file contents from Version Control with Subversion, tweaking as necessary to suit my needs. In a short time, I had what I thought was the perfect setup. I was wrong.
Say, like me, you wish to configure a repository such that it permits anonymous reads to most of it, authenticated reads to the rest of it, and authenticated writes to the whole thing. You already have an Apache htpasswd file with your writers' usernames and password hashes, and you've configured Apache to use that htpasswd file for authentication, and an authz file for authorization. You then make the obvious additions to your authz file:
[groups]
writers = someuser1, someuser2, …
[repository:/]
* = r
@writers = rw
[repository:/trunk/private-area]
* =
@writers = rw
There's a group with your writers' usernames. There's a rule which grants anonymous read to the world, and write access to just the writers. And there's an override rule which removes read access from unauthenticated users in the repository's private area. Looks great.
Then you start testing.
Upon checking out your repository's /trunk directory, anonymous users get what you'd expect — the tree, minus the /trunk/private-area directory.
But what about your authenticated would-be writers? Ah, therein lies the rub! There are no authenticated users. Since anonymous users can checkout the tree, Apache never bothers to query you for authentication credentials. And you can't force Subversion to transmit authentication credentials when Apache hasn't asked for them.
So what's the workaround?
First, you could disable anonymous access altogether, and force non-writers to share a username like "anonymous" and a publicized password. In your authz rules, the user "anonymous" would have only read permission, and only on the public portion of the repository. This works fine, but at some discomfort to non-writers. They now have to supply a password which, though not secret, might still be non-obvious and/or unknown to them.
Secondly, you could just leave things the way they are, and force writers to checkout just the private area of the repository separately. They won't have the luxury of both the public and private areas being connected inside a single working copy, but that might be okay.
Thirdly, you could keep the private stuff in its own repository. For writers, this is very similar to the second workaround. But your writers won't be able to make a private thing public without breaking the history across repositories.
Finally, you could setup a second <Location> block in your httpd.conf file which points to the same repository but with a slightly different URL (for example, with "-no-anon" appended to it). In this block, disallow anonymous access. Then add a matching redundant entry in your authz file, too:
[repository-no-anon:/]
* =
@writers = rw
Now, anonymous non-writers can checkout from the original repository URL without prompting, and won't see the private area. Non-anonymous writers can checkout from the alternate repository URL with prompting, and will see the private area. (Thanks to Max Bowsher for this great hybrid workaround idea.)
ShareThis

The Subversion book points out the correct way to handle this: you need to set
AuthzSVNAccessFile /path/to/repo/authz
Require valid-user
Satisfy Any
with your authentication options.
That way the client will first try the request unauthenticated, mod_authz_svn will refuse it based on your authz file, and so Apache will return 401 telling the client to retry with authentication. The client then prompts the user for a password, Apache gets the same request but with credentials, it is accepted by mod_authz_svn, and everyone is happy.
Dan C | July 27, 2007 at 08:43 AM
Thanks for your comment, Dan C. But as one of the co-authors of the book, I definitely checked there first before claiming defeat. :-)
My httpd.conf is actually configured exactly as you (and the book) recommend. The problem in my situation is that only a subdirectory of my repository is meant to be private. That would work fine if Subversion fetched each file and directory in the tree individually, with separate HTTP requests which could each be individually optionally authenticated. But Subversion uses a single large REPORT request/response for checkouts and updates and such.
Because the root of my repository is readable by all (per my authz rules), mod_authz_svn won't refuse it based on my authz file, and Apache won't return 401 telling the client to retry with authn. That means my one checkout REPORT request will succeed as an anonymous user. But Subversion will still internally verify that my anonymous user has access to each and every path under the checked-out tree, and because that user doesn't have access to the private portion of the tree, Subversion will simply omit that subtree from the checkout altogether.
C. Michael Pilato | July 27, 2007 at 09:49 AM
I can't seem to get your suggestion to work; my situation is only slightly different. I have 3 classes of users: anon, regular, and special. In my repository, I have a single folder (let's say "private") that should be rw only for special users, invisible to everyone else. Anon can read everything but private, and normal users have rw on everything but private.
I have added a new Location entry to httpd.conf, which is exactly the same as the original one except the name is slightly different.
I then add the perms as you suggest to dav_svn.authz:
[repo:/]
* = r
@regularusers = rw
[repo:/path/to/private]
* =
# Now spec the alternate url, such that only special users have rw on everything
[repo-special:/]
@specialusers = rw
---
When I attempt to checkout using the new url repo-special, I still get the "403 Forbidden" error as before.
Any ideas?
Thanks...thomas b
Thomas B | November 30, 2007 at 08:32 AM
I can't seem to get your suggestion to work; my situation is only slightly different. I have 3 classes of users: anon, regular, and special. In my repository, I have a single folder (let's say "private") that should be rw only for special users, invisible to everyone else. Anon can read everything but private, and normal users have rw on everything but private.
I have added a new Location entry to httpd.conf, which is exactly the same as the original one except the name is slightly different.
I then add the perms as you suggest to dav_svn.authz:
[repo:/]
* = r
@regularusers = rw
[repo:/path/to/private]
* =
# Now spec the alternate url, such that only special users have rw on everything
[repo-special:/]
@specialusers = rw
---
When I attempt to checkout using the new url repo-special, I still get the "403 Forbidden" error as before.
Any ideas?
Thanks...thomas b
Thomas B | November 30, 2007 at 08:34 AM
Thomas B, does your second httpd.conf Location block demand authentication for all accesses, like my post recommends? Is there a chance that in your testing Subversion is using a cache non-special user's credentials against that special repository?
C. Michael Pilato | November 30, 2007 at 09:57 AM
Thanks for the article, Michael!
It really saved me a lot of time and hair on my head!
Vladiuz | June 26, 2008 at 02:51 AM
Hi Michael,
Is it possible to make some folders invisible to certain users? For example, assuming I only have 2 folders, FolderA & FolderB in my repository. Currently, irregardless of the user's permissions, a "svn ls" would list both folders. Is there a way to prevent certain users from seeing the existence of FolderB?
Thanks in advance.
Cheers,
John
John | July 21, 2008 at 12:37 AM
@John: Sure! What would path-based authorization be if not a means by which to authorize access to paths? Take a cruise though the Path-Based Authorization section of Version Control with Subversion (http://svnbook.red-bean.com/nightly/en/svn.serverconfig.pathbasedauthz.html). Basically, you need two things in place: 1) authentication, so that Subversion always know what user is accessing what, and then 2) the authorization rules that say which paths a particular user can and cannot access, and whether that access is read-only or read-write. Now, this only works when using Subversion in client-server model (hosted via Apache/mod_dav_svn or svnserve), but that's the most common deployment scenario anyway, so hopefully it's yours, too.
C. Michael Pilato | July 21, 2008 at 06:50 AM
Hi Michael,
Thanks for the prompt reply. Sorry if I was not clear in my previous post but I have implemented Path-Based Authorization (on Apache) and implemented authentication as well. The issue really is that a user who does not have read access to a folder will still be able to see the existence of the folder. Is there a way to hide the folder such that if a user does not have read access to the folder, he will not be able to see the folder itself?
For example:
[repo:/]
* = r
@regularusers = rw
[repo:/Logs]
* =
@admin = rw
In the above example, all users would be able to "see" the Logs folder even though they are unable to access it. Is there a way to disallow @regularusers to see the existence of the Logs folder?
Thanks!:)
Cheers,
John
John | July 21, 2008 at 07:17 AM
@John: OH! Sorry, yes, I misunderstood the question. Unfortunately, no, that's not possible with Subversion today. It's been documented since the inception of Subversion's authz policy as a known leakage (see http://svn.collab.net/repos/svn/trunk/notes/authz_policy.txt).
C. Michael Pilato | July 21, 2008 at 07:38 AM
Hi Michael,
Thanks for pointing that out. :)
Cheers,
John
John | July 21, 2008 at 06:08 PM
Thank you so much for your post. It was extremely useful (saved my day).
-Patricia
Patricia Goldweic | July 28, 2008 at 03:21 PM
I have a related question. If I follow your suggested use of the extra tag in the Apache configuration file, it all works out fine in terms of checkout. However, when it is time to create tags for a project that has some private folders, it seems that I'll have to go through the pain of setting the authorization rules for each of these tags, since, although I am tagging the whole trunk, I don't want the private folders to be accessible by the general public in my new tags :-(.
Do you have any suggestion as to how to deal with this problem? Or, would you perhaps say that what you described as your third option in your post (that is: create a separate repository that holds your private folders) may actually be the most practical approach in this case?
-Patricia
Patricia Goldweic | July 29, 2008 at 01:24 PM
@Patricia: Subversion's authorization logic is path-based, not object-based like a real ACL system might be. So yes, when you change the path of a versioned object deemed private by authz rules (either via a rename, or by exposing the object at multiple paths due to a copy such as you'd use when branching and tagging), you must update the authz rules to account for the change. For this reason, I think it very unwise to mix public and private data in the same tree if that data is likely to be branched or tagged.
C. Michael Pilato | July 29, 2008 at 01:36 PM
Is it possible to use LDAP for authentication and still use SVN authz to restrict user access when using Apache as the server?
Michael Griffith | November 13, 2008 at 09:29 AM
@Michael: Sure. I've not personally managed such a configuration (so I can't help you with the details), but my understanding is that this is a very common setup in corporate environments.
C. Michael Pilato | November 13, 2008 at 10:02 AM
@Michael Griffith:
You can enable LDAP authentication as follows (in apache config):
---------------------------------------------------
DAV svn
SVNPath /var/lib/svn
AuthzSVNAccessFile /var/lib/svn/authz
AuthLDAPEnabled on
AuthLDAPUrl ldap://ldap.server:389/base-dn
AuthType basic
AuthName "Subversion Repository"
require valid-user
---------------------------------------------------
One problem here is that ldap groups are not used in the authz access checks.
I have written a patch to the authz module that uses the system groups (via getgrnam)
instead of the groups defined in the authz file under the [groups] section.
I.e. using libnss_ldap, you get the groups from ldap for access checks!
I plan to propose this for upstream subversion code soon.
Cheers - Michael
Michael Adam | December 05, 2008 at 02:24 PM
Oops, sorry, I found a working configuration where instead of "AuthLDAPEnabled on",
the following two options are set:
---------------------------------
AuthzLDAPAuthoritative on
AuthBasicProvider ldap
---------------------------------
I currently don't have a setup available to verify correct workings.
But I think this was the correct version.
Michael
Michael Adam | December 05, 2008 at 02:35 PM
Oops, sorry, I found a working configuration where instead of "AuthLDAPEnabled on",
the following two options are set:
---------------------------------
AuthzLDAPAuthoritative on
AuthBasicProvider ldap
---------------------------------
I currently don't have a setup available to verify correct workings.
But I think this was the correct version.
Michael
Michael Adam | December 05, 2008 at 02:41 PM
Hi!
Can someone tell me whether would be possible to give a user read/write access to only a subdirectory inside a private directory where he can't even read the content?
To be more clear, think about the user JOHN accessing a repository with the following structure:
/trunk
/trunk/RestrictedToJohn-A (the files here should be unreadable by john)
/trunk/RestrictedToJohn-A/RestrictedToJohn-B (the files here should be unreadable by john)
/trunk/RestrictedToJohn-A/RestrictedToJohn-B/AllowedToJohn
...
JOHN can only read/write a specific subdiretory and I can't allow him to even read the content of the parent folders. The "AllowedToJohn" folder is part of the project contained in the parent folders so I don't think would be necessary to create a specific repository to store it.
I tried the following, without success:
[groups]
internal = user1,user2,user3,...
external = john
[repo:/]
* =
@internal = rw
[repo:/trunk/RestrictedToJohn-A/RestrictedToJohn-B/AllowedToJohn/]
@external = rw
Any advices are welcome!
Thank you!
JFO | December 02, 2009 at 08:17 AM
@JFO: What you are trying to do is quite common, but if you are seeing problems then I suspect you are hitting the likes of Subversion issue #3242[1], "Subversion demands unnecessary access to parent directories of operations".
[1] http://subversion.tigris.org/issues/show_bug.cgi?id=3242
C. Michael Pilato | December 02, 2009 at 09:24 AM
@C. Michael Pilato: Thank you to point me to the issue page!
The workaround suggested there by Christian Iversen worked for me. How I have a lot of subdirectories it gave me a little work to setup everything, but now it is fine.
Thank you very much!
JFO | December 06, 2009 at 04:17 AM
I was sent a link to your article after posting a help message regarding sub-directory permissioning on the subversion user group. The final work-around you suggest (creating another Location URL), does not seem to work if you want to also restrict read access to a sub-directory. It seems there is a security hole in this approach, which is that web-browsing of the anonymous URL still allows browsing of the restricted sub-directory. Just wondering if you had any suggestions for this case.
Thanks,
-Gabriel
Gabriel Ricardo | December 21, 2009 at 03:28 PM
@Gabriel: There is no security hole (besides the documented leak of the name of the private directory itself[1]). The authz rules don't cease to apply simply because the access is anonymous -- it's just that any access which requires a real username ain't gonna get granted.
This isn't merely a theoretical workaround. I use this approach myself for the 'svn-org' repository on svn.collab.net. Now, rather than use two name-based locations, I opted for another approach: anonymous access over http and authenticated access over https, but the concept is the same. (The benefit of the approach I took is that I don't need separate authz rules for my two "locations" since the repository's "name" is the same in the SSL vs. non-SSL cases.) So please, I invite you to visit that repository at http://svn.collab.net/repos/svn-org. And then browse the 'trunk/private' directory. Tell me what you find. :-) Not much more than "Nuh-uh" custom 404 handler, I'm guessing. If, however, you visit https://svn.collab.net/repos/svn-org (note the http*S*), and you have a valid login, and that username is granted read access to 'trunk/private', you can fully browse the contents of that directory.
[1] See 'KNOWN LEAKAGE OF UNREADABLE PATHS' in http://svn.apache.org/repos/asf/subversion/trunk/notes/authz_policy.txt for details.
C. Michael Pilato | December 21, 2009 at 04:19 PM
Yup, my mistake. An oversight in my testing as my browser session was caching the authenticated access. Details on the svn user group...
http://mail-archives.apache.org/mod_mbox/subversion-users/200912.mbox/browser
Gabriel Ricardo | December 21, 2009 at 05:38 PM
While setting up the authz file I am running into a problem similar to what others have seen. My authz file looks like:
[groups]
dev_all = user1, user2
qa_all = qa1, qa2
[/]
* = r
@dev_all = rw
[repo:/trunk/branches/V50]
@dev_all = r
[repo:/trunk/branches/V40]
@dev_all = r
With the above authz file, I would expect the dev_all group to not be able to commit changes to "repo:/trunk/branches/V40" and to "repo:/trunk/branches/V50". However, they are able to commit their changes. What am I doing wrong? Also, if I change the very first line from [/] to [repo:/], access is denied to everyone. Please help me - I have been at this for a couple of days now!
sga | January 14, 2010 at 10:21 AM
sga: Are you sure that the 'repo' string in your configuration string is accurate? If it's not clear, the format of those section headers is:
"[" + [REPOSITORY_NAME + ":"] + PATH + "]"
where REPOSITORY_NAME is the single-path-component "name" of your repository. By default, a repository's name is the basename of the directory where it lives (so, "floopy" for the path /usr/local/svn/repositories/floopy). But that can be changed via the SVNReposName mod_dav_svn directive.
Try removing the repository specifier from your headers temporarily (so that they read just as "[/trunk/branches/V50]" and "[/trunk/branches/V40]") and see if the situation improves. If it does, that might mean that Subversion is using a different "repository name" for your repository than you expected.
C. Michael Pilato | January 19, 2010 at 07:22 AM
Hi there,
I want to give svn web access to all(read only , Should not ask for password).
And commit access to restricted users. Is it possible. If Yes Please Help me.
Thanks in Advance for your reply.
Santhosh
Santhosh Kumar | March 14, 2010 at 10:01 PM
Santhosh, thats pretty much exactly what this post is talking about. If
all you want is repository-wide restrictions, you can follow the simple
instructions in the Subversion book for making this happen. See
http://svnbook.red-bean.com/en/1.5/svn.serverconfig.httpd.html#svn.serverconfig.httpd.authz.blanket
C. Michael Pilato | March 15, 2010 at 08:09 AM
Thank you very much
Santhosh Kumar | March 16, 2010 at 10:46 PM