You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2018/05/29 12:46:50 UTC

[ambari] branch trunk updated: [AMBARI-23920] Ambari 2way SSL does not work if CA signed certs are used

This is an automated email from the ASF dual-hosted git repository.

rlevas pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 36e41b9  [AMBARI-23920] Ambari 2way SSL does not work if CA signed certs are used
36e41b9 is described below

commit 36e41b962c08173227bdc6e140228cb9eef72981
Author: Robert Levas <rl...@hortonworks.com>
AuthorDate: Sat May 26 22:43:04 2018 -0400

    [AMBARI-23920] Ambari 2way SSL does not work if CA signed certs are used
---
 .../ambari/server/configuration/Configuration.java |  8 ++++
 .../ambari/server/security/CertificateManager.java | 44 +++++++++++++++-------
 .../unsecured/rest/CertificateDownload.java        |  4 +-
 .../server/security/CertificateManagerTest.java    | 38 +++++++++++++++++++
 4 files changed, 79 insertions(+), 15 deletions(-)

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 91eafe5..d9ffe24 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
@@ -544,6 +544,13 @@ public class Configuration {
       "security.server.cert_name", "ca.crt");
 
   /**
+   * The name of the file that contains the CA certificate chain for certificate validation during 2-way SSL communication.
+   */
+  @Markdown(description = "The name of the file located in the `security.server.keys_dir` directory containing the CA certificate chain used to verify certificates during 2-way SSL communications.")
+  public static final ConfigurationProperty<String> SRVR_CRT_CHAIN_NAME = new ConfigurationProperty<>(
+      "security.server.cert_chain_name", "ca_chain.pem");
+
+  /**
    * The name of the certificate request file used when generating certificates.
    */
   @Markdown(description = "The name of the certificate request file used when generating certificates.")
