Commit ab7379bb authored by Misagh Moayyed's avatar Misagh Moayyed
Browse files

SPNEGO client config automated

parent ccccc4b7
......@@ -128,10 +128,12 @@ Then verify that your are able to read the keytab file:
* Internet Explorer - Enable _Integrated Windows Authentication_ and add the CAS server URL to the _Local Intranet_
zone.
* Firefox - Set the `network.negotiate-auth.trusted-uris` configuration parameter in `about:config` to the CAS server
URL, e.g. https://cas.example.com.
URL, e.g. `https://cas.example.com`.
### CAS Component Configuration
### Configuration
####Webflow Configuration
Define two new action states in `login-webflow.xml` before the `viewLoginForm` state:
{% highlight xml %}
......@@ -149,33 +151,27 @@ Define two new action states in `login-webflow.xml` before the `viewLoginForm` s
Additionally, find action `generateLoginTicket` - replace `viewLoginForm` with `startAuthenticate`.
Add two bean definitions in `cas-servlet.xml`:
Insert the appropriate action before SPNEGO initiation, assigning a `yes` response route to SPNEGO,
and a `no` response to route to viewing the login form.
{% highlight xml %}
<bean id="negociateSpnego" class="org.jasig.cas.support.spnego.web.flow.SpnegoNegociateCredentialsAction" />
<bean id="spnego" class="org.jasig.cas.support.spnego.web.flow.SpnegoCredentialsAction"
p:centralAuthenticationService-ref="centralAuthenticationService" />
<action-state id="eveluateClientRequest">
<evaluate expression="hostnameSpnegoClientAction" />
<transition on="yes" to="startAuthenticate" />
<transition on="no" to="generateLoginTicket" />
</action-state>
{% endhighlight %}
####Authentication Configuration
Update `deployerConfigContext.xml` according to the following template:
{% highlight xml %}
<bean id="jcifsConfig"
class="org.jasig.cas.support.spnego.authentication.handler.support.JcifsConfig"
p:jcifsServicePrincipal="HTTP/cas.example.com@EXAMPLE.COM"
p:kerberosDebug="false"
p:kerberosRealm="EXAMPLE.COM"
p:kerberosKdc="172.10.1.10"
p:loginConf="/path/to/login.conf" />
<bean id="spnegoAuthentication" class="jcifs.spnego.Authentication" />
<bean id="spnegoHandler"
class="org.jasig.cas.support.spnego.authentication.handler.support.JcifsSpnegoAuthenticationHandler"
p:authentication-ref="spnegoAuthentication"
p:principalWithDomainName="false"
p:NTLMallowed="true" />
p:principalWithDomainName="${cas.spengo.use.principal.domain:false}"
p:NTLMallowed="${cas.spnego.ntlm.allowed:true}" />
<bean id="spnegoPrincipalResolver"
class="org.jasig.cas.support.spnego.authentication.principal.SpnegoPrincipalResolver" />
......@@ -195,8 +191,7 @@ Update `deployerConfigContext.xml` according to the following template:
</bean>
{% endhighlight %}
Provide a JAAS `login.conf` file in a location that agrees with the `loginConf` property of the `JCIFSConfig` bean
above.
Provide a JAAS `login.conf` file:
jcifs.spnego.initiate {
com.sun.security.auth.module.Krb5LoginModule required storeKey=true useKeyTab=true keyTab="/home/cas/kerberos/myspnaccount.keytab";
......@@ -205,67 +200,79 @@ above.
com.sun.security.auth.module.Krb5LoginModule required storeKey=true useKeyTab=true keyTab="/home/cas/kerberos/myspnaccount.keytab";
};
## Client Selection Strategy
CAS provides a set of components that attempt to activate the SPNEGO flow conditionally, in case deployers need a configurable way to decide whether SPNEGO should be applied to the current authentication/browser request. The components provided are webflow actions that return either `yes` or `no` to the webflow and allow you to reroute the webflow conditionally based the outcome, to either SPNEGO or the normal CAS login flow.
You may use the following configuration in `cas.properties`:
{% highlight properties %}
# cas.spnego.ldap.attribute=spnegoattribute
# cas.spnego.ldap.filter=host={0}
# cas.spnego.ldap.basedn=
# cas.spnego.hostname.pattern=.+
# cas.spnego.ip.pattern=
# cas.spnego.alt.remote.host.attribute
# cas.spengo.use.principal.domain=false
# cas.spnego.ntlm.allowed=true
# cas.spnego.kerb.debug=false
# cas.spnego.kerb.realm=EXAMPLE.COM
# cas.spnego.kerb.kdc=172.10.1.10
# cas.spnego.login.conf.file=/path/to/login.conf
# cas.spnego.jcifs.domain=
# cas.spnego.jcifs.domaincontroller=
# cas.spnego.jcifs.netbios.cache.policy:600
# cas.spnego.jcifs.netbios.wins=
# cas.spnego.jcifs.password=
# cas.spnego.jcifs.service.password=
# cas.spnego.jcifs.socket.timeout:300000
# cas.spnego.jcifs.username=
# cas.spnego.kerb.conf=
# cas.spnego.ntlm=false
# cas.spnego.supportedBrowsers=MSIE,Trident,Firefox,AppleWebKit
# cas.spnego.mixed.mode.authn=false
# cas.spnego.send.401.authn.failure=false
{% endhighlight %}
The activation strategies are as follows:
## Client Selection Strategy
CAS provides a set of components that attempt to activate the SPNEGO flow conditionally,
in case deployers need a configurable way to decide whether SPNEGO should be applied to the
current authentication/browser request.
### By Remote IP
Checks to see if the request's remote ip address matches a predefine pattern.
```xml
<bean id="baseSpnegoClientAction"
class="org.jasig.cas.support.spnego.web.flow.client.BaseSpnegoKnownClientSystemsFilterAction"
c:ipsToCheckPattern="127.+"
c:alternativeRemoteHostAttribute="alternateRemoteHeader" />
```
{% highlight xml %}
...
<evaluate expression="baseSpnegoClientAction" />
...
{% endhighlight %}
### By Hostname
Checks to see if the request's remote hostname matches a predefine pattern. This action supports all functionality provided by `BaseSpnegoKnownClientSystemsFilterAction`.
Checks to see if the request's remote hostname matches a predefine pattern.
```xml
<bean id="hostnameSpnegoClientAction"
class="org.jasig.cas.support.spnego.web.flow.client.HostNameSpnegoKnownClientSystemsFilterAction"
c:hostNamePatternString="something.+" />
```
{% highlight xml %}
...
<evaluate expression="hostnameSpnegoClientAction" />
...
{% endhighlight %}
### By LDAP Attribute
Checks an LDAP instance for the remote hostname, to locate a pre-defined attribute whose mere existence would allow the webflow to resume to SPNEGO. This action supports all functionality provided by `BaseSpnegoKnownClientSystemsFilterAction`.
Checks an LDAP instance for the remote hostname, to locate a pre-defined attribute whose mere existence
would allow the webflow to resume to SPNEGO.
```xml
{% highlight xml %}
<bean id="ldapSpnegoClientAction"
class="org.jasig.cas.support.spnego.web.flow.client.LdapSpnegoKnownClientSystemsFilterAction"
c:connectionFactory-ref="connectionFactory"
c:searchRequest-ref="searchRequest"
c:spnegoAttributeName="spnegoAttribute" />
```
Sample search request and filer:
c:spnegoAttributeName="${cas.spnego.ldap.attribute:spnegoAttribute}" />
```xml
<bean id="searchRequest" class="org.ldaptive.SearchRequest"
p:baseDn-ref="baseDn"
p:searchFilter-ref="searchFilter"/>
<bean id="searchFilter" class="org.ldaptive.SearchFilter"
c:filter="host={0}" />
c:filter="${cas.spnego.ldap.filter:host={0}}" />
<bean id="baseDn" class="java.lang.String">
<constructor-arg type="java.lang.String" value="${ldap.baseDn}" />
<constructor-arg type="java.lang.String" value="${cas.spnego.ldap.basedn:}" />
</bean>
```
### Webflow Configuration
Insert the appropriate action before SPNEGO initiation, assigning a `yes` response route to SPNEGO, and a `no` response to route to viewing the login form.
```xml
<action-state id="eveluateClientRequest">
<evaluate expression="hostnameSpnegoClientAction" />
<transition on="yes" to="startAuthenticate" />
<transition on="no" to="generateLoginTicket" />
</action-state>
...
```
{% endhighlight %}
......@@ -19,6 +19,7 @@
package org.jasig.cas.support.spnego.authentication.handler.support;
import jcifs.Config;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
......@@ -59,6 +60,10 @@ public final class JcifsConfig implements InitializingBean {
private static final String JCIFS_PROP_CLIENT_PASSWORD = "jcifs.smb.client.password";
private static final String JCIFS_PROP_CLIENT_SOTIMEOUT = "jcifs.smb.client.soTimeout";
private static final String JCIFS_PROP_NETBIOS_CACHE_POLICY = "jcifs.netbios.cachePolicy";
/**
* -- the service principal you just created. Using the previous example,
* this would be "HTTP/mybox at DOMAIN.COM".
......@@ -80,15 +85,15 @@ public final class JcifsConfig implements InitializingBean {
* Instantiates a new jCIFS config.
*/
public JcifsConfig() {
Config.setProperty("jcifs.smb.client.soTimeout", "300000");
Config.setProperty("jcifs.netbios.cachePolicy", "600");
Config.setProperty(JCIFS_PROP_CLIENT_SOTIMEOUT, "300000");
Config.setProperty(JCIFS_PROP_NETBIOS_CACHE_POLICY, "600");
}
@Override
public void afterPropertiesSet() throws Exception {
final String propValue = System.getProperty(SYS_PROP_LOGIN_CONF);
if (propValue != null) {
logger.warn("found login config in system property, may overide : {}", propValue);
logger.warn("found login config in system property, may override : {}", propValue);
}
URL url = getClass().getResource(
......@@ -107,15 +112,19 @@ public final class JcifsConfig implements InitializingBean {
logger.debug("configured login configuration path : {}", propValue);
}
/**
* Sets the jcifs service password.
*
* @param jcifsServicePassword the new jcifs service password
*/
public void setJcifsServicePassword(final String jcifsServicePassword) {
if (StringUtils.isNotBlank(jcifsServicePassword)) {
logger.debug("jcifsServicePassword is set to *****");
Config.setProperty(JCIFS_PROP_SERVICE_PASSWORD, jcifsServicePassword);
}
}
/**
* Sets the jcifs service principal.
......@@ -123,9 +132,11 @@ public final class JcifsConfig implements InitializingBean {
* @param jcifsServicePrincipal the new jcifs service principal
*/
public void setJcifsServicePrincipal(final String jcifsServicePrincipal) {
if (StringUtils.isNotBlank(jcifsServicePrincipal)) {
logger.debug("jcifsServicePrincipal is set to {}", jcifsServicePrincipal);
Config.setProperty(JCIFS_PROP_SERVICE_PRINCIPAL, jcifsServicePrincipal);
}
}
/**
* Sets the kerberos conf.
......@@ -133,9 +144,12 @@ public final class JcifsConfig implements InitializingBean {
* @param kerberosConf the new kerberos conf
*/
public void setKerberosConf(final String kerberosConf) {
if (StringUtils.isNotBlank(kerberosConf)) {
logger.debug("kerberosConf is set to :{}", kerberosConf);
System.setProperty(SYS_PROP_KERBEROS_CONF, kerberosConf);
}
}
/**
* Sets the kerberos kdc.
......@@ -143,9 +157,12 @@ public final class JcifsConfig implements InitializingBean {
* @param kerberosKdc the new kerberos kdc
*/
public void setKerberosKdc(final String kerberosKdc) {
if (StringUtils.isNotBlank(kerberosKdc)) {
logger.debug("kerberosKdc is set to : {}", kerberosKdc);
System.setProperty(SYS_PROP_KERBEROS_KDC, kerberosKdc);
}
}
/**
* Sets the kerberos realm.
......@@ -153,9 +170,11 @@ public final class JcifsConfig implements InitializingBean {
* @param kerberosRealm the new kerberos realm
*/
public void setKerberosRealm(final String kerberosRealm) {
if (StringUtils.isNotBlank(kerberosRealm)) {
logger.debug("kerberosRealm is set to :{}", kerberosRealm);
System.setProperty(SYS_PROP_KERBEROS_REALM, kerberosRealm);
}
}
public void setLoginConf(final String loginConf) {
this.loginConf = loginConf;
......@@ -177,47 +196,83 @@ public final class JcifsConfig implements InitializingBean {
* @param kerberosDebug the new kerberos debug
*/
public void setKerberosDebug(final String kerberosDebug) {
if (StringUtils.isNotBlank(kerberosDebug)) {
logger.debug("kerberosDebug is set to : {}", kerberosDebug);
System.setProperty(SYS_PROP_KERBEROS_DEBUG, kerberosDebug);
}
}
/**
* @param jcifsDomain the jcifsDomain to set
*/
public void setJcifsDomain(final String jcifsDomain) {
if (StringUtils.isNotBlank(jcifsDomain)) {
logger.debug("jcifsDomain is set to {}", jcifsDomain);
Config.setProperty(JCIFS_PROP_CLIENT_DOMAIN, jcifsDomain);
}
}
/**
* @param jcifsDomainController the jcifsDomainController to set
*/
public void setJcifsDomainController(final String jcifsDomainController) {
if (StringUtils.isNotBlank(jcifsDomainController)) {
logger.debug("jcifsDomainController is set to {}", jcifsDomainController);
Config.setProperty(JCIFS_PROP_DOMAIN_CONTROLLER, jcifsDomainController);
}
}
/**
* @param jcifsPassword the jcifsPassword to set
*/
public void setJcifsPassword(final String jcifsPassword) {
if (StringUtils.isNotBlank(jcifsPassword)) {
Config.setProperty(JCIFS_PROP_CLIENT_PASSWORD, jcifsPassword);
logger.debug("jcifsPassword is set to *****");
}
}
/**
* @param jcifsUsername the jcifsUsername to set
*/
public void setJcifsUsername(final String jcifsUsername) {
if (StringUtils.isNotBlank(jcifsUsername)) {
logger.debug("jcifsUsername is set to {}", jcifsUsername);
Config.setProperty(JCIFS_PROP_CLIENT_USERNAME, jcifsUsername);
}
}
/**
* @param jcifsNetbiosWins the jcifsNetbiosWins to set
*/
public void setJcifsNetbiosWins(final String jcifsNetbiosWins) {
if (StringUtils.isNotBlank(jcifsNetbiosWins)) {
logger.debug("jcifsNetbiosWins is set to {}", jcifsNetbiosWins);
Config.setProperty(JCIFS_PROP_NETBIOS_WINS, jcifsNetbiosWins);
}
}
/**
* Sets jcifs netbios cache policy.
*
* @param policy the policy
*/
public void setJcifsNetbiosCachePolicy(final String policy) {
if (StringUtils.isNotBlank(policy)) {
logger.debug("jcifsNetbiosCachePolicy is set to {}", policy);
Config.setProperty(JCIFS_PROP_NETBIOS_CACHE_POLICY, policy);
}
}
/**
* Sets jcifs socket timeout.
*
* @param timeout the timeout
*/
public void setJcifsSocketTimeout(final String timeout) {
if (StringUtils.isNotBlank(timeout)) {
logger.debug("jcifsSocketTimeout is set to {}", timeout);
Config.setProperty(JCIFS_PROP_CLIENT_SOTIMEOUT, timeout);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to Apereo under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Apereo licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean id="negociateSpnego" class="org.jasig.cas.support.spnego.web.flow.SpnegoNegociateCredentialsAction"
p:mixedModeAuthentication="${cas.spnego.mixed.mode.authn:false}"
p:ntlm="${cas.spnego.ntlm:false}"
p:supportedBrowser="#{T(java.util.Arrays).asList('${cas.spnego.supportedBrowsers:MSIE,Trident,Firefox,AppleWebKit}')}"/>
<bean id="spnego" class="org.jasig.cas.support.spnego.web.flow.SpnegoCredentialsAction"
p:send401OnAuthenticationFailure="${cas.spnego.send.401.authn.failure:false}"
p:principalFactory-ref="principalFactory"
p:centralAuthenticationService-ref="centralAuthenticationService" />
<bean id="jcifsConfig"
class="org.jasig.cas.support.spnego.authentication.handler.support.JcifsConfig"
p:jcifsServicePrincipal="HTTP/cas.example.com@EXAMPLE.COM"
p:kerberosDebug="${cas.spnego.kerb.debug:false}"
p:kerberosRealm="${cas.spnego.kerb.realm:EXAMPLE.COM}"
p:kerberosKdc="${cas.spnego.kerb.kdc:172.10.1.10}"
p:jcifsDomain="${cas.spnego.jcifs.domain:}"
p:jcifsDomainController="${cas.spnego.jcifs.domaincontroller:}"
p:jcifsNetbiosCachePolicy="${cas.spnego.jcifs.netbios.cache.policy:600}"
p:jcifsNetbiosWins="${cas.spnego.jcifs.netbios.wins:}"
p:jcifsPassword="${cas.spnego.jcifs.password:}"
p:jcifsServicePassword="${cas.spnego.jcifs.service.password:}"
p:jcifsSocketTimeout="${cas.spnego.jcifs.socket.timeout:300000}"
p:jcifsUsername="${cas.spnego.jcifs.username:}"
p:kerberosConf="${cas.spnego.kerb.conf:}"
p:useSubjectCredsOnly="${cas.spnego.use.subject.creds:false}"
p:loginConf="${cas.spnego.login.conf.file:/path/to/login.conf}" />
<bean id="spnegoAuthentication" class="jcifs.spnego.Authentication" />
<bean id="baseSpnegoClientAction"
class="org.jasig.cas.support.spnego.web.flow.client.BaseSpnegoKnownClientSystemsFilterAction"
c:ipsToCheckPattern="${cas.spnego.ip.pattern:127.+}"
c:alternativeRemoteHostAttribute="${cas.spnego.alt.remote.host.attribute:alternateRemoteHeader}" />
<bean id="hostnameSpnegoClientAction"
class="org.jasig.cas.support.spnego.web.flow.client.HostNameSpnegoKnownClientSystemsFilterAction"
c:hostNamePatternString="${cas.spnego.hostname.pattern:something.+}" />
</beans>
......@@ -356,9 +356,37 @@ tgc.signing.key=szxK-5_eJjs-aUj-64MpUZ-GPPzGLhYPLGl0wrYjYNVAGva2P0lLe6UGKGM7k8dW
# memcached.failureMode=Redistribute
# memcached.buffersize=8192
##
# OpenID Support
#
# Enforce relying party id
# cas.openid.enforce.rpid=false
##
# SPNEGO Authentication
#
# cas.spnego.ldap.attribute=spnegoattribute
# cas.spnego.ldap.filter=host={0}
# cas.spnego.ldap.basedn=
# cas.spnego.hostname.pattern=.+
# cas.spnego.ip.pattern=
# cas.spnego.alt.remote.host.attribute
# cas.spengo.use.principal.domain=false
# cas.spnego.ntlm.allowed=true
# cas.spnego.kerb.debug=false
# cas.spnego.kerb.realm=EXAMPLE.COM
# cas.spnego.kerb.kdc=172.10.1.10
# cas.spnego.login.conf.file=/path/to/login
# cas.spnego.jcifs.domain=
# cas.spnego.jcifs.domaincontroller=
# cas.spnego.jcifs.netbios.cache.policy:600
# cas.spnego.jcifs.netbios.wins=
# cas.spnego.jcifs.password=
# cas.spnego.jcifs.service.password=
# cas.spnego.jcifs.socket.timeout:300000
# cas.spnego.jcifs.username=
# cas.spnego.kerb.conf=
# cas.spnego.ntlm=false
# cas.spnego.supportedBrowsers=MSIE,Trident,Firefox,AppleWebKit
# cas.spnego.mixed.mode.authn=false
# cas.spnego.send.401.authn.failure=false
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment