NIFI-2325:

- Adding support for LDAPS.

This closes #1275.

Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
Matt Gilman 2016-11-28 16:38:33 -05:00 committed by Andy LoPresto
parent 316cae16d3
commit c5ef076786
No known key found for this signature in database
GPG Key ID: 3C6EF65B2F7DEF69
5 changed files with 200 additions and 64 deletions

View File

@ -307,17 +307,17 @@ nifi.security.user.login.identity.provider=ldap-provider
[options="header,footer"] [options="header,footer"]
|================================================================================================================================================== |==================================================================================================================================================
| Property Name | Description | Property Name | Description
|`Authentication Strategy` | How the connection to the LDAP server is authenticated. Possible values are ANONYMOUS, SIMPLE, or START_TLS. |`Authentication Strategy` | How the connection to the LDAP server is authenticated. Possible values are ANONYMOUS, SIMPLE, LDAPS, or START_TLS.
|`Manager DN` | The DN of the manager that is used to bind to the LDAP server to search for users. |`Manager DN` | The DN of the manager that is used to bind to the LDAP server to search for users.
|`Manager Password` | The password of the manager that is used to bind to the LDAP server to search for users. |`Manager Password` | The password of the manager that is used to bind to the LDAP server to search for users.
|`TLS - Keystore` | Path to the Keystore that is used when connecting to LDAP using START_TLS. |`TLS - Keystore` | Path to the Keystore that is used when connecting to LDAP using LDAPS or START_TLS.
|`TLS - Keystore Password` | Password for the Keystore that is used when connecting to LDAP using START_TLS. |`TLS - Keystore Password` | Password for the Keystore that is used when connecting to LDAP using LDAPS or START_TLS.
|`TLS - Keystore Type` | Type of the Keystore that is used when connecting to LDAP using START_TLS (i.e. JKS or PKCS12). |`TLS - Keystore Type` | Type of the Keystore that is used when connecting to LDAP using LDAPS or START_TLS (i.e. JKS or PKCS12).
|`TLS - Truststore` | Path to the Truststore that is used when connecting to LDAP using START_TLS. |`TLS - Truststore` | Path to the Truststore that is used when connecting to LDAP using LDAPS or START_TLS.
|`TLS - Truststore Password` | Password for the Truststore that is used when connecting to LDAP using START_TLS. |`TLS - Truststore Password` | Password for the Truststore that is used when connecting to LDAP using LDAPS or START_TLS.
|`TLS - Truststore Type` | Type of the Truststore that is used when connecting to LDAP using START_TLS (i.e. JKS or PKCS12). |`TLS - Truststore Type` | Type of the Truststore that is used when connecting to LDAP using LDAPS or START_TLS (i.e. JKS or PKCS12).
|`TLS - Client Auth` | Client authentication policy when connecting to LDAP using START_TLS. Possible values are REQUIRED, WANT, NONE. |`TLS - Client Auth` | Client authentication policy when connecting to LDAP using LDAPS or START_TLS. Possible values are REQUIRED, WANT, NONE.
|`TLS - Protocol` | Protocol to use when connecting to LDAP using START_TLS. (i.e. TLS, TLSv1.1, TLSv1.2, etc). |`TLS - Protocol` | Protocol to use when connecting to LDAP using LDAPS or START_TLS. (i.e. TLS, TLSv1.1, TLSv1.2, etc).
|`TLS - Shutdown Gracefully` | Specifies whether the TLS should be shut down gracefully before the target context is closed. Defaults to false. |`TLS - Shutdown Gracefully` | Specifies whether the TLS should be shut down gracefully before the target context is closed. Defaults to false.
|`Referral Strategy` | Strategy for handling referrals. Possible values are FOLLOW, IGNORE, THROW. |`Referral Strategy` | Strategy for handling referrals. Possible values are FOLLOW, IGNORE, THROW.
|`Connect Timeout` | Duration of connect timeout. (i.e. 10 secs). |`Connect Timeout` | Duration of connect timeout. (i.e. 10 secs).