@@ -2726,6 +2733,7 @@ public class Configuration {
     configsMap.put(SRVR_ONE_WAY_SSL_PORT.getKey(), getProperty(SRVR_ONE_WAY_SSL_PORT));
     configsMap.put(SRVR_KSTR_DIR.getKey(), getProperty(SRVR_KSTR_DIR));
     configsMap.put(SRVR_CRT_NAME.getKey(), getProperty(SRVR_CRT_NAME));
+    configsMap.put(SRVR_CRT_CHAIN_NAME.getKey(), getProperty(SRVR_CRT_CHAIN_NAME));
     configsMap.put(SRVR_KEY_NAME.getKey(), getProperty(SRVR_KEY_NAME));
     configsMap.put(SRVR_CSR_NAME.getKey(), getProperty(SRVR_CSR_NAME));
     configsMap.put(KSTR_NAME.getKey(), getProperty(KSTR_NAME));
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/CertificateManager.java b/ambari-server/src/main/java/org/apache/ambari/server/security/CertificateManager.java
index 532c749..2ac54be 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/CertificateManager.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/CertificateManager.java
@@ -22,6 +22,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.charset.Charset;
+import java.nio.file.Files;
 import java.text.MessageFormat;
 import java.util.Map;
 
@@ -145,7 +146,7 @@ public class CertificateManager {
     Map<String, String> configsMap = configs.getConfigsMap();
     String srvrKstrDir = configsMap.get(Configuration.SRVR_KSTR_DIR.getKey());
     String srvrCrtName = configsMap.get(Configuration.SRVR_CRT_NAME.getKey());
-    String srvrCsrName = configsMap.get(Configuration.SRVR_CSR_NAME.getKey());;
+    String srvrCsrName = configsMap.get(Configuration.SRVR_CSR_NAME.getKey());
     String srvrKeyName = configsMap.get(Configuration.SRVR_KEY_NAME.getKey());
     String kstrName = configsMap.get(Configuration.KSTR_NAME.getKey());
     String srvrCrtPass = configsMap.get(Configuration.SRVR_CRT_PASS.getKey());
@@ -174,20 +175,37 @@ public class CertificateManager {
   }
 
   /**
-   * Returns server certificate content
-   * @return string with server certificate content
+   * Returns server's PEM-encoded CA chain file content
+   * @return string server's PEM-encoded CA chain file content
    */
-  public String getServerCert() {
-    Map<String, String> configsMap = configs.getConfigsMap();
-    File certFile = new File(configsMap.get(Configuration.SRVR_KSTR_DIR.getKey()) +
-        File.separator + configsMap.get(Configuration.SRVR_CRT_NAME.getKey()));
-    String srvrCrtContent = null;
-    try {
-      srvrCrtContent = FileUtils.readFileToString(certFile);
-    } catch (IOException e) {
-      LOG.error(e.getMessage());
+  public String getCACertificateChainContent() {
+    String serverCertDir = configs.getProperty(Configuration.SRVR_KSTR_DIR);
+
+    // Attempt to send the explicit CA certificate chain file.
+    String serverCertChainName = configs.getProperty(Configuration.SRVR_CRT_CHAIN_NAME);
+    File certChainFile = new File(serverCertDir, serverCertChainName);
+    if(certChainFile.exists()) {
+      try {
+        return new String(Files.readAllBytes(certChainFile.toPath()));
+      } catch (IOException e) {
+        LOG.error(e.getMessage());
+      }
     }
-    return srvrCrtContent;
+
+    // Fall back to the original way things were done and send the server's SSL certificate as the
+    // Certificate chain file.
+    String serverCertName = configs.getProperty(Configuration.SRVR_CRT_NAME);
+    File certFile = new File(serverCertDir, serverCertName);
+    if(certFile.canRead()) {
+      try {
+        return new String(Files.readAllBytes(certFile.toPath()));
+      } catch (IOException e) {
+        LOG.error(e.getMessage());
+      }
+    }
+
+    // If all else fails, send nothing...
+    return null;
   }
 
   /**
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/unsecured/rest/CertificateDownload.java b/ambari-server/src/main/java/org/apache/ambari/server/security/unsecured/rest/CertificateDownload.java
index 881e614..43a9106 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/unsecured/rest/CertificateDownload.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/unsecured/rest/CertificateDownload.java
@@ -41,8 +41,8 @@ public class CertificateDownload {
 
   @GET @ApiIgnore // until documented
   @Produces({MediaType.TEXT_PLAIN})
-  public String downloadSrvrCrt() {
-    return certMan.getServerCert();
+  public String downloadCACertificateChainFile() {
+    return certMan.getCACertificateChainContent();
   }
 
 }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/CertificateManagerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/CertificateManagerTest.java
index dceeab2..47de7b5 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/CertificateManagerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/CertificateManagerTest.java
@@ -21,7 +21,9 @@ package org.apache.ambari.server.security;
 import static org.easymock.EasyMock.expect;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.reflect.Method;
+import java.nio.file.Files;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -163,6 +165,42 @@ public class CertificateManagerTest extends EasyMockSupport {
     Assert.assertEquals("Incorrect passphrase from the agent", response.getMessage());
   }
 
+  @Test
+  public void testGetCACertificateChain() throws IOException {
+    Injector injector = getInjector();
+
+    File directory = folder.newFolder();
+    String caCertFileName = "myca.crt";
+    String caCertChainFileName = "myca_chain.pem";
+
+    Configuration configuration = injector.getInstance(Configuration.class);
+    expect(configuration.getProperty(Configuration.SRVR_KSTR_DIR)).andReturn(directory.getAbsolutePath()).anyTimes();
+    expect(configuration.getProperty(Configuration.SRVR_CRT_NAME)).andReturn(caCertFileName).anyTimes();
+    expect(configuration.getProperty(Configuration.SRVR_CRT_CHAIN_NAME)).andReturn(caCertChainFileName).anyTimes();
+
+    final File caCertFile = new File(directory, caCertFileName);
+    final File caCertChainFile = new File(directory, caCertChainFileName);
+
+    CertificateManager certificateManager = new CertificateManager();
+    injector.injectMembers(certificateManager);
+
+    replayAll();
+
+    String content;
+
+    // Only the CA certificate file is available, this is the fallback option
+    Files.write(caCertFile.toPath(), Collections.singleton(caCertFile.getAbsolutePath()));
+    content = certificateManager.getCACertificateChainContent();
+    Assert.assertEquals(caCertFile.getAbsolutePath(), content.trim());
+
+    // The CA certificate chain file is available, this is the preferred option
+    Files.write(caCertChainFile.toPath(), Collections.singleton(caCertChainFile.getAbsolutePath()));
+    content = certificateManager.getCACertificateChainContent();
+    Assert.assertEquals(caCertChainFile.getAbsolutePath(), content.trim());
+
+    verifyAll();
+  }
+
   private Injector getInjector() {
     return Guice.createInjector(new AbstractModule() {
 

-- 
To stop receiving notification emails like this one, please contact
rlevas@apache.org.