spring-security : Using user's certificate to authenticate against LDAP - ldap

I managed to authentify the user against the Ldap using the username found in the certificate. What I would like to obtain is to authentify the user using directly the certificate on the Ldap.
I cannot found how to pass the certificate to the Ldap.
here is the current config (using the certificate's username) :
<security:x509 subject-principal-regex="CN=(.*?)," user-service-ref="userService"/>
<bean name="userService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
<constructor-arg ref="ldapUserSearch"/>
<constructor-arg ref="ldapAuthoritiesPopulator"/>
</bean>
<bean name="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg value=""/>
<constructor-arg value="sAMAccountName={0}"/>
<constructor-arg ref="contextSource" />
</bean>
<bean name="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource" />
<constructor-arg value="" />
<property name="groupSearchFilter" value="member={0}" />
<property name="searchSubtree" value="true" />
</bean>

I was looking in to this issue myself. I have yet to find an authentication stack that does X509->account resolution "right". I got hung up on the fact that Spring Security's UserDetailsService interface insists on a string uid for lookup, but in many cases it is impossible to derive such a UID from the information contained in an X509 certificate's subject (e.g. there are many cn=John Smith in the world, or even within a single organization, nor is email required in a certificate DN). The uniqueness of a certificate lies in the Issuer + Serial Number combination, not the Subject.
After looking through the API there are a couple ways to go about this. Either way probably precludes using the namespace and setting up the filter chain and beans yourself:
1) Implement your own AuthenticationUserDetailsService and bind this to the PreAuthenticatedAuthenticationProvider. By default, I believe, the namespace sets up a UserDetailsByNameServiceWrapper using the passed-in user-service-ref. Going this route means you have to do everything to set up the UserDetails, including granted authorities resolution. Of course you can delegate all this, but its more work.
2) If your LDAP store is keyed by some UID, and this is the route I am leaning towards, implement your own X509PrincipalExtractor and bind it to the X509AuthenticationFilter and return the string uid that your LDAPUserDetailsService is configured to expect. Within the extractor implement the logic to search your LDAP store for the stored certificate. I do not know of any strategies that will work across LDAP servers, the easiest way would be if your LDAP supports RFC4523 certificateMatch or certificateExactMatch and you can configure a search filter that will return you a unique account from which you can then return the attribute you need (e.g. sAMAccountName). If not, if your certificates contain a value that you can filter on (e.g. certificate cn = LDAP cn) that you can use to retrieve a candidate set of LDAP results for, extract their certificates to X509Certificate and do .equals() against the passed in certificate to find the account that matches and return its uid.

Set up the LDAP server to use SSL with client authentication.

Finally, I've implemented the following sollution in my NON-web application :
<bean id="x509ContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://hostname:389/DC=base,DC=com" />
<property name="authenticationStrategy">
<bean class="org.springframework.ldap.core.support.ExternalTlsDirContextAuthenticationStrategy">
<property name="sslSocketFactory">
<bean class="yourOwnSocketFactory"/>
</property>
<property name="shutdownTlsGracefully" value="true" />
</bean>
</property>
</bean>
where yourOwnSocketFactory takes the user's certificate to establish the TLS connection.
A successfull TLS connection means the user is authenticated. That's the case with a well configured LDAP, which should check the user including certificate revokation list.
Once the connection established, you have to recover the user's informations with a custom BindAuthenticator which could extract (X509PrincipalExtractor) Certificate DN (or other usefull info) to match the LDAP user.

Related

Make the user enter the credentials for specific pages

In my Grails 3.3.10 application I'm using Spring security plugin , for a specific pages that got critical information I need the user to enter the credentials again although this user logged in before while entering to the application, Any ideas to achieve this.
You can configure this in your application.groovy. For example
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
[pattern: '/', access: ['permitAll']],
[pattern: '/sensitivepage/**', access: ['ROLE_USER', 'isFullyAuthenticated()']]
]
Anything under /sensitivepage/ will require re-authentication.
It's explained in the Spring Security Documentation.
You can try to create a filter chain for endpoints that do need to log in again and register a custom filter. In that filter, you can revoke the existing token. Make these endpoints secure so that if a request comes without token then it would redirect the user to the login page.
Here is a sample code for your reference: (Please note that I haven't tried this scenario, it just a thought that I'm sharing here.)
<bean id="reauthenticateFilterChain" class="org.springframework.security.web.DefaultSecurityFilterChain">
<constructor-arg index="0" ref="reauthenticateResourceRequestMatcher"/>
<constructor-arg index="1">
<list>
<ref bean="reauthenticateFilter"/>
</list>
</constructor-arg>
</bean>
This is how you can register your custom filter chain.
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<ref bean="reauthenticateFilterChain"/>
</list>
</constructor-arg>
</bean>

LDAP authentication with Apache Nifi not working

I have been working on this problem for quite some time and I would like answers and suggestions from you guys on the issue I am facing. I am trying to get my Nifi standalone instance on my VM, which is in my company's network, authenticated using the ldap-provider in login-identity-providers.xml. I input all the required values except for the truststores and keystores because the company has an LDAP and not LDAPS . So, I figured I wouldn't need those certificates. I have set the initial admin identity in the authorizers.xml too. And, of course, set the https port and host(0.0.0.0). But, when I try to run and call the server from the browser using the url, the Site can't be reached , ERR_CONNECTION_REFUSED pops up. Basically, can't reach the server. I have checked the DN, LDAP url and other properties to be correct and working when I queried through ldapsearch.
So, is it because I don't specify the truststore and keystore in the configuration? If so, do i need to manually create these certs for each client that wish to access Nifi. I thought the LDAP certificate would be enough for a person to authenticate to Nifi. Please advise me on how to go about getting the CA for truststore, server and client certificates for the employees to use Nifi through LDAP.
My ldap-provider looks like this(scrubbed) :
<provider>
<identifier>ldap-provider</identifier>
<class>org.apache.nifi.ldap.LdapProvider</class>
<property name="Authentication Strategy">SIMPLE</property>
<property name="Manager DN">cn=user-name,ou=Accounts,dc=domain,dc=company-name,dc=com</property>
<property name="Manager Password">My-account-password</property>
<property name="TLS - Keystore"></property>
<property name="TLS - Keystore Password"></property>
<property name="TLS - Keystore Type"></property>
<property name="TLS - Truststore"></property>
<property name="TLS - Truststore Password"></property>
<property name="TLS - Truststore Type"></property>
<property name="TLS - Client Auth"></property>
<property name="TLS - Protocol">TLS</property>
<property name="TLS - Shutdown Gracefully"></property>
<property name="Referral Strategy">IGNORE</property>
<property name="Connect Timeout">10 secs</property>
<property name="Read Timeout">10 secs</property>
<property name="Url">the-ldap-url-of-company</property>
<property name="User Search Base">cn=Users,ou=Accounts,dc=corp,dc=company-name,dc=com</property>
<property name="User Search Filter">sAMAccountName={0}</property>
<property name="Authentication Expiration">12 hours</property>
</provider>
Nifi version - 1.0.0
Running in a Ubuntu Trusty VM.
When using LDAP authentication you should reach a login page for NiFi where you supply the username and password to authenticate against LDAP. Since you are not even reaching that page, something else is wrong before it is even getting to the LDAP part.
Some things to check...
In NiFi properties the active identity provider should be set with the following property:
nifi.security.user.login.identity.provider=ldap-provider
When you are using any kind of authentication NiFi must be configured to use https which requires providing the following configuration:
nifi.web.https.host=
nifi.web.https.port=
nifi.security.keystore=
nifi.security.keystoreType=
nifi.security.keystorePasswd=
nifi.security.keyPasswd=
nifi.security.truststore=
nifi.security.truststoreType=
nifi.security.trustStorePassword=
The hostname that you set in nifi.web.https.host should also line up with the hostname of the certificate being used for the nifi.security.keystore. I have a feeling that setting the https host to 0.0.0.0 is not what you want, but I'm not totally sure what that does.
Once you get the https configuration correct, then you should reach the login page when going to https://yourhost:post/nifi and after that is where your LDAP configuration will come into play.

allow specific role in cas

I have a cas server that authenticate and send back some attributes corerectly. Now i want to add a service that check user roles in principal attributes and allow access to a service only if logged in user has the Specific role ( like admin role!).
I read about 'requiredHandlers' and thought it can help, but i can not make it work!
For service, i have something like this:
<bean class="org.jasig.cas.services.RegexRegisteredService">
<property name="id" value="1"/>
<property name="name" value="Admin panel service"/>
<property name="serviceId" value="http://localhost:8080/admin"/>
<property name="evaluationOrder" value="0"/>
<property name="ignoreAttributes" value="true"/>
<property name="requiredHandlers" value="supporterAuthenticationHandler"> <!-- this is what i found so far -->
</property>
</bean>
where supporterAuthenticationHandler is defined in authenticationManager
<bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
<constructor-arg>
<map>
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver"/>
<entry key-ref="primaryAuthenticationHandler"><null /></entry>
<entry key-ref="supporterAuthenticationHandler"><null /></entry>
</map>
</constructor-arg>
<property name="authenticationPolicy">
<bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy"/>
</property>
</bean>
And supporterAuthenticationHandler is a simple implemention of AuthenticationHandler (nothing implemented in it yet.
problem is i can not make cas to check supporterAuthenticationHandler so that i can go further (and probably fall into another hole where i need the new Principal with attributes).
Am i going completely the wrong way? Shall i check user role in my admin application? is it even possible to check roles with cas with different services?
This goes to the point that CAS is an authentication service not an authorization service. CAS only ensures that users are who they say they are not what they can do in an application (service).
please see this answer
https://security.stackexchange.com/questions/7623/can-central-authentication-service-cas-do-authorization

Separate Keystore for Spring-WS SSL handshake and Message Encryption

I have a question in regards to
SSLHandshakeException talking to a https Web service using Spring WebServiceTemplate
answerd by borodark
"No need to import keys into keystore."
If we dont provide a keystore then what will Httpclient use for sending the Client certificate for SSL handshake ?
I have a requirement to invoke web services on a bussiness partner -
a) Authenticate using SSL using a public key certificate X
b) Encrypt and Sign SOAP messages using public key certificate Y
I guess I will need to specify certificate Y to the following :
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Signature"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference"/>
<property name="securementUsername" value="mycert"/>
<property name="securementPassword" value="certpass"/>
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
I am not sure how/where to specify certificate X for SSL handshake. I think its the HttpClient but I dont see it in the XML posted by borodark.
Please help !
in the xml file where you have configured the keystore you should have something like this:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
the cue here is
<property name="location" value="classpath:keystore.jks"/>
that is the path to the keystore.. now, possibly you can use a certain alias inside the keystore for ssl handhshake (and that's what you configure here), additionally the security policy leverages on the same file, but then again in the securitypolicy file you can specify a different alias.. and that should do the trick..
Consider that while
<property name="location" value="classpath:keystore.jks"/>
indicates classpath you can use other form to reference resources outside the war itself, and that lets you change the certificate without touching the war at all..

Jasig CAS LDAP - How to get roles

I have jasig 3.4.7 installed with OpenLDAP and ssl enabled. I can login through CAS to an account created in the LDAP : fine.
Now I need to declare services via the management interface : I realized I've no "admin" account. So I tried to add a group "ADMIN" to my user. My first question : is it the right way ?
I've contextSource declared this way :
<bean id="contextSource" class="org.jasig.cas.adaptors.ldap.util.AuthenticatedLdapContextSource">
What do I need in order CAS to retrieve my user admin group. I tried :
<bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource"/>
<constructor-arg value="ou=Groups"/>
<property name="groupRoleAttribute" value="cn"/>
<property name="groupSearchFilter" value="(uniqueMember={0})" />
</bean>
But DefaultLdapAuthoritiesPopulator is incompatible with jasig AuthenticatedLdapContextSource.
What bean should I use instead. I've seen some people dont use jasig context source but default spring org.springframework.ldap.core.support.LdapContextSource instead. Is it correct ? Would it help ?
Thanks,