I setup a Tomcat server running on a Linux box with SPNEGO, so that the users can Single-Sign-On the server without typing their password. You can follow the instructions on
http://spnego.sourceforge.net/spnego_tomcat.html. Although this tutorial uses Windows for example, but the steps are same as the ones on Linux.
The big problem I faced was my company's network settings:
- There are two networks: corp.mycompany.com and lab.mycompany.com.
- lab trusts corp, but corp doesn't trust lab
The goal is "the users from a Windows machine in
corpcan access the tomcat server in
lab without typing username and password."
Here is a question: where should you create the pre-auth account: in
lab or
corp?
I tried to create a service account in
lab's AD, and registered SPNs in
lab. It didn't work. When I accessed
hello_spnego.jsp page on a Windows machine in
corp, I always got the dialog asking for username and password. This is because I enabled downgrade to basic authentication for NTLM. If I disabled basic authentication, I would get 500 error.
I used wireshark to catch the packets and found out the traffic as bellow:
- Browser sends GET /hello_spnego.jsp
- Server returns 401 Unauthorized with Negotiate
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1\r\n
WWW-Authenticate: Negotiate\r\n
WWW-Authenticate: Basic realm="LAB.MYCOMPANY.COM"\r\n
- Client sends KRB5 TGS-REQ
- Client receives KRB5 KRB Error: KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
Kerberos KRB-ERROR
Pvno: 5
MSG Type: KRB-ERROR(30)
stime: 2012-10-10 23:04:48 (UTC)
susec: 394362
error_code: KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
Realm: CORP.MYCOMPANY.COM
Server Name (Service and Instance): HTTP/tomcat.lab.mycompany.com
- Browser sends GET /hello_spnego.jsp HTTP/1.1, NTLMSSP_NEGOTIATE
Obviously, the machine in
corp tries to query its own realm
CORP.MYCOMPANY.COM to find the server SPN
HTTP/tomcat.lab.mycompany.com. That means we should register SPNs in
corp. After creating a new service account and registering SPNs in
corp, I changed the pre-auth account in
web.xml to
serviceaccount@CORP.COMPANY.COM, then everything worked.
Then I tried to use keytab method because I don't like to put username/password in plaintext in
web.xml. There are still a log of pitfalls in this step. Here are the working version of my
login.confspnego-client {
com.sun.security.auth.module.Krb5LoginModule required;
};
spnego-server {
com.sun.security.auth.module.Krb5LoginModule
required
useKeyTab=true
keyTab="conf/appserver.keytab"
principal="serviceaccount@CORP.MYCOMPANY.COM"
storeKey=true
isInitiator=false;
};
and
krb5.conf.
[libdefaults]
default_realm = LAB.MYCOMPANY.COM
default_tgs_enctypes = arcfour-hmac-md5 des-cbc-crc des-cbc-md5 des3-hmac-sha1
default_tkt_enctypes = arcfour-hmac-md5 des-cbc-crc des-cbc-md5 des3-hmac-sha1
clockskew = 300
[realms]
LAB.MYCOMPANY.COM = {
kdc = kdc1.lab.mycompany.com
kdc = kdc2.lab.mycompany.com
default_domain = lab.mycompany.com
}
[default_realm]
lab.mycompany.com = LAB.MYCOMPANY.COM
.lab.mycompany.com = LAB.MYCOMPANY.COM
You may encounter different issues if something is wrong. Here is my experience:
- If I don't quote the principal like this principal=serviceaccount@CORP.MYCOMPANY.COM, I will get the configuration error. And the message is misleading because line 9 is keyTab.
Caused by: java.io.IOException: Configuration Error:
Line 9: expected [option key], found [null]
- When you use ktab, the first thing you need to know is only windows version has this tool, while Linux RPM from oracle doesn't have it.
- You should use the service account in corp network, not lab, to generate the keytable file like this:
ktab -a serviceaccount@CORP.MYCOMPANY.COM -k appserver.keytab
- Make sure your
- I also encountered this error: KrbException: Specified version of key is not available (44). It turns out that the keytab file I generated with kvno=1 and the expected is 2. You can use wireshark to catch the packet for KRB5 TGT-REP, and it will tell you what kvno is expected.
Ticket
Tkt-vno: 5
Realm: LAB.MYCOMPANY.COM
Server Name ....
enc-part rc5-hmac
Encryption type: ...
Kvno: 2 *** Here it is
enc-part: ...
- You have to run ktab command multiple times to achieve the correct kvno just like this page http://dmdaa.wordpress.com/2010/05/08/how-to-get-needed-kvno-for-keytab-file-created-by-java-ktab-utility/. Use can just use ktab -l to find the kvno:
ktab -l -k appserver.keytab
- Which version of JDK seems not important. A keytab file generated by JDK 7 worked in JDK 1.6.0_32.
- I also got this Checksum error if I used my lab service account (serviceaccount@lab.mycompany.com) in pre-auth fields or keytab.
SEVERE: Servlet.service() for servlet [jsp] in context with path [] threw exception [GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)] with root cause
java.security.GeneralSecurityException: Checksum failed
at sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(ArcFourCrypto.java:388)
at sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(ArcFourHmac.java:74)