Monthly Archive: March 2010

Custom Identity Asserter for Weblogic Server

Identity Asserters are used in token based authentication mechanism. It’s very useful when we have to implement Single Sign on between WLS and some other Server. A Single Identity Asserter can support multiple token types, but only one is active at a time. We can develop an Authentication provider along with the Custom Identity Asserter if the users exists outside WLS. If we want to do perimeter authentication with users within WLS, we don’t have to develop an authenticator along with it.

The way it works is pretty straight forward. Whenever a request is made for a resource which is secure, and requires token based authentication, WLS checks the request header for the active token type. If the token is present, then the container passes it on the Identity Asserter’s assertIdentity method.

In the method we have to write the logic to parse the token and pass the token (username mostly) to the login module. The token can be passed in base64 encoded format or plain, depending on the type of token accepted by the identity asserter.

The steps to create it are the same as other providers.

First we need to create an MDF (Mbean definition file)

SimpleSampleServletAuthenticationFilter.xml

<?xml version=”1.0? ?>
<!DOCTYPE MBeanType SYSTEM “commo.dtd”>

<MBeanType
Name = “SimpleSampleIdentityAsserter”
DisplayName = “SimpleSampleIdentityAsserter”
Package = “examples.security.providers.identityassertion.simple”
Extends = “weblogic.management.security.authentication.IdentityAsserter”
PersistPolicy = “OnUpdate”
>

<MBeanAttribute
Name = “ProviderClassName”
Type = “java.lang.String”
Writeable = “false”
Preprocessor = “weblogic.management.configuration.LegalHelper.checkClassName(value)”
Default = “&quot;examples.security.providers.saf.simple.SimpleSampleServletAuthenticationFilter&quot;”
/>

<MBeanAttribute
Name = “ProviderClassName”
Type = “java.lang.String”
Writeable = “false”
Preprocessor = “weblogic.management.configuration.LegalHelper.checkClassName(value)”
Default = “&quot;examples.security.providers.identityassertion.simple.SimpleSampleIdentityAsserterProviderImpl&quot;”
/>
<MBeanAttribute
Name = “Description”
Type = “java.lang.String”
Writeable = “false”
Default = “&quot;WebLogic Simple Sample Identity Asserter Provider&quot;”
/>

<mbeanattribute
Name = “SupportedTypes”
Type = “java.lang.String[]“
Writeable = “false”
Default = “new String[] { &quot;MyToken&quot; }”
/>

<mbeanattribute
Name = “ActiveTypes”
Type = “java.lang.String[]“
Default = “new String[] { &quot; MyToken &quot; }”
/>

<MBeanAttribute
Name = “Version”
Type = “java.lang.String”
Writeable = “false”
Default = “&quot;1.0&quot;”
/>

</MBeanType>

Implement the IdentityAsserterV2 & AuthenticationProviderV2 SSPI.

SimpleSampleIdentityAsserterProviderImpl.java

/**
*
* @author faisalk
*/

package examples.security.providers.identityassertion.simple;

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.AppConfigurationEntry;
import weblogic.management.security.ProviderMBean;
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.AuthenticationProviderV2;
import weblogic.security.spi.IdentityAsserterV2;
import weblogic.security.spi.IdentityAssertionException;
import weblogic.security.spi.PrincipalValidator;
import weblogic.security.spi.SecurityServices;
import javax.servlet.http.HttpServletRequest;