View File

@ -23,25 +23,25 @@
Identity Provider for users logging in with username/password against an LDAP server. Identity Provider for users logging in with username/password against an LDAP server.
'Authentication Strategy' - How the connection to the LDAP server is authenticated. Possible 'Authentication Strategy' - How the connection to the LDAP server is authenticated. Possible
values are ANONYMOUS, SIMPLE, or START_TLS. values are ANONYMOUS, SIMPLE, LDAPS, or START_TLS.
'Manager DN' - The DN of the manager that is used to bind to the LDAP server to search for users. 'Manager DN' - The DN of the manager that is used to bind to the LDAP server to search for users.
'Manager Password' - The password of the manager that is used to bind to the LDAP server to 'Manager Password' - The password of the manager that is used to bind to the LDAP server to
search for users. search for users.
'TLS - Keystore' - Path to the Keystore that is used when connecting to LDAP using START_TLS. 'TLS - Keystore' - Path to the Keystore that is used when connecting to LDAP using LDAPS or START_TLS.
'TLS - Keystore Password' - Password for the Keystore that is used when connecting to LDAP 'TLS - Keystore Password' - Password for the Keystore that is used when connecting to LDAP
using START_TLS. using LDAPS or START_TLS.
'TLS - Keystore Type' - Type of the Keystore that is used when connecting to LDAP using 'TLS - Keystore Type' - Type of the Keystore that is used when connecting to LDAP using
START_TLS (i.e. JKS or PKCS12). LDAPS or START_TLS (i.e. JKS or PKCS12).
'TLS - Truststore' - Path to the Truststore that is used when connecting to LDAP using START_TLS. 'TLS - Truststore' - Path to the Truststore that is used when connecting to LDAP using LDAPS or START_TLS.
'TLS - Truststore Password' - Password for the Truststore that is used when connecting to 'TLS - Truststore Password' - Password for the Truststore that is used when connecting to
LDAP using START_TLS. LDAP using LDAPS or START_TLS.
'TLS - Truststore Type' - Type of the Truststore that is used when connecting to LDAP using 'TLS - Truststore Type' - Type of the Truststore that is used when connecting to LDAP using
START_TLS (i.e. JKS or PKCS12). LDAPS or START_TLS (i.e. JKS or PKCS12).
'TLS - Client Auth' - Client authentication policy when connecting to LDAP using START_TLS. 'TLS - Client Auth' - Client authentication policy when connecting to LDAP using LDAPS or START_TLS.
Possible values are REQUIRED, WANT, NONE. Possible values are REQUIRED, WANT, NONE.
'TLS - Protocol' - Protocol to use when connecting to LDAP using START_TLS. (i.e. TLS, 'TLS - Protocol' - Protocol to use when connecting to LDAP using LDAPS or START_TLS. (i.e. TLS,
TLSv1.1, TLSv1.2, etc). TLSv1.1, TLSv1.2, etc).
'TLS - Shutdown Gracefully' - Specifies whether the TLS should be shut down gracefully 'TLS - Shutdown Gracefully' - Specifies whether the TLS should be shut down gracefully
before the target context is closed. Defaults to false. before the target context is closed. Defaults to false.

View File

@ -23,5 +23,6 @@ public enum LdapAuthenticationStrategy {
ANONYMOUS, ANONYMOUS,
SIMPLE, SIMPLE,
LDAPS,
START_TLS START_TLS
} }

View File

@ -47,6 +47,7 @@ import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.search.LdapUserSearch; import org.springframework.security.ldap.search.LdapUserSearch;
import org.springframework.security.ldap.userdetails.LdapUserDetails; import org.springframework.security.ldap.userdetails.LdapUserDetails;
import javax.naming.Context;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import java.io.IOException; import java.io.IOException;
import java.security.KeyManagementException; import java.security.KeyManagementException;
@ -96,11 +97,6 @@ public class LdapProvider implements LoginIdentityProvider {
setTimeout(configurationContext, baseEnvironment, "Connect Timeout", "com.sun.jndi.ldap.connect.timeout"); setTimeout(configurationContext, baseEnvironment, "Connect Timeout", "com.sun.jndi.ldap.connect.timeout");
setTimeout(configurationContext, baseEnvironment, "Read Timeout", "com.sun.jndi.ldap.read.timeout"); setTimeout(configurationContext, baseEnvironment, "Read Timeout", "com.sun.jndi.ldap.read.timeout");
// set the base environment is necessary
if (!baseEnvironment.isEmpty()) {
context.setBaseEnvironmentProperties(baseEnvironment);
}
// authentication strategy // authentication strategy
final String rawAuthenticationStrategy = configurationContext.getProperty("Authentication Strategy"); final String rawAuthenticationStrategy = configurationContext.getProperty("Authentication Strategy");
final LdapAuthenticationStrategy authenticationStrategy; final LdapAuthenticationStrategy authenticationStrategy;
@ -126,6 +122,20 @@ public class LdapProvider implements LoginIdentityProvider {
case SIMPLE: case SIMPLE:
context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy()); context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy());
break; break;
case LDAPS:
context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy());
// indicate a secure connection
baseEnvironment.put(Context.SECURITY_PROTOCOL, "ssl");
// get the configured ssl context
final SSLContext ldapsSslContext = getConfiguredSslContext(configurationContext);
if (ldapsSslContext != null) {
// initialize the ldaps socket factory prior to use
LdapsSocketFactory.initialize(ldapsSslContext.getSocketFactory());
baseEnvironment.put("java.naming.ldap.factory.socket", LdapsSocketFactory.class.getName());
}
break;
case START_TLS: case START_TLS:
final AbstractTlsDirContextAuthenticationStrategy tlsAuthenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy(); final AbstractTlsDirContextAuthenticationStrategy tlsAuthenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy();
@ -136,49 +146,13 @@ public class LdapProvider implements LoginIdentityProvider {
tlsAuthenticationStrategy.setShutdownTlsGracefully(shutdownGracefully); tlsAuthenticationStrategy.setShutdownTlsGracefully(shutdownGracefully);
} }
final String rawKeystore = configurationContext.getProperty("TLS - Keystore"); // get the configured ssl context
final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password"); final SSLContext startTlsSslContext = getConfiguredSslContext(configurationContext);
final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type"); if (startTlsSslContext != null) {
final String rawTruststore = configurationContext.getProperty("TLS - Truststore"); tlsAuthenticationStrategy.setSslSocketFactory(startTlsSslContext.getSocketFactory());
final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password");
final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type");
final String rawClientAuth = configurationContext.getProperty("TLS - Client Auth");
final String rawProtocol = configurationContext.getProperty("TLS - Protocol");
final ClientAuth clientAuth;
if (StringUtils.isBlank(rawClientAuth)) {
clientAuth = ClientAuth.NONE;
} else {
try {
clientAuth = ClientAuth.valueOf(rawClientAuth);
} catch (final IllegalArgumentException iae) {
throw new ProviderCreationException(String.format("Unrecognized client auth '%s'. Possible values are [%s]",
rawClientAuth, StringUtils.join(ClientAuth.values(), ", ")));
}
}
// ensure the protocol is specified
if (StringUtils.isBlank(rawProtocol)) {
throw new ProviderCreationException("TLS - Protocol must be specified.");
}
try {
final SSLContext sslContext;
if (StringUtils.isBlank(rawKeystore)) {
sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, rawProtocol);
} else {
if (StringUtils.isBlank(rawTruststore)) {
sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, rawProtocol);
} else {
sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, rawProtocol);
}
}
tlsAuthenticationStrategy.setSslSocketFactory(sslContext.getSocketFactory());
} catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException | IOException e) {
throw new ProviderCreationException(e.getMessage(), e);
} }
// set the authentication strategy
context.setAuthenticationStrategy(tlsAuthenticationStrategy); context.setAuthenticationStrategy(tlsAuthenticationStrategy);
break; break;
} }
@ -241,6 +215,11 @@ public class LdapProvider implements LoginIdentityProvider {
} }
} }
// set the base environment is necessary
if (!baseEnvironment.isEmpty()) {
context.setBaseEnvironmentProperties(baseEnvironment);
}
try { try {
// handling initializing beans // handling initializing beans
context.afterPropertiesSet(); context.afterPropertiesSet();
@ -269,6 +248,56 @@ public class LdapProvider implements LoginIdentityProvider {
} }
} }
private SSLContext getConfiguredSslContext(final LoginIdentityProviderConfigurationContext configurationContext) {
final String rawKeystore = configurationContext.getProperty("TLS - Keystore");
final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password");
final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type");
final String rawTruststore = configurationContext.getProperty("TLS - Truststore");
final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password");
final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type");
final String rawClientAuth = configurationContext.getProperty("TLS - Client Auth");
final String rawProtocol = configurationContext.getProperty("TLS - Protocol");
// create the ssl context
final SSLContext sslContext;
try {
if (StringUtils.isBlank(rawKeystore) && StringUtils.isBlank(rawTruststore)) {
sslContext = null;
} else {
// ensure the protocol is specified
if (StringUtils.isBlank(rawProtocol)) {
throw new ProviderCreationException("TLS - Protocol must be specified.");
}
if (StringUtils.isBlank(rawKeystore)) {
sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, rawProtocol);
} else if (StringUtils.isBlank(rawTruststore)) {
sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, rawProtocol);
} else {
// determine the client auth if specified
final ClientAuth clientAuth;
if (StringUtils.isBlank(rawClientAuth)) {
clientAuth = ClientAuth.NONE;
} else {
try {
clientAuth = ClientAuth.valueOf(rawClientAuth);
} catch (final IllegalArgumentException iae) {
throw new ProviderCreationException(String.format("Unrecognized client auth '%s'. Possible values are [%s]",
rawClientAuth, StringUtils.join(ClientAuth.values(), ", ")));
}
}
sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType,
rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, rawProtocol);
}
}
} catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException | IOException e) {
throw new ProviderCreationException(e.getMessage(), e);
}
return sslContext;
}
@Override @Override
public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException { public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException {
if (provider == null) { if (provider == null) {

View File

@ -0,0 +1,106 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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
*
* 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.
*/
package org.apache.nifi.ldap;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* SSLSocketFactory used when connecting to a Directory Server over LDAPS.
*/
public class LdapsSocketFactory extends SSLSocketFactory {
// singleton
private static LdapsSocketFactory instance;
// delegate
private SSLSocketFactory delegate;
/**
* Initializes the LdapsSocketFactory with the specified SSLSocketFactory. The specified
* socket factory will be used as a delegate for all subsequent instances of this class.
*
* @param sslSocketFactory delegate socket factory
*/
public static void initialize(final SSLSocketFactory sslSocketFactory) {
instance = new LdapsSocketFactory(sslSocketFactory);
}
/**
* Gets the LdapsSocketFactory that was previously initialized.
*
* @return socket factory
*/
public static SocketFactory getDefault() {
return instance;
}
/**
* Creates a new LdapsSocketFactory.
*
* @param sslSocketFactory delegate socket factory
*/
private LdapsSocketFactory(final SSLSocketFactory sslSocketFactory) {
delegate = sslSocketFactory;
}
// delegate methods
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException {
return delegate.createSocket(socket, string, i, bln);
}
@Override
public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException {
return delegate.createSocket(ia, i, ia1, i1);
}
@Override
public Socket createSocket(InetAddress ia, int i) throws IOException {
return delegate.createSocket(ia, i);
}
@Override
public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException {
return delegate.createSocket(string, i, ia, i1);
}
@Override
public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
return delegate.createSocket(string, i);
}
@Override
public Socket createSocket() throws IOException {
return delegate.createSocket();
}
}