Archive for March, 2010

Tomcat Symlink Fun

Here’s how I stumbled across this (names changed to protect the guilty):
Suppose you have 4 tomcat instances, A1, A2, B1 and B2. A1 and A2 run application Apple/. B1 and B2 run application Breakfast. Someone decided “Hey, we can save deployment time if we symlink A1 and A2 to the same directory.”

Obvious questions aside (why bother setting up two if they’re going to be hosting the same content on the same box?), lets look at what happens when you misconfigure this relatively simple idea. The tomcat instances each had a webapps directory where their application was deployed, for example,  A1/webapps/Apple/ or B2/webapps/Breakfast/. The goal was to create a common deployment location for A1 and A2, B1 and B2, however a shortcut was taken and the webapps directory was symlinked rather than Apple and Breakfast, then BOTH Apple and Breakfast were placed in the shared directory. This resulted in 2 things:
* a unified place to deploy code
* every single tomcat instance loading BOTH Apple and Breakfast

This means B1/webapps/ now includes Apple/. While this may not seem like a problem (B2’s context root is set to Breakfast/, for example), it can lead to complications down the road:
* each instance now needs to load and compile things for both applications, resulting in wasted resources and startup time.
* If Breakfast/ happens to include it’s OWN Apple/ (because apples are part of a complete breakfast,) B2/webapps/Breakfast/Apple/ is now ignored in favor of B2/webapps/Apple/.

Fortunately I’d stumbled across this misconfiguration the day before others did, so I was able to quickly deduce what was wrong. Why didn’t I fix it? Because I wasn’t sure I was right, and I didn’t want to break someone elses apparently broken configuration any further. Besides, I’m trying to fight this obsessive feeling that “if I don’t do it myself, I won’t know if it’s right,” and doublechecking everyone’s work feeds that.

Fortunately the fix was quick and simple. It may not seem like that big of a deal, but you must remember this is a simplified view of the situation. the REAL setup had 10 applications, with some weighing in at 4-8 gigs each. While most of that is content (images and flash that tomcat doesn’t have to process), sorting through an extra 600k files *per project* will slow the system down.

Now, this behavior may seem dumb until you think of it from a tomcat point of view. “I know you have this /register/ under your context root, but you also have this application named register/, so I’m gonna go ahead and presume you want to run the full application rather thank this dinky directory. Besides, it’s probably a ‘sorry, registration is currently out of order’ in case the full application is taken offline.”

Complex ACLs in Apache Locations

So the problem I’m having is with limiting LDAP users access to WebDAV directories; specifically, how do I keep devs from committing to the release branch. The setup is each of the large Projects (Project1, Project2) has a trunk and release branch; however some pesky devs try to ninja changes into the release branch, circumventing the entire process. That’s bad. The access should go like this (note this is a subset of the mess I’m dealing with):

  1. Everyone can read and write all projects under / (/Project1, /Project2) EXCEPT:
    1. Only a certain team of devs (and I) can write to /Project1/trunk.
    2. Only a certain team of qa can write to /Project1/branches/release.

Specifically, I should be able to commit to trunk but not release, however that doesn’t seem to be the case. Here’s an abbreviated version of my vhost:

<VirtualHost 10.0.0.5:443>
 blah blah blah snip...
<Location />
     DAV svn
     SVNParentPath /var/svn/
     SVNPathAuthz off
     AuthName  "SVN Access"
     AuthType  Basic
     AuthLDAPUrl     "ldap://ldap.example.int:389/ou=Users,dc=example,dc=int?uid"
     AuthBasicProvider ldap
     AuthzLDAPAuthoritative off
     AuthLDAPGroupAttribute   "memberUid"
     <LimitExcept none>
         Require valid-user
     </LimitExcept>
 </Location>
 <Location /Project1/trunk>
     # Everyone can read, but only devs (and I) can change.
     <LimitExcept REPORT GET OPTIONS PROPFIND>
         Require ldap-group cn=devs,ou=Groups,dc=example,dc=int
         Require ldap-user morgajel
         Satisfy any
    </LimitExcept>
 </Location>
 <Location /Project1/branches/release>
    # Everyone can read, but only QA can change.
    <LimitExcept REPORT GET OPTIONS PROPFIND>
        Require ldap-group cn=qa,ou=Groups,dc=example,dc=int
        Satisfy any
    </LimitExcept>
 </Location>
</VirtualHost>

Any thoughts as to why I’m still able to write to release? and no, I’m not in the QA group; I suspect it has something to do with the Locations essentially being nested. Since we’re managing users and groups with LDAP, simple SVN ACLs won’t work, and I’m not really sure how to accomplish what I need to do.

Thoughts?

