Thursday, February 4, 2010

Kerberos and WebLogic Server on Windows step-by-step

The month of Kerberos continues

I got a frantic call late last week asking for help getting WebLogic and Kerberos working. WebLogic would be deployed on Windows but, unlike in my previous post, this customer wanted IE to talk directly to WebLogic with no IIS server in between.

Easy enough, right? It is, but there are some nuances.

There are official docs available from download.oracle.com, but some people find them confusing. These are a bit simplified and are intended for this configuration only.

In this environment the desktop and web server machines are both in the same Windows domain and thus also in the same Kerberos Domain. It's important to note that in my case the machine is part of the domain, but it doesn't actually have to be in the domain to get configuration working.

Prerequisites:
  • You are using WebLogic with JDK 1.6 (either JRockit or the Sun JDK)
  • You have the Active Directory domain created
  • You have downloaded and installed WebLogic and may have created a domain
  • You are frustrated because the docs are confusing and despite working on this for hours you still can't get Kerberos authentication working
Setup the Kerberos KDC bits...

Before you do anything else we need to make sure that you're starting in a "good" state.

Delete any keytab files you've already created. Delete any cached keys (del "%USERPROFILE%\krb5cc*"). Clear out any config files you've created. And log out and back into the account in which you're running Internet Explorer. Also make keep my previous post in mind while you're testing.

If you have installed IIS on the machine uninstall it. IIS registers the kerberos service principal HTTP/machine and HTTP/machine.domain.com and if you leave IIS installed you'll never manage to get Kerberos on WebLogic working correctly.

Install the setspn utility. Info on the utility is available on MS TechNet, and it is installed as part of the Windows Server 2003 Support Tools from the Windows product CD.

Run the command "ldifde -f c:\export.txt" on your Windows server. This will export the entire contents of your Active Directory to a flat file so we can search it. What we need to do is make sure that no user or machine has already registered the Kerberos Service Principal we need for WebLogic. If you don't know what that means don't worry about it, just search the export.txt file for HTTP/machine (where machine is the name of the machine). If you find it in the file you'll need to use the setspn utility to remove the mapping. Here's what my export contained when things were broken:
dn: CN=WEBSERVER,CN=Computers,DC=kerbtest,DC=com
servicePrincipalName: HOST/WEBSERVER
servicePrincipalName: HOST/webserver.kerbtest.com
servicePrincipalName: HTTP/webserver
servicePrincipalName: HTTP/webserver.kerbtest.com
Use the setspn utility to remove the extraneous mappings. In my example above I had a mapping from the machine named "WEBSERVER" to the two principals HTTP/webserver and HTTP/webserver.kerbtest.com. I needed to remove those mappings before continuing on to the next step. You have to use the setspn utility with the -D flag to remove the mappings like so:
C:\>setspn -d HTTP/webserver webserver
Unregistering ServicePrincipalNames for CN=WEBSERVER,CN=Computers,DC=kerbtest,DC=com
HTTP/webserver
Updated object
C:\>setspn -d HTTP/webserver.kerbtest.com webserver
Unregistering ServicePrincipalNames for CN=WEBSERVER,CN=Computers,DC=kerbtest,DC=com
HTTP/webserver.kerbtest.com
Updated object
Finally we need to create a new user in the domain so that WebLogic can get the Kerberos secret associated with the Kerberos SPN. The official docs and basically everything else you'll find on the Internet says to make the username the same as the machine name; I disagree strongly. If you give the machine and user the same name you'll just confuse yourself later trying to figure out which is which plus the setspn tool assumes you're talking about the machine and there's no way to tell it to operate on the user instead. So when you create the username make it generic, for example "wlsuser", "webuser" or "webserveruser".