public final class SimpleSampleIdentityAsserterProviderImpl implements AuthenticationProviderV2, IdentityAsserterV2
{
final static private String TOKEN_TYPE = “MyToken”;
final static private String TOKEN_PREFIX = “username=”;
private String description;

public void initialize(ProviderMBean mbean, SecurityServices services)
{
System.out.println(“SimpleSampleIdentityAsserterProviderImpl.initialize”);
SimpleSampleIdentityAsserterMBean myMBean = (SimpleSampleIdentityAsserterMBean)mbean;
description= myMBean.getDescription() + “\n” + myMBean.getVersion();
}

public String getDescription()
{
return description;
}

public void shutdown()
{
System.out.println(“SimpleSampleIdentityAsserterProviderImpl.shutdown”);
}

public IdentityAsserterV2 getIdentityAsserter()
{
return this;
}

public CallbackHandler assertIdentity(String type, Object token, ContextHandler context) throws IdentityAssertionException
{
System.out.println(“SimpleSampleIdentityAsserterProviderImpl.assertIdentity”);
System.out.println(“\tType\t\t= ” + type);
System.out.println(“\tToken\t\t= ” + token);

Object requestValue = context.getValue(“com.bea.contextelement.servlet.HttpServletRequest”);
if ((requestValue == null) || (!(requestValue instanceof HttpServletRequest)))
{
System.out.println(“do nothing”);
}
else{
HttpServletRequest request = (HttpServletRequest) requestValue;
java.util.Enumeration names = request.getHeaderNames();
while(names.hasMoreElements()){
String name = (String) names.nextElement();
System.out.println(name + “:” + request.getHeader(name));
}
}

// check the token type
if (!(TOKEN_TYPE.equals(type))) {
String error =” received unknown token type \”” + type + “\”.” +” Expected ” + TOKEN_TYPE;
System.out.println(“\tError: ” + error);
throw new IdentityAssertionException(error);
}

// make sure the token is an array of bytes
if (!(token instanceof byte[])) {
String error =”unknown token class \”” + token.getClass() + “\”.” +” Expected a byte[].”;
System.out.println(“\tError: ” + error);
throw new IdentityAssertionException(error);
}

// convert the array of bytes to a string
byte[] tokenBytes = (byte[])token;
if (tokenBytes == null || tokenBytes.length < 1) {String error =”received empty token byte array”;System.out.println(“\tError: ” + error);throw new IdentityAssertionException(error);}String tokenStr = new String(tokenBytes);// make sure the string contains “username=someusernameif (!(tokenStr.startsWith(TOKEN_PREFIX))) {String error =”received unknown token string \”” + type + “\”.” +” Expected ” + TOKEN_PREFIX + “username”;System.out.println(“\tError: ” + error);throw new IdentityAssertionException(error);}// extract the username from the tokenString userName = tokenStr.substring(TOKEN_PREFIX.length());System.out.println(“\tuserName\t= ” + userName);// store it in a callback handler that authenticators can use// to retrieve the username.return new SimpleSampleCallbackHandlerImpl(userName);}public AppConfigurationEntry getLoginModuleConfiguration(){return null;}public AppConfigurationEntry getAssertionModuleConfiguration(){return null;}public PrincipalValidator getPrincipalValidator(){return null;}}Copy the Provider Class and the MDF in a folder.Keep the following build script in the same folderbuild.xml

<project name=”Expenselink Build” default=”all” basedir=”.”>
<property name=”fileDir” value=”test” />

<target name=”all” depends=”build”/>

<target name=”build” depends=”clean,build.mdf,build.mjf”/>

<target name=”clean”>
<delete dir=”${fileDir}” failonerror=”false”/>
<delete file=”SimpleSampleIdentityAsserter.jar” failonerror=”false”/>
<echo message=”Clean finish” />
</target>

<!– helper to build an MDF (mbean definition file) –>
<target name=”build.mdf”>
<java dir=”${basedir}” fork=”false” classname=”weblogic.management.commo.WebLogicMBeanMaker”>
<arg line=”-files ${fileDir}” />
<arg value=”-createStubs” />
<arg line=”-MDF SimpleSampleIdentityAsserter.xml” />
</java>
<echo message=”Created Supporting Classes” />
</target>

<target name=”build.mjf”>

<copy todir=”${fileDir}” flatten=”true”>
<fileset dir=”.”>
<include name=”*.java” />
</fileset>
</copy>

<java dir=”${basedir}” fork=”false” classname=”weblogic.management.commo.WebLogicMBeanMaker”>
<arg line=”-MJF SimpleSampleIdentityAsserter.jar” />
<arg line=”-files ${fileDir}” />
</java>
<echo message=”Created Mbean Jar” />
</target>

</project>

Copy commo.dtd present in server lib to this directory.
Execute setWLSEnv.cmd and cd to this directory.
Type ant in the command prompt
An Identity Asserter jar file would be created in the same directory.

Place this jar file in WL_HOME\server\lib\mbeantypes
Restart the Server.
Go to Security Realm Providers, create a new Authentication Provider
Home > Summary of Security Realms > myrealm > Providers > Authentication > new Simple Sample Identity Asserter

On restart Identity Asserter will get invoked whenever the active token is present in the header.

References:-
http://download.oracle.com/docs/cd/E12840_01/wls/docs103/dvspisec/ia.html

Configuring SQL Authenticator with Weblogic Server.

Weblogic Server, by default stores the user and group information in an Embedded LDAP Server.

If we want to store the user and group information in a database, Weblogic Server provides an Out of the Box Provider, SQL Authenticator.

The SQL Authenticator uses a default schema, which can be modified.
To create the tables required by the SQL Authenticator using the default schema, execute the following query.

CREATE TABLE USERS (
U_NAME VARCHAR(200) NOT NULL,
U_PASSWORD VARCHAR(50) NOT NULL,
U_DESCRIPTION VARCHAR(1000))
;
ALTER TABLE USERS
ADD CONSTRAINT PK_USERS
PRIMARY KEY (U_NAME)
;
CREATE TABLE GROUPS (
G_NAME VARCHAR(200) NOT NULL,
G_DESCRIPTION VARCHAR(1000) NULL)
;
ALTER TABLE GROUPS
ADD CONSTRAINT PK_GROUPS
PRIMARY KEY (G_NAME)
;
CREATE TABLE GROUPMEMBERS (
G_NAME VARCHAR(200) NOT NULL,
G_MEMBER VARCHAR(200) NOT NULL)
;
ALTER TABLE GROUPMEMBERS
ADD CONSTRAINT PK_GROUPMEMS
PRIMARY KEY (
G_NAME,
G_MEMBER
)
;
ALTER TABLE GROUPMEMBERS
ADD CONSTRAINT FK1_GROUPMEMBERS
FOREIGN KEY ( G_NAME )
REFERENCES GROUPS (G_NAME)
ON DELETE CASCADE

Insert the User and Group records into the database.

insert into USERS (U_NAME,U_PASSWORD,U_DESCRIPTION) values(’system’,’weblogic’,’admin user’);

insert into GROUPS (G_NAME,G_DESCRIPTION) values(‘Adminsitrators’,’Adnministrators’);

insert into GROUPMEMBERS (G_NAME,G_MEMBER) values(‘Administrators’,’system’);

Create a datasource on Weblogic Server.

Create an SQLAuthenticator

myrealm > Providers > new SQLAuthenticator

Under myrealm > Providers > SQLAuthenticator > Provider Specific

Check Plaintext Passwords Enabled
Data Source Name: DS1

Leave the rest as default since we are using default schema.

In my example I have used plain text password.

If you want to log in to the console from users in the database, change the default authenticator flag as OPTIONAL.

Restart the server and log in as system.

Go to myrealm > Users and Groups to see the user and groups from the database.

Kerberos in a Proxy/Load Balancer/ Weblogic Cluster

Recently one of my colleague pointed out that I did not cover few aspects of Kerberos configurations in an earlier post. He had few queries such as how should he set the service principal name if a proxy is there in front of Weblogic Server. Or for that matter if there is a cluster of Weblogic Server.

Here are the answers.

If the proxy server is on the same machine as WLS, then the steps remain the same (outlined in an earlier post). The Kerberos ticket will be propagated to WLS.

If it’s in a different machine, then both the proxy url and the WLS url should be registered with WLS.

e.g.

WLS Server Machine: beaiis
Proxy Server Machine: beaproxy

setspn -a HTTP/ beaiis.BEATEST.COM beawin
setspn -a HTTP/ beaproxy.BEATEST.COM beawin

And then configure your client browser with the proxy server url.

For a cluster of Managed servers running on different machine.

WLS Server Machine1 : beaiisone
WLS Server Machine2 : beaiistwo
WLS Server Machine3 : beaiisthree
Proxy Server Machine :beaproxy

Then we have to register all the urls with the KDC

setspn -a HTTP/ beaiisone.BEATEST.COM beawin
setspn -a HTTP/ beaiistwo.BEATEST.COM beawin
setspn -a HTTP/ beaiisthree.BEATEST.COM beawin
setspn -a HTTP/ beaproxy.BEATEST.COM beawin

And then verify

setspn -L beawin
Registered ServicePrincipalNames for CN=beawin,CN=Users,DC=BEATEST,DC=COM

HTTP/beaproxy.BEATEST.COM
HTTP/beaiisone.BEATEST.COM
HTTP/beaiistwo.BEATEST.COM
HTTP/beaiisthree.BEATEST.COM

Each Server will have the same keytab and krb5Login.conf file, preferably copied to the domains directory on all machines. And in the Client browser the local internet setting should have the proxy url.

SSL Exceptions in Admin Server and Node Manager.

javax.net.ssl.SSLKeyException: [Security:090482]BAD_CERTIFICATE alert was received from oracle.test.com – xx.xxx.xx.xx. Check the peer to determine why it rejected the certificate chain (trusted CA configuration, hostname verification). SSL debug tracing may be required to determine the exact reason the certificate was rejected.

 

<WARNING> <Uncaught exception in server handlerjavax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake>javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:849) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1170)

