Protect CXF Service in Fuse with Basic Authentication on LDAP Users - cxf

I have a SOAP/REST service implemented in CXF inside Red Hat JBoss Fuse (in a Fabric).
I need to protect it with Basic Authentication, and credentials must be checked on a LDAP server.
Can this be done without a custom interceptor?
Can I maybe use the container JAAS security (configured with LDAP) to protect the service the same way I can protect the console?

Yes the container JAAS security realm can be used to protect a web service.
An example is here.
The example page doesn't explain the implementation, but a quick look at the blueprint.xml file reveals the following configuration:
<jaxrs:server id="customerService" address="/securecrm">
<jaxrs:serviceBeans>
<ref component-id="customerSvc"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref component-id="authenticationFilter"/>
</jaxrs:providers>
</jaxrs:server>
<bean id="authenticationFilter" class="org.apache.cxf.jaxrs.security.JAASAuthenticationFilter">
<!-- Name of the JAAS Context -->
<property name="contextName" value="karaf"/>
</bean>
So it's just a matter of configuring a JAAS authentication filter.
"karaf" is the default JAAS realm for the container: users are defined in etc/users.properties
To define more realms, info is here.
To have users on LDAP, see here.

The answer above is correct, but please note that for more recent versions of Fuse (past 6.1), the "rank" in the LDAP configuration must be greater than 100 in order to override the default karaf realm.
Also, with current patches applied, in Fuse 6.2.X, connection pooling for the LDAP connections can be enabled:
<!-- LDAP connection pooling -->
<!-- http://docs.oracle.com/javase/jndi/tutorial/ldap/connect/pool.html -->
<!-- http://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html -->
context.com.sun.jndi.ldap.connect.pool=true
</jaas:module>
</jaas:config>
This is very important for high volume web-services. A connection pool is maintained to the LDAP server. This both avoids connection creation overhead and having closing sockets lingering in TIME-WAIT state.

Related

how to do snowfalkes DB JNDI connection in Websphere Liberty application server

Is there a way to configure snowflakes connection pooling in websphere application serve.
I tried below config inside server.xml file. But not working.
<dataSource id="SnowflakeDataSource" jndiName="jdbc/BM_SF" type="javax.sql.DataSource">
<properties db="abcd" schema="_TARGET" URL="jdbc:snowflake://adpdc_cdl.us-east-1.privatelink.snowflakecomputing.com" user="****" password="****" />
<jdbcDriver libraryRef="DatacloudLibs" javax.sql.DataSource="net.snowflake.client.jdbc.SnowflakeBasicDataSource"/>
</dataSource>
To clarify, the configuration that you have configures WebSphere Application Server Liberty's connection pooling for a Snowflake data source, rather than Snowflake's connection pooling.
The configuration that you have looks mostly pretty good.
When I looked up the SnowflakeBasicDataSource class that you are using, I can see that it has a property called "databaseName", not "db", so you'll need to switch that in your configuration.
You will also need to configure one of the jdbc-4.x features in Liberty if you haven't already, and if you plan to look it up in JNDI (vs inject it), you'll need the jndi-1.0 feature.
Here is an example with some corrections:
<featureManager>
<feature>jdbc-4.2</feature>
<feature>jndi-1.0</feature>
... your other features here
</featureManager>
<dataSource id="SnowflakeDataSource" jndiName="jdbc/BM_SF" type="javax.sql.DataSource">
<properties databaseName="abcd" schema="_TARGET" URL="jdbc:snowflake://adpdc_cdl.us-east-1.privatelink.snowflakecomputing.com" user="****" password="****" />
<jdbcDriver libraryRef="DatacloudLibs" javax.sql.DataSource="net.snowflake.client.jdbc.SnowflakeBasicDataSource"/>
</dataSource>
If this still doesn't work, look into your definition of the DatacloudLibs library to ensure that it is properly pointing at the Snowflake JDBC driver, and if it still doesn't work, post the error message that you see in case it helps to determine the cause.

ActiveMQ embedded bridge to Camel JMS bridge

I have an old application which handle JMS messages with ActiveMQ 5.8.0 and some JNDI remote topic connected to this ActiveMQ.
I have a connector like that :
<bean class="org.apache.activemq.network.jms.JmsConnector">
<property name="outboundTopicConnectionFactory" ref="jmsConnectionFactoryTo" />
<property name="outboundClientId" value="${remote.clientId}" />
<property name="jndiOutboundTemplate" ref="jndiTemplateTo" />
<property name="preferJndiDestinationLookup" value="true" />
<property name="inboundTopicBridges">
<list>
<bean class="org.apache.activemq.network.jms.InboundTopicBridge">
<property name="inboundTopicName" value="${remote.topic.to}"/>
<property name="localTopicName" value="${local.topic.to}"/>
<property name="consumerName" value="${remote.consumer.name}"/>
<property name="selector" value="${remote.selector}"/>
</bean>
</list>
</property>
</bean>
It works great, but now, for some technical reasons (strict JMS 1.1), I need to use "ConnectionFactory" instead of "TopicConnectionFactory".
With the actual configuration, I'm stuck because ActiveMQ seems to use "TopicConnectionFactory" instead of "ConnectionFactory", and my new class "MyConnectionFactoryImpl" implements "ConnectionFactory" now :
nested exception is org.springframework.beans.ConversionNotSupportedException:
Failed to convert property value of type 'com.webmethods.jms.impl.MyConnectionFactoryImpl'
to required type 'javax.jms.TopicConnectionFactory'
for property 'outboundTopicConnectionFactory';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type [com.webmethods.jms.impl.MyConnectionFactoryImpl]
to required type [javax.jms.TopicConnectionFactory] for property 'outboundTopicConnectionFactory':
no matching editors or conversion strategy found
In "org.apache.activemq.network.jms.JmsConnector" class, it use everywhere "TopicConnectionFactory", which is not recommended anymore in JMS 1.1.
EDIT :
According to #Justin Bertram, I need to use Camel instead of ActiveMQ embedded bridge. But I can't find any example of XML configuration which I can use to replace my actual two beans JMSConnector. Which is the simple way to do this keeping my XML config files ?
As the documentation for the JMS to JMS Bridge (i.e. org.apache.activemq.network.jms.JmsConnector) states:
ActiveMQ provides bridging functionality to other JMS providers that implement the JMS 1.0.2 and above specification.
In other words, the whole goal of the JMS to JMS Bridge is to use the JMS 1.0.2 interface(s). Changing it so that it only used JMS 1.1 would defeat the purpose.
The documentation also states that you should use Camel instead of the JMS to JMS Bridge:
Warning, try Camel first!
Note that we recommend you look at using Apache Camel for bridging ActiveMQ to or from any message broker (or indeed any other technology, protocol or middleware) as its much easier to:
keep things flexible; its very easy to map different queue/topic to one or more queues or topics on the other provider
perform content based routing, filtering and other Enterprise Integration Patterns
allows you to work with any technology, protocol or middleware, not just JMS providers
Therefore I recommend you use Camel instead of org.apache.activemq.network.jms.JmsConnector.
I would think that having your code return a TopicConnectionFactory would be the simplest solution. Even the JMS 2.0 specification provides the TopicConnectionFactory. No matter what version of ActiveMQ you are using, you certainly have the option of using the TopicConnectionFactory in your code and providing that to your bridge.
Note that the Camel route:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="mqseries:Foo.Bar"/>
<to uri="activemq:Cheese"/>
</route>
</camelContext>
has no error handling. For example, if the 'to' endpoint is down, this route will read from the 'from' endpoint and just throw the messages on the floor. Furthermore, if the 'to' component is not configured to use a caching/pooling connection factory, then a new JMS connection will be created for each message sent. This has poor performance and can result in many sockets in the TIME_WAIT state. Bottom line - beware trivial Camel routes.

How to connect to IBM MQ from a Camel route with SSL connection?