Once you've created that user use the setspn utility to associate the HTTP/machine and HTTP/machine.domain.com principals with the user you just created. Yes, you are going to associate the SPN with the user and NOT with the machine. I'll explain why later, but this is really important! When you do this you want HTTP to be in all caps and the machine name to be all lower case. The capitalization is important because the format has to match what Internet Explorer uses when it talks to the KDC. Here's what my output looks like:
C:\>setspn -a HTTP/webserver webuser
Registering ServicePrincipalNames for CN=web user,CN=Users,DC=kerbtest,DC=com
HTTP/webserver
Updated object
C:\>setspn -a HTTP/webserver.kerbtest.com webuser
Registering ServicePrincipalNames for CN=web user,CN=Users,DC=kerbtest,DC=com
HTTP/webserver.kerbtest.com
Updated object
You can use ldifde to export the contents of AD again just to be sure you've got it right.

Configure the WebLogic bits...

Install WebLogic as normal.
Create a new WebLogic domain using the Config Wizard.
Make a backup of config/config.xml. Don't ask, just do it!
Open the WebLogic console (http://localhost:7001/console by default)
In the left Domain Structure navigation menu click on Security Realms, then click on myrealm.
Click the Providers tab and you should see the list of Authentication Providers.
Click the New button to create a new authentication provider and select Negotiate from the drop down.
Go into the Identity Asserter's configuration and click on the Provider Specific tab. We don't want the NegotiateIdentityAsserter to run when you deploy an application with the authentication method set to Forms or Basic, so uncheck the "Form Based Negotiation Enabled" box.

Note that once you hit save and restart WebLogic you'll probably be locked out of the console until we do the next bunch of steps. If you wind up in trouble just back out the above changes by swapping your backup of config.xml in, restarting WebLogic and your web browser.

Copy a test app that requires Certificate authentication into your autodeploy directory. Then restart your server. If you don't have such app you'll have to create one.

When you access the app you should get a 401 error page from WebLogic. If you do an HTTP trace you should see an HTTP transaction that looks something like this
HTTP/1.1 401 Unauthorized
Date: Thu, 04 Feb 2010 21:44:10 GMT
Content-Length: 1518
Content-Type: text/html
WWW-Authenticate: Negotiate
the key to look for is WWW-Authenticate: Negotiate. If you see that the Negotiate Identity Asserter is running and you can go ahead and finish up the Kerberos setup.

Back to more Kerberos stuff...

We need to create the kerberos ini and keytab files:

Create a c:\windows\krb5.ini file that looks like the following. Update the domain and other information to reflect your environment:
[libdefaults]
default_realm = KERBTEST.COM
ticket_lifetime = 600

[realms]
KERBTEST.COM = {
kdc = 10.99.2.181
admin_server = testmachine
default_domain = KERBTEST.COM
}

[domain_realm]
.kerbtest.com = KERBTEST.COM

[appdefaults]
autologin = true
forward = true
forwardable = true
encrypt = true
In the official docs and all over the Internet you will find similar files with lines that start with default_tkt_enctypes and default_tgs_enctypes. As long as you are using JDK 1.6 you should be able to leave them out. The "kdc" setting is the IP address of your KDC and the "admin_server" setting is the host name of your KDC.

Run the command "c:\Oracle\Middleware\wlserver_10.3\common\bin\commEnv.cmd" to set your path and other environment variables so that you can run the Kerberos-related tools like ktab and kinit. Then cd into your domain's directory. In my case that's c:\Oracle\Middleware\user_projects\domains\base_domain.

The Kerberos related classes included with the JDK require a config file to run properly. Basically this file tells the GSS layer which classes are used to do the actual work and provides configuration information to those classes. Again, don't worry about what any of this means, just do the same thing I do and you'll be fine. Use notepad to create a krb5login.conf file with these contents:
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
principal="webuser@KERBTEST.COM"
useKeyTab=true
keyTab=keytab
storeKey=true
debug=true;
};

com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required
principal="webuser@KERBTEST.COM"
useKeyTab=true
keyTab=keytab
storeKey=true
debug=true;
};

com.sun.security.jgss.krb5.accept {
com.sun.security.auth.module.Krb5LoginModule required
principal="webuser@KERBTEST.COM"
useKeyTab=true
keyTab=keytab
storeKey=true
debug=true;
};
We need to create the keytab file I specified in the krb5login.conf file above. To create the file you use the ktab tool, and to verify its contents you use the kinit command line tool. Both of these tools come with the JDK so you want to make sure that you're using the same JDK as WebLogic is going to use. The instructions you find in the docs talk about using the ktab and kinit command line utilities directly, and if you want you can use them. I on the other hand like lots and lots of debugging information, so I prefer to invoke the same classes by running java.exe and passing a few extra command line options. Do whichever you feel more comfortable with and as long as kinit works everything is fine.