Side note: REPORT GET OPTIONS PROPFIND are the only methods needed for read-only svn webdav access. Fun fact, huh?

UPDATE:

I was overthinking the situation- Apache config is not programming. There is no inheritance between locations. There is no nesting. Once you create a new location, you need to set up perms for that, so setting the base / with read/write for everyone, I then define sublocations

 <Location /Project1/trunk>
    # If you want to write to trunk, you need to be one of the required people. You can still read it.
    <LimitExcept PROPFIND OPTIONS GET REPORT>
         Require ldap-group cn=devs,ou=People,ou=Groups,dc=mrm,dc=int
    </LimitExcept>
    <Limit PROPFIND OPTIONS GET REPORT>
         Require valid-user
    </Limit>
 </Location>
 <Location /Project1/branches/release>
    # Everyone can read, but only QA can change.
    <LimitExcept REPORT GET OPTIONS PROPFIND>
        Require ldap-group cn=qa,ou=Groups,dc=example,dc=int
        Satisfy any
    </LimitExcept>
    <Limit PROPFIND OPTIONS GET REPORT>
         Require valid-user
    </Limit>
 </Location>

What I was missing was the second half of the limits, thinking it would inherit from /. It doesn’t.  Without that require valid-user, it was allowing unauthenticated users to read the files (which was no good). Life is good (until I find out where this is broken).

UPDATE 2:

So the above didn’t work when I retested it, so I tried one last time with good results (note the goals have changed, but the principles still apply):

<VirtualHost 10.0.0.5:443>
    SSLEngine on
    SSLCertificateFile      /etc/pki/certs/example.int.crt
    SSLCertificateKeyFile   /etc/pki/certs/example.int.key
    DocumentRoot      /var/www/svn.example.int/docs
    ServerName        svn.example.int
    ServerAlias       svn.exampleco.com
    ErrorLog logs/svn.example.int-error_log
    CustomLog logs/svn.example.int-access_log common

    <Location />
        DAV svn
        SVNParentPath /var/svn/
        SVNPathAuthz off
        AuthName  "SVN Access"
        AuthType  Basic
        AuthLDAPUrl             "ldap://ldap.example.int:389    /ou=Users,dc=example,dc=int?uid"
        AuthBasicProvider ldap
        AuthzLDAPAuthoritative on
        AuthLDAPGroupAttribute   "memberUid"
        AuthLDAPGroupAttributeIsDN off
        order Deny,Allow
        Deny from all
        Satisfy any
       
        # access
        <LimitExcept NONE>
            Require valid-user
            Satisfy any
        </LimitExcept>
    </Location>
   
   
    <Location /ProjectA/branches/releases//>
        order Deny,Allow
        Deny from all
        Satisfy any
   
        #read-only access    
        <Limit GET PROPFIND OPTIONS REPORT>
            Require valid-user
            Satisfy any
        </Limit>
        # write access
        <LimitExcept GET PROPFIND OPTIONS REPORT>
            Require ldap-group cn=Administrators,ou=People,ou=Groups,dc=example,dc=int
            Require ldap-group cn=Team Leads,ou=People,ou=Groups,dc=example,dc=int
           #            Require ldap-user jmorgan
            Satisfy any
        </LimitExcept>
    </Location>
   
   
    <Location /ProjectB/branches/releases//>
        order Deny,Allow
        Deny from all
        Satisfy any
       
        #read-only access    
        <Limit GET PROPFIND OPTIONS REPORT>
            Require valid-user
            Satisfy any
        </Limit>
        # write access
        <LimitExcept GET PROPFIND OPTIONS REPORT>
            Require ldap-group cn=Administrators,ou=People,ou=Groups,dc=example,dc=int
            Require ldap-group cn=Team Leads,ou=People,ou=Groups,dc=example,dc=int
           #            Require ldap-user jmorgan
            Satisfy any
        </LimitExcept>
    </Location>
   
    <Location /ProjectC/branches/releases//>
        order Deny,Allow
        Deny from all
        Satisfy any
       
        #read-only access    
        <Limit GET PROPFIND OPTIONS REPORT>
            Require valid-user
            Satisfy any
        </Limit>
        # write access
        <LimitExcept GET PROPFIND OPTIONS REPORT>
            Require ldap-group cn=Administrators,ou=People,ou=Groups,dc=example,dc=int
            Require ldap-group cn=Team Leads,ou=People,ou=Groups,dc=example,dc=int
           #            Require ldap-user jmorgan
            Satisfy any
        </LimitExcept>
    </Location>
   
</VirtualHost>
   

I am SURE this one has some redundancy, but quite honestly, I don’t want to deal with it anymore- I have far more important things.

Go to Top