Well, here's what I hope is the definitive email on fronting CAS with  
SPNEGO in apache. Stop reading here if you still can say you don't  
know more about the internal handling of apache requests than you  
want to.


Problem: Authenticate Users via SPNEGO

Approach: Use Apache SPNEGO module to perform auth, setup CAS to  
trust the REMOTE_USER

The general setup discussed here is using Apache 2.0.x, Tomcat 5.5.x,  
mod_jk configured to pass the SSL env vars and hand off /cas/* to  
Tomcat.


As previously discussed, problems arise in a configuration where  
mod_auth_kerb is protecting /cas/login. The two possible outcomes are:

1. Client has a kerberos ticket and a SPNEGO capable browser.  
REMOTE_USER set, CAS uses  
PrincipalBearingCredentialsAuthenticationHandler to auth user. There  
is much rejoicing.

2. Client does not have a kerberos ticket or capable browser. Client  
is blocked from accessing /cas/login by apache and sees the standard  
Apache error document saying they failed to authenticate.

The second case is the problem, we want SPNEGO to be optional, but  
the stateless nature of HTTP connections and the apache  
authentication architecture don't provide for this. To understand the  
process, here's a step by step of the SPNEGO auth process.

1. Browser asks for SPNEGO protected resource (/private)
2. Apache returns a 401 error (authorization required) and sets a  
header to "Authorize: Negotiate" which tells the browser what  
authentication protocols are accepted.
3a. If the browser supports Negotiate (another name for SPNEGO) then  
it requests the protected resource again and includes it's own header  
"Authorization: <kerb ticket>"
3b. If the browser doesn't have a ticket or support Negotiate, it  
displays the Error Document that the server returned in another header.

Notice that authenticating with SPNEGO involves a second request,  
because there is not state from the first one. This is what prevents  
some kind of optional method. (Sidebar: Client certs get around this  
problem because there is a protocol negotiation process involved in  
setting the SSL connection, so the client and server can actually  
have an if-then conversation).


This morning I set out hoping to find a workaround that would allow a  
failover from a SPNEGO attempt. The most likely solution involved  
changing the ErrorDocument returned by Apache if the browser can't do  
SPNEGO.

First I changed the cas-ser     vlet.xml file to include a new URL  
mapping. I set /login2 to be the same as /login and point to the  
loginController. This would allow cas to answer on both of those  
urls. Testing showed that worked as expected, cas would authenticate  
users on either url using PKI or the web form. First hopeful sign.

The next step was to change the mod_auth_kerb apache config file to  
include a custom ErrorDocument directive:

<Location "/cas/login">
         AuthType Kerberos
         AuthName "Kerberos"
         KrbMethodNegotiate on
         KrbMethodK5Passwd off
         KrbAuthRealms   EXAMPLE.COM
         KrbServiceName  HTTP/[EMAIL PROTECTED]
         Krb5Keytab /etc/httpd/conf/keytab
         Require valid-user
         ErrorDocument 401 /cas/login2
</Location>

This allowed SPNEGO to protect /cas/login, but not /cas/login2.  
Another nice effect was that the user also never saw /cas/login2 in  
the URL address, but if SPNEGO failed they would proceed to the  
normal login. It was beautiful, worked in all the test cases, PKI,  
web form, the crowd was going wild...there was just one  
problem....CAS was never logging the user in via SPNEGO.

This took a while to figure out (understatement), but the reason is  
that if the ErrorDocument points to a Server Side Include document, a  
CGI script, or a module, then it does an internal redirect to that  
URL, never giving the browser a chance to complete the second step  
and send the Kerberos credentials. This is the case with /cas/login2,  
since it's a virtual URL handled by mod_jk. I also tried a .htacces  
file with a RewriteRule in it, but that generated an internal  
redirect as well.

(Sidenote: I was puzzled for a while trying to figure out why Safari  
was successfully using SPNEGO to log in sometimes. Turns out that if  
it already has a HTTP service ticket for the server, it will send it  
in the header of every request to that server. So if you went to  
another SPNEGO protected page first and had the HTTP service ticket,  
then Safari would successfully log in without receiving the 401  
failure first. Also explains why sometimes I'd be prompted for my  
kerberos pass when accessing a non-spnego protected page, I had an  
expired ticket with a HTTP service ticket.)

With this knowledge is was possible to set up a fairly ugly hack and  
get everything working. You can set the ErrorDocument to an actual  
file. Either a html file with a meta-redirect or a simple PHP script  
which does the same thing. Point them at /cas/login2, and away the  
user will go. SPNEGO will now work and the user will end up at a /cas/ 
login URL, and PKI and the web form will also work, but the user will  
see /cas/login2. Not ideal, but I can say it does work.


Considering the hackish nature of the only solution we could find and  
the ugliness surrounding the different URLs, we're probably going to  
wait for the CAS internal SPNEGO solution. But if this sounds like  
fun for you, hope it's useful.

Steve Cochran
Dartmouth College


_______________________________________________
Yale CAS mailing list
[email protected]
http://tp.its.yale.edu/mailman/listinfo/cas

Reply via email to