First create the keytab with kinit:
C:\Oracle\Middleware\user_projects\domains\base_domain>java.exe -Dsun.security.krb5.debug=true sun.security.krb5.internal.tools.Ktab -k keytab -a webuser@KERBTEST.COM
Password for webuser@KERBTEST.COM:abcd1234
Config name: C:\WINDOWS\krb5.ini
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 3 1 23 16 17.
>>> KeyTabEntry: key tab entry size is 46
>>> KeyTabEntry: key tab entry size is 46
>>> KeyTabEntry: key tab entry size is 54
>>> KeyTabEntry: key tab entry size is 62
>>> KeyTabEntry: key tab entry size is 54
Done!
Service key for webuser@KERBTEST.COM is saved in keytab
If you don't see ktab create a keytab file, or if the file winds up being just a few bytes then don't proceed any further since it won't work; instead go back and check the first bunch of steps - exporting the AD contents, checking the user you created, etc.

Next run kinit to insure that the keytab file is OK and that your configuration files are all setup correctly.
C:\Oracle\Middleware\user_projects\domains\base_domain>java.exe -Dsun.security.krb5.debug=true sun.security.krb5.internal.tools.Kinit -k -t keytab webuser@KERBTEST.COM
>>>KinitOptions cache name is C:\Documents and Settings\administrator.KERBTEST\krb5cc_administrator
Principal is webuser@KERBTEST.COM
>>> Kinit using keytab
>>> Kinit keytab file name: keytab
>>> KeyTabInputStream, readName(): KERBTEST.COM
>>> KeyTabInputStream, readName(): webuser
>>> KeyTab: load() entry length: 46; type: 3
>>> KeyTabInputStream, readName(): KERBTEST.COM
>>> KeyTabInputStream, readName(): webuser
>>> KeyTab: load() entry length: 46; type: 1
>>> KeyTabInputStream, readName(): KERBTEST.COM
>>> KeyTabInputStream, readName(): webuser
>>> KeyTab: load() entry length: 54; type: 23
>>> KeyTabInputStream, readName(): KERBTEST.COM
>>> KeyTabInputStream, readName(): webuser
>>> KeyTab: load() entry length: 62; type: 16
>>> KeyTabInputStream, readName(): KERBTEST.COM
>>> KeyTabInputStream, readName(): webuser
>>> KeyTab: load() entry length: 54; type: 17
Added key: 17version: 1
Added key: 16version: 1
Added key: 23version: 1
Added key: 1version: 1
Added key: 3version: 1
Ordering keys wrt default_tkt_enctypes list
Config name: C:\WINDOWS\krb5.ini
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 3 1 23 16 17.
>>> Kinit realm name is KERBTEST.COM
>>> Creating KrbAsReq
>>> KrbKdcReq local addresses for webserver are:

webserver/10.99.2.133
IPv4 address
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 3 1 23 16 17.
>>> KrbAsReq calling createMessage
>>> KrbAsReq in createMessage
>>> Kinit: sending as_req to realm KERBTEST.COM
>>> KrbKdcReq send: kdc=10.99.2.181 UDP:88, timeout=30000, number of retries =3,
#bytes=169
>>> KDCCommunication: kdc=10.99.2.181 UDP:88, timeout=30000,Attempt =1, #bytes=1
69
>>> KrbKdcReq send: #bytes read=210
>>> KrbKdcReq send: #bytes read=210
>>> reading response from kdc
>>> KDCRep: init() encoding tag is 126 req type is 11
>>>KRBError:
sTime is Sun Feb 07 14:40:09 EST 2010 1265571609000
suSec is 135477
error code is 25
error Message is Additional pre-authentication required
realm is KERBTEST.COM
sname is krbtgt/KERBTEST.COM
eData provided.
msgType is 30
>>>Pre-Authentication Data:
PA-DATA type = 11
PA-ETYPE-INFO etype = 23
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 15
Kinit: PREAUTH FAILED/REQ, re-send AS-REQ
>>>KrbAsReq salt is KERBTEST.COMwebuser
Pre-Authenticaton: find key for etype = 23
AS-REQ: Add PA_ENC_TIMESTAMP now
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbAsReq calling createMessage
>>> KrbAsReq in createMessage
>>> Kinit: sending as_req to realm KERBTEST.COM
>>> KrbKdcReq send: kdc=10.99.2.181 UDP:88, timeout=30000, number of retries =3,
#bytes=240
>>> KDCCommunication: kdc=10.99.2.181 UDP:88, timeout=30000,Attempt =1, #bytes=2
40
>>> KrbKdcReq send: #bytes read=1243
>>> KrbKdcReq send: #bytes read=1243
>>> reading response from kdc
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbAsRep cons in KrbAsReq.getReply webuser
New ticket is stored in cache file C:\Documents and Settings\administrator.KERBTEST\krb5cc_administrator
Notice that line "New ticket is stored". If you get that line it means that the Kerberos configuration is correct and that the Java code was able to acquire the secret key.

Last configuration step...

Edit the bin\startWebLogic.cmd to add the line set JAVA_OPTIONS below in the place I've shown it:
echo starting weblogic with Java version:

%JAVA_HOME%\bin\java %JAVA_VM% -version

set JAVA_OPTIONS=%JAVA_OPTIONS% -Dsun.security.krb5.debug=true -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.auth.login.config=krb5Login.conf -Djava.security.krb5.realm=KERBTEST.COM -Djava.security.krb5.kdc=testmachine.kerbtest.com
Restart WebLogic, access the site with IE and revel in your success.

Problems? Issues?

Hit me up in the comments!

