AMBARI-20938. LDAPS connections to an Active Directory when enabling Kerberos should validate the server's SSL certificate (rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/c3c06ea9 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/c3c06ea9 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/c3c06ea9 Branch: refs/heads/branch-feature-AMBARI-12556 Commit: c3c06ea9889e0a462d67e8a765b41d0a4aead9b2 Parents: 9acb7a6 Author: Robert Levas <rle...@hortonworks.com> Authored: Fri Jun 2 15:43:43 2017 -0400 Committer: Robert Levas <rle...@hortonworks.com> Committed: Fri Jun 2 15:43:43 2017 -0400 ---------------------------------------------------------------------- ambari-server/conf/unix/ambari.properties | 4 + ambari-server/docs/configuration/index.md | 5 +- .../server/configuration/Configuration.java | 20 +++- .../server/controller/KerberosHelperImpl.java | 6 + .../security/InternalSSLSocketFactory.java | 112 +++++++++++++++++++ .../InternalSSLSocketFactoryNonTrusting.java | 49 ++++++++ .../InternalSSLSocketFactoryTrusting.java | 48 ++++++++ .../kerberos/ADKerberosOperationHandler.java | 34 ++++-- .../KerberosKDCSSLConnectionException.java | 45 ++++++++ .../kerberos/TrustingSSLSocketFactory.java | 101 ----------------- .../ADKerberosOperationHandlerTest.java | 96 +++++++++++++++- 11 files changed, 403 insertions(+), 117 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/conf/unix/ambari.properties ---------------------------------------------------------------------- diff --git a/ambari-server/conf/unix/ambari.properties b/ambari-server/conf/unix/ambari.properties index b8b645d..212591f 100644 --- a/ambari-server/conf/unix/ambari.properties +++ b/ambari-server/conf/unix/ambari.properties @@ -80,6 +80,10 @@ server.execution.scheduler.misfire.toleration.minutes=480 # Kerberos settings kerberos.keytab.cache.dir = $ROOT/var/lib/ambari-server/data/cache +# Validate trust when communicating with the KDC (via SSL) when performing Kerberos-related +# operations, if applicable +kerberos.operation.verify.kdc.trust = true + # Default timeout in seconds before task is killed agent.task.timeout=900 # Default timeout in seconds before package installation task is killed http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/docs/configuration/index.md ---------------------------------------------------------------------- diff --git a/ambari-server/docs/configuration/index.md b/ambari-server/docs/configuration/index.md index ff9ce54..2394264 100644 --- a/ambari-server/docs/configuration/index.md +++ b/ambari-server/docs/configuration/index.md @@ -148,8 +148,9 @@ The following are the properties which can be used to configure Ambari. | kdcserver.connection.check.timeout | The timeout, in milliseconds, to wait when communicating with a Kerberos Key Distribution Center. |`10000` | | kerberos.check.jaas.configuration | Determines whether Kerberos-enabled Ambari deployments should use JAAS to validate login credentials. |`false` | | kerberos.keytab.cache.dir | The location on the Ambari Server where Kerberos keytabs are cached. |`/var/lib/ambari-server/data/cache` | -| kerberos.operation.retries | The number of times failed kerberos operations should be retried to execute. |`3` | -| kerberos.operation.retry.timeout | The time to wait (in seconds) between failed kerberos operations retries. |`10` | +| kerberos.operation.retries | The number of times failed Kerberos operations should be retried to execute. |`3` | +| kerberos.operation.retry.timeout | The time to wait (in seconds) between failed Kerberos operations retries. |`10` | +| kerberos.operation.verify.kdc.trust | Validate the trust of the SSL certificate provided by the KDC when performing Kerberos operations over SSL. |`true` | | ldap.sync.username.collision.behavior | Determines how to handle username collision while updating from LDAP.<br/><br/>The following are examples of valid values:<ul><li>`skip`<li>`convert`</ul> |`convert` | | log4j.monitor.delay | Indicates the delay, in milliseconds, for the log4j monitor to check for changes |`300000` | | logsearch.metadata.cache.expire.timeout | The time, in hours, that the Ambari Server will hold Log File metadata in its internal cache before making a request to the LogSearch Portal to get the latest metadata. |`24` | http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java index d3b8cc5..965b57b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java @@ -1451,13 +1451,25 @@ public class Configuration { * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ - @Markdown(description = "The number of times failed kerberos operations should be retried to execute.") + @Markdown(description = "The number of times failed Kerberos operations should be retried to execute.") public static final ConfigurationProperty<Integer> KERBEROS_OPERATION_RETRIES = new ConfigurationProperty<>( "kerberos.operation.retries", 3); - @Markdown(description = "The time to wait (in seconds) between failed kerberos operations retries.") + @Markdown(description = "The time to wait (in seconds) between failed Kerberos operations retries.") public static final ConfigurationProperty<Integer> KERBEROS_OPERATION_RETRY_TIMEOUT = new ConfigurationProperty<>( "kerberos.operation.retry.timeout", 10); + + /** + * A flag indicating whether to validate the trust of an SSL certificate provided by a KDC when + * performing Kerberos operations. + * + * For example, when communicating with an Active Directory using + * LDAPS. The default behavior is to validate the trust. + */ + @Markdown(description = "Validate the trust of the SSL certificate provided by the KDC when performing Kerberos operations over SSL.") + public static final ConfigurationProperty<Boolean> KERBEROS_OPERATION_VERIFY_KDC_TRUST = new ConfigurationProperty<>( + "kerberos.operation.verify.kdc.trust", Boolean.TRUE); + /** * The type of connection pool to use with JDBC connections to the database. */ @@ -6080,6 +6092,10 @@ public class Configuration { return Integer.valueOf(getProperty(KERBEROS_OPERATION_RETRY_TIMEOUT)); } + public boolean validateKerberosOperationSSLCertTrust() { + return Boolean.parseBoolean(getProperty(KERBEROS_OPERATION_VERIFY_KDC_TRUST)); + } + /** * Return configured acceptors for agent api connector. Default = null */ http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java index 10df7a2..7b6ac7e 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java @@ -75,6 +75,7 @@ import org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileWr import org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileWriterFactory; import org.apache.ambari.server.serveraction.kerberos.KerberosInvalidConfigurationException; import org.apache.ambari.server.serveraction.kerberos.KerberosKDCConnectionException; +import org.apache.ambari.server.serveraction.kerberos.KerberosKDCSSLConnectionException; import org.apache.ambari.server.serveraction.kerberos.KerberosLDAPContainerException; import org.apache.ambari.server.serveraction.kerberos.KerberosMissingAdminCredentialsException; import org.apache.ambari.server.serveraction.kerberos.KerberosOperationException; @@ -1579,6 +1580,11 @@ public class KerberosHelperImpl implements KerberosHelper { "Failed to connect to KDC - " + e.getMessage() + "\n" + "Update the KDC settings in krb5-conf and kerberos-env configurations to correct this issue.", e); + } catch (KerberosKDCSSLConnectionException e) { + throw new KerberosInvalidConfigurationException( + "Failed to connect to KDC - " + e.getMessage() + "\n" + + "Make sure the server's SSL certificate or CA certificates have been imported into Ambari's truststore.", + e); } catch (KerberosRealmException e) { throw new KerberosInvalidConfigurationException( "Failed to find a KDC for the specified realm - " + e.getMessage() + "\n" + http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactory.java new file mode 100644 index 0000000..9ecf5d1 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactory.java @@ -0,0 +1,112 @@ +/* + * 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.ambari.server.security; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +/** + * InternalSSLSocketFactory is an abstract class implementing a SSLSocketFactory. + * <p> + * Implementers of this class need to specific the SSLContext protocol and whether the server's SSL + * certificates should be trusted or validated. + */ +public class InternalSSLSocketFactory extends SSLSocketFactory { + private SSLSocketFactory socketFactory; + + InternalSSLSocketFactory(String protocol, boolean trusting) { + try { + SSLContext ctx = SSLContext.getInstance(protocol); + + // If trusting, install our own TrustManager to accept certificates without validating the + // trust chain; else, use the default TrustManager, which validates the certificate's trust chain. + TrustManager[] trustManagers = (trusting) + ? new TrustManager[]{new LenientTrustManager()} + : null; + + ctx.init(null, trustManagers, new SecureRandom()); + socketFactory = ctx.getSocketFactory(); + } catch (Exception ex) { + ex.printStackTrace(System.err); /* handle exception */ + } + } + + @Override + public String[] getDefaultCipherSuites() { + return socketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return socketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { + return socketFactory.createSocket(socket, string, i, bln); + } + + @Override + public Socket createSocket(String string, int i) throws IOException { + return socketFactory.createSocket(string, i); + } + + @Override + public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException { + return socketFactory.createSocket(string, i, ia, i1); + } + + @Override + public Socket createSocket(InetAddress ia, int i) throws IOException { + return socketFactory.createSocket(ia, i); + } + + @Override + public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { + return socketFactory.createSocket(ia, i, ia1, i1); + } + + + /** + * LenientTrustManager is a TrustManager that accepts all certificates without validating the + * chain of trust. + */ + public static class LenientTrustManager implements X509TrustManager { + public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { + // do nothing + } + + public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { + // do nothing + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryNonTrusting.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryNonTrusting.java b/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryNonTrusting.java new file mode 100644 index 0000000..84eada8 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryNonTrusting.java @@ -0,0 +1,49 @@ +/* + * 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.ambari.server.security; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +/** + * InternalSSLSocketFactoryNonTrusting is a non-trusting {@link SSLSocketFactory} implementation + * using the TLSv1.2 protocol. + * <p> + * This SSL socket factory should allow for at least the following SSL protocols: + * <ul> + * <li>TLSv1.2</li> + * <li>TLSv1.1</li> + * <li>TLSv1</li> + * </ul> + * <p> + * However, the actual set of enabled SSL protocols is dependent on the underlying JVM. + * <p> + * This SSL socket factory creates non-trusting connections, meaning that the server's SSL certificate + * will be validated using the JVM-specific internal {@link javax.net.ssl.TrustManager} + */ +public class InternalSSLSocketFactoryNonTrusting extends InternalSSLSocketFactory { + + public InternalSSLSocketFactoryNonTrusting() { + super("TLSv1.2", false); + } + + public static SocketFactory getDefault() { + return new InternalSSLSocketFactoryNonTrusting(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryTrusting.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryTrusting.java b/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryTrusting.java new file mode 100644 index 0000000..4bb3604 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/InternalSSLSocketFactoryTrusting.java @@ -0,0 +1,48 @@ +/* + * 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.ambari.server.security; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +/** + * InternalSSLSocketFactoryTrusting is a trusting {@link SSLSocketFactory} implementation + * using the TLSv1.2 protocol. + * <p> + * This SSL socket factory should allow for at least the following SSL protocols: + * <ul> + * <li>TLSv1.2</li> + * <li>TLSv1.1</li> + * <li>TLSv1</li> + * </ul> + * <p> + * However, the actual set of enabled SSL protocols is dependent on the underlying JVM. + * <p> + * This SSL socket factory creates trusting connections, meaning that the server's SSL certificate + * will not be tested to see if it is trusted. + */ +public class InternalSSLSocketFactoryTrusting extends InternalSSLSocketFactory { + public InternalSSLSocketFactoryTrusting() { + super("TLSv1.2", true); + } + + public static SocketFactory getDefault() { + return new InternalSSLSocketFactoryTrusting(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java index b2dbd5e..8903fa1 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandler.java @@ -46,7 +46,11 @@ import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapContext; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import javax.net.ssl.SSLHandshakeException; +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.InternalSSLSocketFactoryNonTrusting; +import org.apache.ambari.server.security.InternalSSLSocketFactoryTrusting; import org.apache.ambari.server.security.credential.PrincipalKeyCredential; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.StringUtils; @@ -60,6 +64,7 @@ import org.apache.velocity.exception.ResourceNotFoundException; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; +import com.google.inject.Inject; /** * Implementation of <code>KerberosOperationHandler</code> to created principal in Active Directory @@ -107,7 +112,11 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler { * The Gson instance to use to convert the template-generated JSON structure to a Map of attribute * names to values. */ - private Gson gson = new Gson(); + @Inject + private Gson gson; + + @Inject + private Configuration configuration; /** * Prepares and creates resources to be used by this KerberosOperationHandler @@ -175,8 +184,6 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler { this.createTemplate = kerberosConfiguration.get(KERBEROS_ENV_AD_CREATE_ATTRIBUTES_TEMPLATE); - this.gson = new Gson(); - setOpen(true); } @@ -189,8 +196,6 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler { public void close() throws KerberosOperationException { this.searchControls = null; - this.gson = null; - if (this.ldapContext != null) { try { this.ldapContext.close(); @@ -447,14 +452,29 @@ public class ADKerberosOperationHandler extends KerberosOperationHandler { properties.put(Context.SECURITY_CREDENTIALS, String.valueOf(administratorCredential.getKey())); properties.put(Context.SECURITY_AUTHENTICATION, "simple"); properties.put(Context.REFERRAL, "follow"); - properties.put("java.naming.ldap.factory.socket", TrustingSSLSocketFactory.class.getName()); + + if (ldapUrl.startsWith("ldaps")) { + if (configuration.validateKerberosOperationSSLCertTrust()) { + properties.put("java.naming.ldap.factory.socket", InternalSSLSocketFactoryNonTrusting.class.getName()); + } else { + properties.put("java.naming.ldap.factory.socket", InternalSSLSocketFactoryTrusting.class.getName()); + } + } try { return createInitialLdapContext(properties, null); } catch (CommunicationException e) { + Throwable rootCause = e.getRootCause(); + String message = String.format("Failed to communicate with the Active Directory at %s: %s", ldapUrl, e.getMessage()); LOG.warn(message, e); - throw new KerberosKDCConnectionException(message, e); + + if(rootCause instanceof SSLHandshakeException) { + throw new KerberosKDCSSLConnectionException(message, e); + } + else { + throw new KerberosKDCConnectionException(message, e); + } } catch (AuthenticationException e) { String message = String.format("Failed to authenticate with the Active Directory at %s: %s", ldapUrl, e.getMessage()); LOG.warn(message, e); http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosKDCSSLConnectionException.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosKDCSSLConnectionException.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosKDCSSLConnectionException.java new file mode 100644 index 0000000..1e588c5 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosKDCSSLConnectionException.java @@ -0,0 +1,45 @@ +/* + * 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.ambari.server.serveraction.kerberos; + +/** + * KerberosKDCSSLConnectionException is a KerberosOperationException thrown in the event a connection + * to the KDC was not able to be made due to an SSL issue. + */ +public class KerberosKDCSSLConnectionException extends KerberosOperationException { + + /** + * Creates a new KerberosKDCSSLConnectionException with a message + * + * @param message a String containing the message indicating the reason for this exception + */ + public KerberosKDCSSLConnectionException(String message) { + super(message); + } + + /** + * Creates a new KerberosKDCSSLConnectionException with a message and a cause + * + * @param message a String containing the message indicating the reason for this exception + * @param cause a Throwable declaring the previously thrown Throwable that led to this exception + */ + public KerberosKDCSSLConnectionException(String message, Throwable cause) { + super(message, cause); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/TrustingSSLSocketFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/TrustingSSLSocketFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/TrustingSSLSocketFactory.java deleted file mode 100644 index 52b3703..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/TrustingSSLSocketFactory.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.ambari.server.serveraction.kerberos; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.SecureRandom; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -public class TrustingSSLSocketFactory extends SSLSocketFactory { - private SSLSocketFactory socketFactory; - - public TrustingSSLSocketFactory() { - try { - SSLContext ctx = SSLContext.getInstance("TLS"); - ctx.init(null, new TrustManager[]{new LenientTrustManager()}, new SecureRandom()); - socketFactory = ctx.getSocketFactory(); - } catch (Exception ex) { - ex.printStackTrace(System.err); /* handle exception */ - } - } - - public static SocketFactory getDefault() { - return new TrustingSSLSocketFactory(); - } - - @Override - public String[] getDefaultCipherSuites() { - return socketFactory.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return socketFactory.getSupportedCipherSuites(); - } - - @Override - public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { - return socketFactory.createSocket(socket, string, i, bln); - } - - @Override - public Socket createSocket(String string, int i) throws IOException, UnknownHostException { - return socketFactory.createSocket(string, i); - } - - @Override - public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { - return socketFactory.createSocket(string, i, ia, i1); - } - - @Override - public Socket createSocket(InetAddress ia, int i) throws IOException { - return socketFactory.createSocket(ia, i); - } - - @Override - public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { - return socketFactory.createSocket(ia, i, ia1, i1); - } - - - public static class LenientTrustManager implements X509TrustManager { - public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { - // do nothing - } - - public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { - // do nothing - } - - public X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[0]; - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/c3c06ea9/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java index 603f744..49f8d56 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ADKerberosOperationHandlerTest.java @@ -41,13 +41,22 @@ import javax.naming.directory.SearchResult; import javax.naming.ldap.Control; import javax.naming.ldap.LdapContext; +import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.security.InternalSSLSocketFactoryNonTrusting; +import org.apache.ambari.server.security.InternalSSLSocketFactoryTrusting; import org.apache.ambari.server.security.credential.PrincipalKeyCredential; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.stack.OsFamily; import org.easymock.Capture; import org.easymock.CaptureType; import org.easymock.IAnswer; import org.junit.Ignore; import org.junit.Test; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + import junit.framework.Assert; public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest { @@ -112,6 +121,8 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest @Test(expected = KerberosAdminAuthenticationException.class) public void testTestAdministratorCredentialsIncorrectAdminPassword() throws Exception { + Injector injector = getInjector(); + PrincipalKeyCredential kc = new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, "wrong"); Map<String, String> kerberosEnvMap = new HashMap<String, String>() { { @@ -123,6 +134,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class) .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class)) .createNiceMock(); + injector.injectMembers(handler); expect(handler.createInitialLdapContext(anyObject(Properties.class), anyObject(Control[].class))).andAnswer(new IAnswer<LdapContext>() { @Override @@ -140,6 +152,8 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest @Test(expected = KerberosAdminAuthenticationException.class) public void testTestAdministratorCredentialsIncorrectAdminPrincipal() throws Exception { + Injector injector = getInjector(); + PrincipalKeyCredential kc = new PrincipalKeyCredential("wrong", DEFAULT_ADMIN_PASSWORD); Map<String, String> kerberosEnvMap = new HashMap<String, String>() { { @@ -151,6 +165,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class) .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class)) .createNiceMock(); + injector.injectMembers(handler); expect(handler.createInitialLdapContext(anyObject(Properties.class), anyObject(Control[].class))).andAnswer(new IAnswer<LdapContext>() { @Override @@ -197,6 +212,8 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest @Test public void testTestAdministratorCredentialsSuccess() throws Exception { + Injector injector = getInjector(); + PrincipalKeyCredential kc = new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD); Map<String, String> kerberosEnvMap = new HashMap<String, String>() { { @@ -209,6 +226,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class)) .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createSearchControls")) .createNiceMock(); + injector.injectMembers(handler); expect(handler.createInitialLdapContext(anyObject(Properties.class), anyObject(Control[].class))) .andAnswer(new IAnswer<LdapContext>() { @@ -250,6 +268,8 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest @Test public void testProcessCreateTemplateDefault() throws Exception { + Injector injector = getInjector(); + PrincipalKeyCredential kc = new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD); Map<String, String> kerberosEnvMap = new HashMap<String, String>() { { @@ -265,6 +285,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class)) .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createSearchControls")) .createNiceMock(); + injector.injectMembers(handler); @SuppressWarnings("unchecked") NamingEnumeration<SearchResult> searchResult = createNiceMock(NamingEnumeration.class); @@ -360,6 +381,8 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest @Test public void testProcessCreateTemplateCustom() throws Exception { + Injector injector = getInjector(); + PrincipalKeyCredential kc = new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD); Map<String, String> kerberosEnvMap = new HashMap<String, String>() { { @@ -394,6 +417,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class)) .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createSearchControls")) .createNiceMock(); + injector.injectMembers(handler); @SuppressWarnings("unchecked") NamingEnumeration<SearchResult> searchResult = createNiceMock(NamingEnumeration.class); @@ -464,16 +488,18 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest @Test public void testDigests() throws Exception { + Injector injector = getInjector(); + PrincipalKeyCredential kc = new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD); Map<String, String> kerberosEnvMap = new HashMap<>(); kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL); kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN); kerberosEnvMap.put(ADKerberosOperationHandler.KERBEROS_ENV_AD_CREATE_ATTRIBUTES_TEMPLATE, "" + - "{" + - "\"principal_digest\": \"$principal_digest\"," + - "\"principal_digest_256\": \"$principal_digest_256\"," + - "\"principal_digest_512\": \"$principal_digest_512\"" + - "}" + "{" + + "\"principal_digest\": \"$principal_digest\"," + + "\"principal_digest_256\": \"$principal_digest_256\"," + + "\"principal_digest_512\": \"$principal_digest_512\"" + + "}" ); Capture<Attributes> capturedAttributes = newCapture(); @@ -482,6 +508,7 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class)) .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createSearchControls")) .createNiceMock(); + injector.injectMembers(handler); @SuppressWarnings("unchecked") NamingEnumeration<SearchResult> searchResult = createNiceMock(NamingEnumeration.class); @@ -628,4 +655,63 @@ public class ADKerberosOperationHandlerTest extends KerberosOperationHandlerTest handler.close(); } + + @Test + public void testCreateLdapContextSSLSocketFactoryTrusting() throws Exception { + testCreateLdapContextSSLSocketFactory(true); + } + + @Test + public void testCreateLdapContextSSLSocketFactoryNonTrusting() throws Exception { + testCreateLdapContextSSLSocketFactory(false); + } + + private void testCreateLdapContextSSLSocketFactory(boolean trusting) throws Exception { + Injector injector = getInjector(); + + Configuration configuration = injector.getInstance(Configuration.class); + expect(configuration.validateKerberosOperationSSLCertTrust()).andReturn(!trusting).once(); + + LdapContext initialContext = createNiceMock(LdapContext.class); + + Capture<? extends Properties> capturedProperties = newCapture(CaptureType.FIRST); + + ADKerberosOperationHandler handler = createMockBuilder(ADKerberosOperationHandler.class) + .addMockedMethod(ADKerberosOperationHandler.class.getDeclaredMethod("createInitialLdapContext", Properties.class, Control[].class)) + .createNiceMock(); + injector.injectMembers(handler); + + expect(handler.createInitialLdapContext(capture(capturedProperties), anyObject(Control[].class))) + .andReturn(initialContext) + .once(); + + replayAll(); + + Map<String, String> kerberosConfiguration = new HashMap<String, String>(); + kerberosConfiguration.put(ADKerberosOperationHandler.KERBEROS_ENV_LDAP_URL, DEFAULT_LDAP_URL); + kerberosConfiguration.put(ADKerberosOperationHandler.KERBEROS_ENV_PRINCIPAL_CONTAINER_DN, DEFAULT_PRINCIPAL_CONTAINER_DN); + + handler.open(new PrincipalKeyCredential("principal", "key"), "EXAMPLE.COM", kerberosConfiguration); + + Properties properties = capturedProperties.getValue(); + Assert.assertNotNull(properties); + + String socketFactoryClassName = properties.getProperty("java.naming.ldap.factory.socket"); + if (trusting) { + Assert.assertEquals(InternalSSLSocketFactoryTrusting.class.getName(), socketFactoryClassName); + } else { + Assert.assertEquals(InternalSSLSocketFactoryNonTrusting.class.getName(), socketFactoryClassName); + } + } + + private Injector getInjector() { + return Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(Clusters.class).toInstance(createNiceMock(Clusters.class)); + bind(Configuration.class).toInstance(createNiceMock(Configuration.class)); + bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class)); + } + }); + } } \ No newline at end of file