You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2017/06/05 20:00:50 UTC
[11/17] ambari git commit: AMBARI-20938. LDAPS connections to an
Active Directory when enabling Kerberos should validate the server's SSL
certificate (rlevas)
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 <rl...@hortonworks.com>
Authored: Fri Jun 2 15:43:43 2017 -0400
Committer: Robert Levas <rl...@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