The above exceptions are  the most common exceptions encountered during the setup of Weblogic Server in an environment. The stack does suggest what could be the reasons but the diagnostics are not mentioned.

To debug this issue, first we need to check the certificates used by Admin Server and the Node Manager. If we have Admin and the Node Manager using demo certificates, then the issue can be due to improper DNS mapping. We can use the nslookup to check the DNS entry. For testing purpose we can provide the ip address as the listen address for the admin server and the node manager and see if the issue is still occurring.

Also we will have to turn of host name verification and the basic validation check of the certificates. We can do it by specifying the following flag in startWeblogic.sh

-Dssl.debug=true -Dweblogic.security.SSL.ignoreHostnameVerification=true -Dweblogic.security.SSL.enforceConstraints=off

And the following flag in startNodeManager.sh

-Dssl.debug=true -Dweblogic.nodemanager.sslHostNameVerificationEnabled=false -Dweblogic.security.SSL.enforceConstraints=off

If the Admin Server is using Custom Identity and Custom trust, then its better to configure the node manger with custom identity and custom trust as well.
By default the Node Manager is configured with Demo Identity and Demo Trust. To change it to custom identity and custom trust, we need to specify the following values in the nodemanager.properties file present in nodemanager home

Keystores=CustomIdentityandCustomTrust
CustomIdentityAlias=
CustomIdentityKeyStoreFileName=
CustomIdentityKeyStorePassPhrase = xxxxxx
CustomIdentityKeyStoreType = JKS
CustomIdentityPrivateKeyPassPhrase = xxxxxxx

Apply the same flags as above in the startup script of Admin Server and Node Manger.

Check from the console whether Node Manager is reachable or not.

Another option can be to use PLAIN communication between Admin Server and Node Manager.

We can change the Listen Type to PLAIN for the Node Manager from the console and set the secureListener=false in the nodemanager.properties file present in nodemanager home.

References:-

http://download.oracle.com/docs/cd/E15051_01/wls/docs103/nodemgr/nodemgr_config.html#wp1101097