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