I could successfully connect to IBM MQ from a camel route and initialize the connection factory bean but now I want to connect with SSL.
I create the key store on the server side for the queue manager and create the certificate and add it to it.
I create a trust store on the client side and add the certificate to it.
And now I want the MQ connection factory to refer to the trust store while connecting to the server.
Here is what I tried:
<bean id="MyConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType" value="${queue.transportType}" />
<property name="channel" value="${queue.channel}" />
<property name="hostName" value="${queue.hostName}" />
<property name="port" value="${queue.port}" />
<property name="queueManager" value="${queue.manager}" />
<property name="sSLCipherSuite" value="SSL_RSA_WITH_NULL_MD5" />
<property name="sSLCertStores" value="file:C:/Servers/TrustStore/truststore.jks" />
</bean>
But this doesn't work. The following exception was returned:
JMSWMQ0018: Failed to connect to queue manager 'QM_TEST_SSL'
with connection mode 'Client' and host name '10.3.13.161(1415)'.;
nested exception is com.ibm.mq.MQException: JMSCMQ0001:
WebSphere MQ call failed with compcode '2' ('MQCC_FAILED')
reason '2397' ('MQRC_JSSE_ERROR').
Can anyone please help to direct me how to do that?
From a security standpoint the Client should only receive generic error messages which could relate to a number of problems. The best place to find out exactly why you client was rejected is the Queue Manager logs. I would suggest looking there to see if there are any errors that help you further determine the problem.
From the info given i can think of 3 problems it could be:
The Queue Manager channel is set with an attribute of SSLCAUTH(REQUIRED) however from the description you've given here the client doesn't appear to be using it's own certificate to connect. SSLCAUTH(REQUIRED) will mean that the Queue Manager will only accept connections on the particular channel where the client is connecting with a certificate it trusts. Check the channel definition and set SSLCAUTH(OPTIONAL)
Depending on your version of IBM MQ the CipherSpec you have used (SSL_RSA_WITH_NULL_MD5) is considered weak and will not be accepted by default. You can reenable these deprecated CipherSpecs and the instructions on how to do so can be found on the following Knowledge Center page
The truststore "C:/Servers/TrustStore/truststore.jks" is not being picked up by the client and so the client cannot trust the Queue Manager's certificate. Double check the path you have supplied and remove the "file:" you have added to the path (unless you were specifically instructed to include it).
You do not state which version of IBM MQ or what JRE that you are using, if it is not the most current version of IBM MQ and is being used with an Oracle JRE then the APAR IT10837 may help here.
There is a good write up of the above APAR at the end of IBM developerWorks blog "MQ Java, TLS Ciphers, Non-IBM JREs & APARs IT06775, IV66840, IT09423, IT10837 -- HELP ME PLEASE!" posted by Tom Leend. He includes a work around for Java clients that do not have this fix.
APAR IT10837
I've got one final APAR to mention and that is IT10837 (targeted for V7.1.0.8 and V7.5.0.7 and shipped in V8.0.0.5). This APAR affects applications running within Oracle JREs that use TLS CipherSuites to connect to a queue manager where the server-connection channel being used has the SSLCAUTH attributed set to "REQUIRED" (the default value). This means that the client should pass a certificate to the queue manager such that the connecting client can be authenticated by the MQ server.
When the application was running in an Oracle JRE, the SunJSSE provider was not creating a default internal Key Manager object for TLS socket connections, meaning that the client's signed personal certificates were not available for client authentication during the handshake. The IBM JSSE provider does do this based off the information passed via the Java System Properties:
javax.net.ssl.keyStore
and
javax.net.ssl.keyStorePassword
Because a KeyManager object was not created by default, the client certificate was not passed to the queue manager (GSKit) for authentication. As such, the connection from the application would failed. In this scenario, the queue manager would write the following error message into its error log file:
AMQ9637 (Channel is lacking a certificate)
The fix for this APAR is for the MQ classes for JMS and classes for Java to read a certificate keystore, based on the information in the two Java System Properties noted above, and create a KeyManager based on that information in the case when com.ibm.mq.cfg.useIBMCipherMappings is set to the value false in the JVM . This can then be used when the SSLContext is created (which is subsequently used to create an SSLSocketFactory and eventually a secure socket object).
There is a local workaround which is for the application itself to create
TrustManagerFactory and KeyManagerFactory factory objects for the appropriate certificate stores and to initialise an SSLContext object these objects. From this SSLContext object and SSLSocketFactory can be created and passed to the MQ classes for JMS (by setting it on the JMS Connection Factory) or to the classes for Java (by setting it on the MQEnvironment or in a Hashtable passed to the MQQueueManager constructor). For example:
---- Code Snippet Start ----
KeyStore keyStore = KeyStore.getInstance("JKS");
java.io.FileInputStream keyStoreInputStream = new java.io.FileInputStream("/home/tom/myKeyStore.jks");
keyStore.load (keyStoreInputStream, password_char_array);
KeyStore trustStore trustStore = KeyStore.getInstance ("JKS");
java.io.FileInputStream trustStoreInputStream = new java.io.FileInputStream("/home/tom/myTrustStore.jks");
trustStore.load (trustStoreInputStream, password_char_array);
keyStoreInputStream.close();
trustStoreInputStream.close();
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore,password);
trustManagerFactory.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(),
null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// classes for JMS
myJmsConnectionFactory.setObjectProperty(
WMQConstants.WMQ_SSL_SOCKET_FACTORY, sslSocketFactory);
// classes for Java
MQEnvironment.sslSocketFactory = sslSocketFactory;
---- Code Snippet End ----

