SSO Configuration
Esta página aún no está disponible en tu idioma.
The aim of this configuration is to allow users to log in to OpenKM with their Windows credentials. To do it, it is necessary to configure some things, including client browsers.
In our case the whole system is integrated by the following components:
- Active Directory on Windows Server 2008 and Windows Server 2012
- Tomcat server on a Linux machine
- Client on Windows 7 (browsers: Internet Explorer and Firefox)
The following guides have been used as a starting point:
- Spring Security, Single Sign-On bajo plataformas Windows con Active Directory y Kerberos V5
- LDAP configuration parameters
- http://projects.spring.io/spring-security-kerberos/
- http://docs.spring.io/spring-security-kerberos/docs/1.0.0.RC2/reference/htmlsingle/
Active Directory
Section titled “Active Directory”It is assumed that there is an Active Directory server installed and running (LDAP implementation) where all the user accounts that will be used later are going to be configured.
The steps to follow are:
-
Create a Computer object inside Active Directory that will correspond to the Linux server
- Once done, modify its properties and activate the following option:
- Trusted for delegation: This step is important because, if not enabled, negotiation between clients and servers won’t be possible.
- Once done, modify its properties and activate the following option:
-
The server must authenticate against Active Directory before validating the tickets sent by the client. This authentication is performed using a file (keytab) that maps the service (application server) to a domain user. Create a domain user. This user must be used only and exclusively for this purpose because if this user is used to log in to another computer, it will not be able to be used to log on to the application server. Once the user is created, modify its properties to prevent the password from being changed and to prevent the account from expiring.
-
Generate keytab file. This file is an encoded copy of the private key of a user in Kerberos. It is used by the web application as an authentication proxy for the credentials sent by the user. The following command is executed:
Terminal window C:\> ktpass -princ HTTP/tomcat.example.com@EXAMPLE.COM -mapuser user@EXAMAPLE.COM -crypto all -kvno 0 -ptype KRB5_NT_PRINCIPAL -pass * -out web.keytabIt is important to highlight:
- 1. tomcat.example.com@EXAMPLE.COM: Full name of the Tomcat server and its domain.
2. user@EXAMPLE.COM: The user created previously.
3. web.keytab: The file to which the private key that will be used by the Tomcat server is exported.
- 1. tomcat.example.com@EXAMPLE.COM: Full name of the Tomcat server and its domain.
-
Register new SPN services. A Service Principal Name (SPN) needs to be set up with HTTP and a server name, for example: tomcat.example.org where Tomcat is running. This is used with a domain user, and its keytab is then used as a service credential:
Terminal window C:\> setspn -A HTTP/tomcat.example.org@EXAMPLE.ORG userC:\> setspn -A HTTP/tomcat.example.org user -
Verify the services have been correctly created and linked to the user:
Terminal window C:\> setspn -l user -
Finally create the groups that will be assigned to the users and from which the corresponding roles will be obtained (you should have them from the previous AD integration step). It is recommended to create:
- An Organization Unit (OU) with name OpenKM
- Inside the previous Organization Unit , create the groups that will be used as roles
- ROLE_USER
- ROLE_ADMIN
Linux Machine
Section titled “Linux Machine”The first thing to do is to include the application server in the domain if it was not done previously. To do so, follow the guide:
http://cerowarnings.blogspot.com.es/2011/11/how-to-linux-en-dominio-windows.html
One thing to consider is to add the following configuration to the file /etc/hosts due to previously known problems:
# ldap in /etc/hosts192.168.1.110 example.com192.168.1.110 Schema.Configuration.example.com192.168.1.110 Configuration.example.com192.168.1.110 DomainDnsZones.example.com192.168.1.110 ForestDnsZones.example.comKerberos configuration file
Section titled “Kerberos configuration file”Install the Kerberos client:
$ apt-get krb5-user krb5-configBefore starting the application, Kerberos must be configured properly to validate tickets correctly. In Linux systems, this file is located at “/etc/krb5.conf”.
In this file, the domain, KDC server, and allowed algorithms must be specified. The most important algorithms are arcfour-hmac-md5 and rc4-hmac which Microsoft Active Directory supports.
[libdefaults]default_realm = EXAMPLE.COM#these are mandatory for kerberos.permitted_enctypes = aes128-cts rc4-hmacdefault_tgs_enctypes = aes128-cts rc4-hmacdefault_tkt_enctypes = aes128-cts rc4-hmac[realms]EXAMPLE.COM = { kdc = SERVER.example.com admin_server = SERVER.example.com default_domain = EXAMPLE.COM}[domain_realm]example.com = EXAMPLE.COM.example.com = EXAMPLE.COMConfiguration files
Section titled “Configuration files”To change the authentication type to use, it is necessary to modify the following two files:
- $CATALINA_HOME/webapps/OpenKM/WEB-INF/appContext.xml:
Change the following line:
<security:http access-decision-manager-ref="accessDecisionManager" access-denied-page="/unauthorized.jsp">to
<security:http access-decision-manager-ref="accessDecisionManager" access-denied-page="/unauthorized.jsp" entry-point-ref="spnegoEntryPoint" authentication-manager-ref="authenticationManagerSpnego">Under this line add:
<security:custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />A detail to take into account in this file is that if it is required to filter access not only by authentication but also by roles, you need to replace the attribute IS_AUTHENTICATED_FULLY with an authorized role list separated by commas (ROLE_USER, ROLE_ADMIN)
-
$CATALINA_HOME/openkm.xml
Comment the line:
<security:authentication-manager alias="authenticationManager"> <security:authentication-provider> <security:password-encoder hash="md5"/> <security:jdbc-user-service data-source-ref="dataSource" users-by-username-query="select usr_id, usr_password, 1 from OKM_USER where usr_id=? and usr_active='T'" authorities-by-username-query="select ur_user, ur_role from OKM_USER_ROLE where ur_user=?"/> </security:authentication-provider> </security:authentication-manager>And add the following:
<beans:bean id="spnegoEntryPoint" class="org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint"> <beans:constructor-arg value="/login" /></beans:bean> <beans:bean id="kerberosAuthenticationProvider" class="org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider"> <beans:property name="userDetailsService" ref="ldapUserDetailsService" /> <beans:property name="kerberosClient"> <beans:bean class="org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient"> <beans:property name="debug" value="true"/> </beans:bean> </beans:property></beans:bean> <!--beans:bean id="kerberosServiceAuthenticationProvider" class="org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider"--><beans:bean id="kerberosServiceAuthenticationProvider" class="com.openkm.spring.OKMKerberosServiceAuthenticationProvider"> <beans:property name="ticketValidator"> <beans:bean class="org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator"> <beans:property name="servicePrincipal" value="HTTP/tomcat.example.com@EXAMPLE.COM" /> <beans:property name="keyTabLocation" value="file:/path/to/web.keytab" /> <beans:property name="debug" value="true" /> </beans:bean> </beans:property> <beans:property name="userDetailsService" ref="ldapUserDetailsService" /></beans:bean><!-- Ticket security manager --><security:authentication-manager id="authenticationManagerSpnego"> <security:authentication-provider ref="kerberosAuthenticationProvider" /> <security:authentication-provider ref="kerberosServiceAuthenticationProvider" /></security:authentication-manager><beans:bean id="spnegoAuthenticationProcessingFilter" class="org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter"> <beans:property name="authenticationManager" ref="authenticationManagerSpnego" /></beans:bean> <beans:bean id="authorizationContextSource" class="org.springframework.security.kerberos.client.KerberosLdapContextSource"> <beans:constructor-arg value="ldap://server.example.com:389/dc=example,dc=com" /> <beans:property name="loginConfig" ref="LoginConfig"/></beans:bean><beans:bean id="LoginConfig" class="org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig" init-method="afterPropertiesSet"> <beans:property name="servicePrincipal" value="HTTP/tomcat.example.com@EXAMPLE.COM"/> <beans:property name="keyTabLocation" value="file:/path/to/web.keytab" /> <beans:property name="useTicketCache" value="false" /> <beans:property name="isInitiator" value="true" /> <beans:property name="debug" value="true" /></beans:bean><beans:bean id="ldapUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService"> <beans:constructor-arg ref="FilterBasedLdapUserSearch" /> <beans:constructor-arg ref="defaultLdapAuthoritiesPopulator" /> <beans:property name="UserDetailsMapper" ref="UserDetailsMapper" /></beans:bean><beans:bean id="FilterBasedLdapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> <beans:constructor-arg value="" /> <!--beans:constructor-arg value="dc=example,dc=com" /--> <beans:constructor-arg value="(| (userPrincipalName={0}) (sAMAccountName={0}))" /> <beans:constructor-arg ref="authorizationContextSource" /></beans:bean><beans:bean id="defaultLdapAuthoritiesPopulator" class="com.openkm.spring.OKMLdapAuthoritiesPopulator"/><beans:bean id="UserDetailsMapper" class="org.springframework.security.ldap.userdetails.LdapUserDetailsMapper"/><beans:bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> <beans:constructor-arg> <beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> <beans:constructor-arg ref="authorizationContextSource"/> <beans:property name="userSearch" ref="FilterBasedLdapUserSearch"/> </beans:bean> </beans:constructor-arg> <beans:constructor-arg ref="defaultLdapAuthoritiesPopulator" /></beans:bean><!-- Alternative Ldap security manager --><security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="ldapAuthProvider" /></security:authentication-manager>The important configuration data to take into account is:
- ldap://server.example.com:389/dc=example,dc=com which is the location of the LDAP server.
- servicePrincipal, which is the service previously created on the server: HTTP/tomcat.example.org@EXAMPLE.COM
- keyTabLocation: that is the location in the file system of the Linux machine of the keytab file created on the Active Directory server. (It is possible to use classpath: but it is preferable to use file:)
Configure LDAP in OpenKM
Section titled “Configure LDAP in OpenKM”Once the previous configurations are done, the Tomcat server can be started up. To allow user names and roles to be available inside OpenKM, it is necessary to configure LDAP. To do this, it’s recommended to follow the steps of LDAP configuration parameters.
Client browsers:
Section titled “Client browsers:”To allow automatic credential negotiation, it is also necessary to configure the client web browsers. This is detailed at:
-
Firefox
- Open Firefox.
- At address field, type about:config.
- In filter/search, type negotiate.
- Parameter network.negotiate-auth.trusted-uris may be set to default https:// which doesn’t work for you. Replace this with your server address (tomcat.example.com in our example).
-
Internet Explorer:
- Open Internet Explorer.
- Click Tools > Internet Options > Security tab.
- In Local intranet section, make sure your server is trusted, e.g. by adding it to a list (tomcat.example.com in our example).
- Ensure the “Enable Integrated Windows Authentication” option is enabled
- Add your server address (tomcat.example.com in our example)
-
Chrome:
- Open Chrome.
- Click Configuration> Show Advanced Configuration > Change proxy configuration > Security tab.
- In Local intranet section, make sure your server is trusted, e.g. by adding it to a list (tomcat.example.com in our example).
- Ensure the “Enable Integrated Windows Authentication” option is enabled.
- Add your server address (tomcat.example.com in our example)
Troubleshooting
Section titled “Troubleshooting”Enable log for SSO authentication
Section titled “Enable log for SSO authentication”Modify the log configuration file and add the line:
<logger name="org.springframework.security" level="DEBUG" />Check Kerberos authentication from Linux command line
Section titled “Check Kerberos authentication from Linux command line”Execute the command (it’s case-sensitive and uppercase is important):
$ kinit user@EXAMPLE.ORGIf all is correct, you will be prompted for the password and the login from Linux will succeed. If it goes wrong, there is something wrong in your Kerberos Linux client configuration (krb5.conf).
Listing keys
Section titled “Listing keys ”Almost all problems we have found are caused by a badly created web.keytab file. If you get errors like:
rg.springframework.ldap.AuthenticationException: GSSAPI; nested exception is javax.naming.AuthenticationException: GSSAPI [Root exception is javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))]] at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:182) at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:285) at org.springframework.ldap.core.support.AbstractContextSource.doGetContext(AbstractContextSource.java:119) at org.springframework.ldap.core.support.AbstractContextSource.getReadOnlyContext(AbstractContextSource.java:138) at org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:791) at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:194) at org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:116) at org.springframework.security.ldap.userdetails.LdapUserDetailsService.loadUserByUsername(LdapUserDetailsService.java:38) at com.openkm.spring.OKMKerberosServiceAuthenticationProvider.authenticate(OKMKerberosServiceAuthenticationProvider.java:64) at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156) at org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745)Caused by: javax.naming.AuthenticationException: GSSAPI [Root exception is javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))]] at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(LdapSasl.java:168) at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:235) at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2740) at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:316) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:193) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:211) at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:154) at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:84) at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684) at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307) at javax.naming.InitialContext.init(InitialContext.java:242) at javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:153) at org.springframework.ldap.core.support.LdapContextSource.getDirContextInstance(LdapContextSource.java:43) at org.springframework.security.kerberos.client.KerberosLdapContextSource.access$001(KerberosLdapContextSource.java:66) at org.springframework.security.kerberos.client.KerberosLdapContextSource$1.run(KerberosLdapContextSource.java:110) at org.springframework.security.kerberos.client.KerberosLdapContextSource$1.run(KerberosLdapContextSource.java:105) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:356) at org.springframework.security.kerberos.client.KerberosLdapContextSource.getDirContextInstance(KerberosLdapContextSource.java:105) at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:273) ... 39 moreCaused by: javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7))] at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:212) at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(LdapSasl.java:123) ... 58 moreCaused by: GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7)) at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:710) at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:248) at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:179) at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:193) ... 59 moreCaused by: KrbException: Server not found in Kerberos database (7) at sun.security.krb5.KrbTgsRep.<init>(KrbTgsRep.java:70) at sun.security.krb5.KrbTgsReq.getReply(KrbTgsReq.java:192) at sun.security.krb5.KrbTgsReq.sendAndGetCreds(KrbTgsReq.java:203) at sun.security.krb5.internal.CredentialsUtil.serviceCreds(CredentialsUtil.java:309) at sun.security.krb5.internal.CredentialsUtil.acquireServiceCreds(CredentialsUtil.java:115) at sun.security.krb5.Credentials.acquireServiceCreds(Credentials.java:456) at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:641) ... 62 moreCaused by: KrbException: Identifier doesn't match expected value (906) at sun.security.krb5.internal.KDCRep.init(KDCRep.java:143) at sun.security.krb5.internal.TGSRep.init(TGSRep.java:66) at sun.security.krb5.internal.TGSRep.<init>(TGSRep.java:61) at sun.security.krb5.KrbTgsRep.<init>(KrbTgsRep.java:55) ... 68 moreProbably your web.keytab is wrong. Ensure you have executed the “ktpass” command before executing the “setspn” commands.