ClearPass and Multiple Server Configurations

Background

Normally ClearPass stores the password information it collects in a non-distributed EhCache-based Map. This works fine in single-server CAS environments but causes issues in multi-server CAS environments. In a normal multi-server CAS environment you would use a Distributed Ticket Registry like the MemcacheTicketRegistry or the EhcacheTicketRegistry so that all CAS servers would have knowledge of all the tickets. After the Distributed Ticket Registry is setup you should replace ClearPass's default in-memory Map with a Map implemenation that matches your ticket registry. 

The Memcached Map

The spymemcached java client includes a Memcached Map implementation called CacheMap. We can easily leverage it with ClearPass by modifying the clearpass-configuration.xml under cas-server-webapp\src\main\webapp\WEB-INF\spring-configuration like this:

Memcached Clearpass-configuration.xml
  <bean id="CPserialTranscoder" class="net.spy.memcached.transcoders.SerializingTranscoder"
      p:compressionThreshold="2048" />
   	  
  <bean id="memcachedMap" class="net.spy.memcached.CacheMap">
    <constructor-arg index="0">
      <bean class="net.spy.memcached.spring.MemcachedClientFactoryBean"
            p:servers="memcache1.college.edu:11211,memcache2.college.edu:11211"
            p:protocol="BINARY"
            p:locatorType="ARRAY_MOD"
            p:failureMode="Redistribute"
            p:transcoder-ref="CPserialTranscoder">
        <property name="hashAlg">
          <util:constant static-field="net.spy.memcached.DefaultHashAlgorithm.FNV1A_64_HASH" />
        </property>
      </bean>
    </constructor-arg>
	<constructor-arg index="1" value="7200" /> <!-- this is the timeout for the cache in seconds -->
    <constructor-arg index="2" value="clearPass_" /> <!-- this is the prefix for the keys stored in the map -->
  </bean>  
  
  <bean id="credentialsCache" class="org.jasig.cas.extension.clearpass.EncryptedMapDecorator">
	<constructor-arg index="0" ref="memcachedMap" />		
	<constructor-arg index="1" value="salt1234" />		<!-- Replace the salt and secret key with one of your choosing -->
	<constructor-arg index="2" value="seCretKey0123456" />
  </bean>
  <!--
    NOTE:
    Name of delegated ticket registry bean in ticketRegistry.xml must be "ticketRegistryValue."
  -->
  <bean id="ticketRegistry" class="org.jasig.cas.extension.clearpass.TicketRegistryDecorator">
    <constructor-arg index="0" ref="ticketRegistryValue"/>
    <constructor-arg index="1" ref="credentialsCache"/>
  </bean>

In this example we create a memcachedMap bean that points at our Memcached servers (p:servers) and uses the Serializing Transcoder bean named CPserialTranscoder. Then, we create an EncryptedMapDecorator called credentialsCache that references the memcachedMap and defines a salt and encryption key. This encrypts the Memcached communication so your CAS servers are not passing sensitive authentication information in clear text. If you are using an SSH tunnel for your Memcached connections the Encrypted Map Decorator would not be necessary.

The EhCache-based Map

By default ClearPass is setup to use a non-distrbuted EhCache to store its passwords. If you are using the EhcacheTicketRegistry you will want to ensure that your ehcacheClearPass.xml file is setup to replicate the ClearPass Ehcache to all your CAS servers. The Ehcache Documentation is a good resource for setting up distributed EhCaches. A sample configuration follows:

WEB-INF/classes/ehcacheClearPass.xml
<ehcache name="clearPassEhCacheManager" updateCheck="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.sf.net/ehcache.xsd">
        <diskStore path="java.io.tmpdir/cas" />

        <cacheManagerPeerProviderFactory 
                class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
                properties="peerDiscovery=manual,
                rmiUrls=//<peerIpAddressOrName>:42001/clearPassCache" />

        <!-- Port where it listens for peers. Should be different from peer provider port defined above -->
        <cacheManagerPeerListenerFactory 
                class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
                properties="port=42001,remoteObjectPort=40002" />
</ehcache>

The set of port numbers used in this EhCache configuration should be different than the Ticket Registry EhCache configuration.

WEB-INF/spring-configuration/clearpass-configuration.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

  <!-- Credentials Cache implementation -->
  <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
        p:configLocation="classpath:ehcacheClearPass.xml" 
        p:shared="false"
        p:cacheManagerName="clearPassEhCacheManager"/>

  <bean id="clearPassEhCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"
        p:cacheManager-ref="ehCacheManager"
        p:bootstrapCacheLoader-ref="ticketCacheBootstrapCacheLoader" 
        p:cacheEventListeners-ref="ticketRMISynchronousCacheReplicator"
        p:cacheName="clearPassCache"
    p:timeToIdle="28800"
    p:timeToLive="0"/>

  <bean id="credentialsCache" class="org.jasig.cas.extension.clearpass.EhcacheBackedMap">
    <constructor-arg index="0" ref="clearPassEhCache"/>
  </bean>

  <!--
    NOTE: Name of delegated ticket registry bean in ticketRegistry.xml must be "ticketRegistryValue".
  -->
  <bean id="ticketRegistry" class="org.jasig.cas.extension.clearpass.TicketRegistryDecorator">
    <constructor-arg index="0" ref="ticketRegistryValue"/>
    <constructor-arg index="1" ref="credentialsCache"/>
  </bean>

  <!-- implementation of the clear pass vending service -->
  <bean id="clearPassController" class="org.jasig.cas.extension.clearpass.ClearPassController">
    <constructor-arg index="0" ref="credentialsCache"/>
  </bean>

  <bean id="handlerMappingClearPass" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
        p:alwaysUseFullPath="true">
    <property name="mappings">
      <props>
        <prop key="/clearPass">
          clearPassController
        </prop>
      </props>
    </property>
  </bean>

  <!-- Security configuration -->
  <bean id="clearPassFilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
    <sec:filter-chain-map request-matcher="ant">
      <sec:filter-chain pattern="/clearPass"
                        filters="casValidationFilter,httpServletRequestWrappingFilter"/>
    </sec:filter-chain-map>
  </bean>
  <!-- NOTE:
     It is dangerous to include a non-proxied CAS Filter for protecting /clearPass. Non-proxied CAS Filters
     like AuthenticationFilter don't honor the Filter chain proxy protection mechanism and, worse yet, allow access to the
     logged on user's cleartext password. It could be useful to enable this bean for easy testing of clearPass functionality however.-->
   <!--
  <bean id="casAuthenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter">
    <property name="casServerLoginUrl" value="${cas.securityContext.casProcessingFilterEntryPoint.loginUrl}"/>
    <property name="serverName" value="${server.name}"/>
  </bean>
  -->
  <!--
    NOTE:
    A bean named clearPassProxyList must be defined in deployerConfigContext.xml that defines
    the list of proxying services authorized to obtain clearpass credentials.
  -->
  <bean id="casValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter">
    <property name="serverName" value="${server.name}"/>
    <property name="exceptionOnValidationFailure" value="false"/>
    <property name="useSession" value="true"/>
    <property name="ticketValidator">
      <bean class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator">
        <constructor-arg index="0" value="${server.prefix}" />
        <property name="allowedProxyChains" ref="clearPassProxyList" />
      </bean>
    </property>
  </bean>

  <bean id="httpServletRequestWrappingFilter" class="org.jasig.cas.client.util.HttpServletRequestWrapperFilter"/>

  <bean id="clearPassProxyList" class="org.jasig.cas.client.validation.ProxyList">
    <constructor-arg>
        <list>
            <value>https://clearpass.client.app.edu/pwm/proxyCallback</value>
    </list>
    </constructor-arg>
  </bean>
</beans>

It should be noted that the EncryptedMapDecorator referenced in the Memcached Map example can also be applied to this example to encrypt the password payload as it syncs between CAS Server nodes.

Thanks to Waukesha County Technical College for graciously sharing their ClearPass EhCache realted config files.

Â