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.)

C. Michael Pilato

C. Michael Pilato is a core Subversion developer, co-author of Version Control With Subversion (O'Reilly Media), and the primary maintainer of ViewVC. He works remotely from his home state of North Carolina as a software engineer for CollabNet, and has been an active open source developer since early 2001. Mike is a proud husband and father who loves traveling, soccer, spending quality time with his family, and any combination of those things. He also enjoys composing and performing music, and harbors not-so-secret fantasies of rock stardom. Mike has a degree in computer science and mathematics from the University of North Carolina at Charlotte.

Posted in Subversion
37 comments on “Authz and Anon Authn Agony
  1. Dan C says:

    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.

  2. C. Michael Pilato says:

    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.

  3. Thomas B says:

    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

  4. Thomas B says:

    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

  5. C. Michael Pilato says:

    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?

  6. Vladiuz says:

    Thanks for the article, Michael!
    It really saved me a lot of time and hair on my head!

  7. John says:

    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

  8. @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.

  9. John says:

    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

  10. @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).

  11. John says:

    Hi Michael,
    Thanks for pointing that out. :)
    Cheers,
    John

  12. Patricia Goldweic says:

    Thank you so much for your post. It was extremely useful (saved my day).
    -Patricia

  13. Patricia Goldweic says:

    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

  14. @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.

  15. Is it possible to use LDAP for authentication and still use SVN authz to restrict user access when using Apache as the server?

  16. C. Michael Pilato says:

    @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.

  17. Michael Adam says:

    @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

  18. Michael Adam says:

    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

  19. Michael Adam says:

    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

  20. JFO says:

    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!

  21. C. Michael Pilato says:

    @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

  22. JFO says:

    @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!

  23. Gabriel Ricardo says:

    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

  24. C. Michael Pilato says:

    @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.

  25. Gabriel Ricardo says:

    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

  26. sga says:

    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!

  27. C. Michael Pilato says:

    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.

  28. Santhosh Kumar says:

    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

  29. 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

  30. Santhosh Kumar says:

    Thank you very much

  31. Santhosh Kumar says:

    Hi Michael.
    I have configured svn in one server, I have another svn server also. If I am doing svn commit in the first server it should reflect in the second. Is there any chance to execute this. Please Assist me.Also I want to make a system trusted with one client machine, ie. only for root user it should not ask for password.
    Thanks in Advance!!!!!!!
    Santhosh Kumar K V

  32. Santhosh Kumar says:

    Waiting for your reply… just tell me it is possible or not…
    Thanks
    Santhosh

  33. JM says:

    Hi there,
    I pretty much had your third solution, but it doesn’t work.
    [groups]
    subversion = jm
    [/]
    * = r
    @subversion = rw
    [jm:/]
    * =
    jm = rw
    My apache conf contains
    Require valid-user
    which is the Debian default config, and should “allow anonymous read, but make committers authenticate themselves”, however I get (403 Forbidden), when trying to checkout repository jm, whether using svn “–username=jm” or not.
    I guess that’s because I would need to specify “* = r” for each repository separately…
    Is there a simple and intuitive way to do this?
    Greetings,
    JM

  34. JM says:

    enclose the “require valid-user” in a “LimitExcept GET PROPFIND OPTIONS REPORT” block BTW

  35. @JM: If you only require a valid user for write requests (those other than
    GET PROPFIND OPTIONS REPORT), then your read requests will hit the server
    anonymously regardless of what you pass via –username. Per the rules
    youve listed, you must be user jm to read from the jm repository.
    Therefore, anonymous (that is, not jm nor anybody else) reads from that
    repository are disallowed.

  36. Patrick Wong says:

    [repository-no-anon:/]
    * =
    @writers = rw
    If I read the subversion 1.5 (r3305) documentation correctly, wouldn’t the above example denied everybody to the ‘repository-no-anon’ repository since “* = “got matched first before “@writers = rw” ?
    Per documentation: “Another important fact is that the _first_ matching rule is the one which gets applied to user”

  37. C. Michael Pilato says:

    Patrick: I think that bit of the documentation might be just plain wrong. As far as I can tell, mod_authz_svn will parse all the rules in a section that apply to the user, and will allow the most permissive ones to “count”.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

CAPTCHA Image

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

connect with CollabNet
   Contact Us
sign up for emails
looking for something
conversations

CollabNet: #Agile2014 - need an ice breaker? Join us at the reception from 7-10pm for some food, drink, entertainment and fun!
Date: 28 July 2014 | 9:13 pm

CollabNet: RT @billportelli: #Cisco Brings DevOps To The Network http://t.co/JdupuQhdhR. #CollabNEt seeing SW governance & business agility need in th…
Date: 28 July 2014 | 9:04 pm

CollabNet: #CollabNet rolls out new #TeamForge for enterprise #agility and support of #Agile, #DevOps, & #continuousdelivery http://t.co/9cyDHE7qNQ
Date: 28 July 2014 | 5:15 pm