Friday, October 12, 2012

Setup Tomcat on CentOS for Windows Authentication using SPNEGO

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:
  1. Browser sends GET /hello_spnego.jsp
  2. Server returns 401 Unauthorized with Negotiate
  3. 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
    
  4. Client sends KRB5 TGS-REQ
  5. Client receives KRB5 KRB Error: KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
  6. 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
    
  7. 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.conf
spnego-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:
  1. 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.
  2. Caused by: java.io.IOException: Configuration Error:
            Line 9: expected [option key], found [null]
    
  3. 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.
  4. You should use the service account in corp network, not lab, to generate the keytable file like this:
  5. ktab -a serviceaccount@CORP.MYCOMPANY.COM -k appserver.keytab
    
  6. Make sure your
  7. 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.
  8. Ticket
      Tkt-vno: 5
      Realm: LAB.MYCOMPANY.COM
      Server Name ....
      enc-part rc5-hmac
        Encryption type: ...
        Kvno: 2 *** Here it is
        enc-part: ...
    
  9. 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:
  10. ktab -l -k appserver.keytab
    
  11. Which version of JDK seems not important. A keytab file generated by JDK 7 worked in JDK 1.6.0_32.
  12. I also got this Checksum error if I used my lab service account (serviceaccount@lab.mycompany.com) in pre-auth fields or keytab.
  13. 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)
    

No comments:

Post a Comment