19 comments:

  1. Hi

    I tried to configure kerberos with Weblogic 9.2 MP3 and when i open up the application page i'm getting http 403 error.

    Any ideas?

    ReplyDelete
  2. Can you give me a suggestion/step, I need to configure SSO for my J2ee env. Am using Weblogic 10.3,AD 2003 and Spring 3.0.

    ReplyDelete
  3. You could use the SPNEGO setup listed in this post to get desktop SSO to WLS. This assumes that all of the servers support SPNEGO. If not, you could use Oracle Access Manager, put that in front of the services. If all you have is WLS, and you don't need desktop SSO, I would just use the out of the box session capabilities of WLS.

    ReplyDelete
  4. Hi,

    Please help me to provide a doc for SPNEG0+WebLogic10.3+Kerberos+Win2k3 Setup so that it will work on Spring security 3.0.

    Please help.

    ReplyDelete
  5. I'm not Spring Security expert, but I would start here. I think this means that you don't do the SPNEGO set-up in WLS, but in Spring Security directly.

    ReplyDelete
  6. Dear Josh,

    Thanks for the article, very usefull for me.

    Please suggest me for my requirement:
    I understand that we need to generate keytab file for all windows users (who intend to use this web service), so how do I generate on keytab file for all such users.

    And in case in future; If more users need to be added/ removed, how best can we do this?

    Thanks and Regards
    Arunachalam.C
    Dubai

    ReplyDelete
  7. You don't need a keytab file for every user. The key tab has the credentials for the user account associated with the service's SPN. It simply to allow the service to authenticate its self with the KDC and decrypt tickets sent by others for the service.

    HTH,

    JB

    ReplyDelete
  8. Hello Josh,

    May I know how the authorization works if we implement this authentication mechanism. How do we know which user logged into the application? In NTLM authentication, we can get the user name of the user who logged in using this owa_util.get_cgi_env('REMOTE_USER');
    How to know which user logged into the application using this authentication mechanism?

    ReplyDelete
  9. Hi,
    I'm trying to configure SSO in Weblogic 10.0 MP1 over AIX without success. The jvm is 1.5_09 IBM JDK. This is a prerequisite for the application we're hosting in weblogic.

    This may be an issue?

    Thanks,
    Mario

    ReplyDelete
  10. Mario,

    As far as I know it should work with that JDK.

    The important things to look for are mentioned above - make sure you see the server asking for "Negotiate" authentication, that you see the browser sending a Kerberos ticket.

    In a later post I mentioned that I have run into trouble getting the browser to do Kerberos instead of NTLM. Info http://fusionsecurity.blogspot.com/2010/02/testing-your-weblogickerberos-setup.html

    What errors are you seeing?
    Have you opened a support request yet?

    ReplyDelete
  11. Santhoshi,

    Identity and Authorization work the same in Weblogic regardless of how you authenticate.

    So you should be able to use username and password, certificate, Kerberos, SPNEGO, or a custom Authenticator or Identity Asserter and not have to change the code in your application.

    HTH

    ReplyDelete
  12. Hi Josh,

    I'm trying to configure sso in my weblogic and AD. My weblogic is in Redhat and AD in win2003. I always get this error:

    Error 401--Unauthorized
    From RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1:
    10.4.2 401 Unauthorized
    The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.46) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity MAY include relevant diagnostic information. HTTP access authentication is explained in section 11.

    Really appreciate the help.

    Regards,
    Eric

    ReplyDelete
  13. Hi John,

    Currnetly our IT helpdesk guy has already registered HTTP services in domain which i am going to access on application server

    And before landing in your blog i was checking http://spnego.sourceforge.net/
    http://spnego.sourceforge.net/pre_flight.html

    and there i could see krb5.conf and login.conf
    files being talked about

    So my question is can we use those .conf files and make it accessible to weblogic

    please note that i am trying to implement kerberos with spnego with applicatioin server being weblogic11g

    Please let me know
    Thanks in advance,
    Sarfraz

    ReplyDelete
  14. Can you please clarify if you have done all your steps on windows 2003 server/XP? Does it work if Weblogic runs on Linux server? Do we need to generate ktab and kinit on windows 2003 server?

    ReplyDelete
  15. Hi

    I am getting the below error at my End

    >>>KRBError:
    sTime is Fri Jan 07 18:11:59 CET 2011 1294420319000
    suSec is 554452
    error code is 24
    error Message is Pre-authentication information was invalid
    realm is ARK.CHRIST.NET
    sname is krbtgt/ARK.CHRIST.NET
    eData provided.
    msgType is 30
    >>>Pre-Authentication Data:
    PA-DATA type = 11
    PA-ETYPE-INFO etype = 1
    <07.01.2011 18:11 Uhr MEZ> <Exception com.bea.common.security.internal.utils.negotiate.NegotiateTokenException: GSS
    Exception: No valid credentials provided (Mechanism level: Attempt to obtain new ACCEPT credentials failed!)
    com.bea.common.security.internal.utils.negotiate.NegotiateTokenException: GSSException: No valid credentials provided (Mechanism level: Attempt to obt
    ain new ACCEPT credentials failed!)
    at com.bea.common.security.internal.utils.negotiate.SPNEGONegotiateToken.getUsername(SPNEGONegotiateToken.java:180)
    at weblogic.security.providers.authentication.NegotiateIdentityAsserterProviderImpl.assertChallengeIdentity(NegotiateIdentityAsserterProviderI
    mpl.java:213)

    Any Help would be highly Appreciated

    ReplyDelete
  16. @keshav: Can you get in touch with me directly. christopher.johnson at oracle.com. I am working on a tool that helps troubleshoot these sorts of issues.

    ReplyDelete
  17. Hi Chris,



    Thanks for the document. Can you please little bit describe about the error 401 Unauthorized? I am still on this same error when I access the app even if I finish the full setup.





    Regards,

    Babu

    ReplyDelete
  18. I wrote another article on how HTTP and Kerberos works. Perhaps that will help? http://fusionsecurity.blogspot.com/2011/01/how-does-kerberos-actually-work-in-http.html

    ReplyDelete
  19. This comment has been removed by a blog administrator.

    ReplyDelete

Note: Only a member of this blog may post a comment.