Simple way to monitor Camel's localhost broker

as stated, is there a simple way to monitor Camel's vm incoming messages to the embedded broker ?
<bean id="jms" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false" />
</bean>
Thanks!
Your question isn't quite clear on what you would like to monitor.
-You could log what you send to the broker and read from it.
-You could connect to jmx to view a bunch of processing information.
-If you are on the FUSE platform there is a management console that exposes the jmx endpoints to a web url.
-You could also setup JBoss operations network to poll all of your jmx info and do trending on the data.
If I didn't cover the use case you had in mind please update your question with some details and ping me in a comment so I can get back to you.

Single Sign On (SSO): How to use Active Directory as an authentication method for CAS service?

I am developing a portal to Liferay and want to apply there a Single Sign On mechanism (SSO). I am using Jasig CAS for centralized authentication of my multiple web applications. Until now I know that I am able to use CAS as an authentication method but the next step would be to add some more intelligence and ask the authentication from an Active Directory server.
This should be possible by using AD as a "database" towards which the authentication is made, but I am new on these things and do not know how to make this with Jasig CAS.
Any clue how to accomplish this task?
I'm making a few assumptions here, so please let me know if I'm off target:
You're using a version of CAS between 3.3.2 and 3.4.8.
You want to tie CAS into Active Directory via LDAP (for Kerberos or SPNEGO see references below) using the Bind LDAP Handler (for FastBind see references below).
You're familiar with building CAS from source via Maven.
Prerequisite
If you're going to bind to AD via "ldaps://" (as opposed to "ldap://"), the JVM on your CAS server needs to trust the SSL certificate of your Active Directory server. If you're using a self-signed cert for AD, you'll need to import this into the JVM's trust store.
Summary
Within your CAS source tree, you'll need to make changes to the following files:
cas-server-webapp/pom.xml
cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xml
Details
pom.xml:
Add the following within <dependencies>:
<!-- LDAP support -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>cas-server-support-ldap</artifactId>
<version>${project.version}</version>
</dependency>
deployerConfigContext.xml:
Reconfigure your Authentication Handers:
Look for: <property name="authenticationHandlers">. Inside this is a <list>, and inside this are (probably) two <bean ...> elements
Keep this one:
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" />
The other <bean> (again, probably) corresponds to the current method of authentication you're using. (I'm not clear based upon the question, as there are several ways
CAS can do this without using external services. The default is SimpleTestUsernamePasswordAuthenticationHandler, this authenticates as long as username is equal to password). Replace that <bean> with:
<!-- LDAP bind Authentication Handler -->
<bean class="org.jasig.cas.adaptors.ldap.BindLdapAuthenticationHandler">
<property name="filter" value="uid=%u" />
<property name="searchBase" value="{your LDAP search path, e.g.: cn=users,dc=example,dc=com}" />
<property name="contextSource" ref="LDAPcontextSource" />
<property name="ignorePartialResultException" value="yes" /> <!-- fix because of how AD returns results -->
</bean>
Modify the "searchBase" property according to your AD configuration.
Create a Context Source for LDAP:
Add this somewhere within the root <beans> element:
<bean id="LDAPcontextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="pooled" value="false"/>
<property name="urls">
<list>
<value>{URL of your AD server, e.g.: ldaps://ad.example.com}/</value>
</list>
</property>
<property name="userDn" value="{your account that has permission to bind to AD, e.g.: uid=someuser, dc=example, dc=com}"/>
<property name="password" value="{your password for bind}"/>
<property name="baseEnvironmentProperties">
<map>
<entry>
<key>
<value>java.naming.security.authentication</value>
</key>
<value>simple</value>
</entry>
</map>
</property>
</bean>
Modify "urls", "userDn" and "password" accordingly.
Rebuild cas-server-webapp and try it.
References:
https://wiki.jasig.org/display/CASUM/LDAP
https://wiki.jasig.org/display/CASUM/Active+Directory

Resources