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!