You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by lm...@apache.org on 2016/08/23 13:00:40 UTC

knox git commit: KNOX-742 - Export Commands in KnoxCLI for the gateway-identity Public Cert

Repository: knox
Updated Branches:
  refs/heads/master b37e4293d -> e5ef45aae


KNOX-742 - Export Commands in KnoxCLI for the gateway-identity Public Cert

Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/e5ef45aa
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/e5ef45aa
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/e5ef45aa

Branch: refs/heads/master
Commit: e5ef45aaecd5dc7372403302fb8be21e485f3a45
Parents: b37e429
Author: Larry McCay <lm...@hortonworks.com>
Authored: Tue Aug 23 09:00:22 2016 -0400
Committer: Larry McCay <lm...@hortonworks.com>
Committed: Tue Aug 23 09:00:22 2016 -0400

----------------------------------------------------------------------
 .../org/apache/hadoop/gateway/util/KnoxCLI.java | 98 +++++++++++++++++++-
 .../apache/hadoop/gateway/util/KnoxCLITest.java | 47 ++++++++++
 .../security/impl/X509CertificateUtil.java      | 36 +++++++
 3 files changed, 180 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/e5ef45aa/gateway-server/src/main/java/org/apache/hadoop/gateway/util/KnoxCLI.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/util/KnoxCLI.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/util/KnoxCLI.java
index c865bfb..acb2034 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/util/KnoxCLI.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/util/KnoxCLI.java
@@ -20,12 +20,15 @@ package org.apache.hadoop.gateway.util;
 import java.io.BufferedReader;
 import java.io.Console;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -52,6 +55,7 @@ import org.apache.hadoop.gateway.services.security.AliasService;
 import org.apache.hadoop.gateway.services.security.KeystoreService;
 import org.apache.hadoop.gateway.services.security.KeystoreServiceException;
 import org.apache.hadoop.gateway.services.security.MasterService;
+import org.apache.hadoop.gateway.services.security.impl.X509CertificateUtil;
 import org.apache.hadoop.gateway.services.topology.TopologyService;
 import org.apache.hadoop.gateway.topology.Provider;
 import org.apache.hadoop.gateway.topology.Topology;
@@ -89,6 +93,7 @@ public class KnoxCLI extends Configured implements Tool {
       "   [" + VersionCommand.USAGE + "]\n" +
       "   [" + MasterCreateCommand.USAGE + "]\n" +
       "   [" + CertCreateCommand.USAGE + "]\n" +
+      "   [" + CertExportCommand.USAGE + "]\n" +
       "   [" + AliasCreateCommand.USAGE + "]\n" +
       "   [" + AliasDeleteCommand.USAGE + "]\n" +
       "   [" + AliasListCommand.USAGE + "]\n" +
@@ -121,6 +126,7 @@ public class KnoxCLI extends Configured implements Tool {
 
   // For testing only
   private String master = null;
+  private String type = null;
 
   /* (non-Javadoc)
    * @see org.apache.hadoop.util.Tool#run(java.lang.String[])
@@ -226,6 +232,12 @@ public class KnoxCLI extends Configured implements Tool {
           printKnoxShellUsage();
           return -1;
         }
+      } else if (args[i].equals("export-cert")) {
+        command = new CertExportCommand();
+        if ((args.length > i + 1) && args[i + 1].equals("--help")) {
+          printKnoxShellUsage();
+          return -1;
+        }
       }else if(args[i].equals("user-auth-test")) {
         if(i + 1 >= args.length) {
           printKnoxShellUsage();
@@ -240,7 +252,6 @@ public class KnoxCLI extends Configured implements Tool {
         } else {
           command = new LDAPSysBindCommand();
         }
-
       } else if (args[i].equals("list-alias")) {
         command = new AliasListCommand();
       } else if (args[i].equals("--value")) {
@@ -284,6 +295,12 @@ public class KnoxCLI extends Configured implements Tool {
         } else {
           this.generate = "true";
         }
+      } else if(args[i].equals("--type")) {
+        if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+          printKnoxShellUsage();
+          return -1;
+        }
+        this.type = args[++i];
       } else if(args[i].equals("--path")) {
         if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
           printKnoxShellUsage();
@@ -360,6 +377,9 @@ public class KnoxCLI extends Configured implements Tool {
       out.println( CertCreateCommand.USAGE + "\n\n" + CertCreateCommand.DESC );
       out.println();
       out.println( div );
+      out.println( CertExportCommand.USAGE + "\n\n" + CertExportCommand.DESC );
+      out.println();
+      out.println( div );
       out.println( AliasCreateCommand.USAGE + "\n\n" + AliasCreateCommand.DESC );
       out.println();
       out.println( div );
@@ -464,6 +484,82 @@ public class KnoxCLI extends Configured implements Tool {
    }
  }
 
+ public class CertExportCommand extends Command {
+
+   public static final String USAGE = "export-cert";
+   public static final String DESC = "The export-cert command exports the public certificate\n" +
+                                     "from the a gateway.jks keystore with the alias of gateway-identity.";
+   private static final String GATEWAY_CREDENTIAL_STORE_NAME = "__gateway";
+   private static final String GATEWAY_IDENTITY_PASSPHRASE = "gateway-identity-passphrase";
+
+    public CertExportCommand() {
+    }
+
+    private GatewayConfig getGatewayConfig() {
+      GatewayConfig result;
+      Configuration conf = getConf();
+      if( conf != null && conf instanceof GatewayConfig ) {
+        result = (GatewayConfig)conf;
+      } else {
+        result = new GatewayConfigImpl();
+      }
+      return result;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.hadoop.gateway.util.KnoxCLI.Command#execute()
+     */
+    @Override
+    public void execute() throws Exception {
+      KeystoreService ks = getKeystoreService();
+
+      AliasService as = getAliasService();
+
+      if (ks != null) {
+        try {
+          if (!ks.isKeystoreForGatewayAvailable()) {
+            out.println("No keystore has been created for the gateway. Please use the create-cert command or populate with a CA signed cert of your own.");
+          }
+          char[] passphrase = as.getPasswordFromAliasForCluster(GATEWAY_CREDENTIAL_STORE_NAME, GATEWAY_IDENTITY_PASSPHRASE);
+          if (passphrase == null) {
+            MasterService ms = services.getService("MasterService");
+            passphrase = ms.getMasterSecret();
+          }
+          Certificate cert = ks.getKeystoreForGateway().getCertificate("gateway-identity");
+          String keyStoreDir = getGatewayConfig().getGatewaySecurityDir() + File.separator + "keystores" + File.separator;
+          File ksd = new File(keyStoreDir);
+          if (!ksd.exists()) {
+            if( !ksd.mkdirs() ) {
+              // certainly should not happen if the keystore is known to be available
+              throw new ServiceLifecycleException("Unable to create keystores directory" + ksd.getAbsolutePath());
+            }
+          }
+          if ("PEM".equals(type) || type == null) {
+            X509CertificateUtil.writeCertificateToFile(cert, new File(keyStoreDir + "gateway-identity.pem"));
+            out.println("Certificate gateway-identity has been successfully exported to: " + keyStoreDir + "gateway-identity.pem");
+          }
+          else if ("JKS".equals(type)) {
+            X509CertificateUtil.writeCertificateToJKS(cert, new File(keyStoreDir + "gateway-client-trust.jks"));
+            out.println("Certificate gateway-identity has been successfully exported to: " + keyStoreDir + "gateway-client-trust.jks");
+          }
+          else {
+            out.println("Invalid type for export file provided. Export has not been done. Please use: [PEM|JKS] default value is PEM.");
+          }
+        } catch (KeystoreServiceException e) {
+          throw new ServiceLifecycleException("Keystore was not loaded properly - the provided (or persisted) master secret may not match the password for the keystore.", e);
+        }
+      }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.hadoop.gateway.util.KnoxCLI.Command#getUsage()
+     */
+    @Override
+    public String getUsage() {
+      return USAGE + ":\n\n" + DESC;
+    }
+  }
+
  public class CertCreateCommand extends Command {
 
   public static final String USAGE = "create-cert [--hostname h]";

http://git-wip-us.apache.org/repos/asf/knox/blob/e5ef45aa/gateway-server/src/test/java/org/apache/hadoop/gateway/util/KnoxCLITest.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/java/org/apache/hadoop/gateway/util/KnoxCLITest.java b/gateway-server/src/test/java/org/apache/hadoop/gateway/util/KnoxCLITest.java
index 4e8c08e..8eed6ab 100644
--- a/gateway-server/src/test/java/org/apache/hadoop/gateway/util/KnoxCLITest.java
+++ b/gateway-server/src/test/java/org/apache/hadoop/gateway/util/KnoxCLITest.java
@@ -312,6 +312,53 @@ public class KnoxCLITest {
   }
 
   @Test
+  public void testExportCert() throws Exception {
+    GatewayConfigImpl config = new GatewayConfigImpl();
+    FileUtils.deleteQuietly( new File( config.getGatewaySecurityDir() ) );
+    createTestMaster();
+    outContent.reset();
+    KnoxCLI cli = new KnoxCLI();
+    cli.setConf( config );
+    String[] gwCreateArgs = {"create-cert", "--hostname", "hostname1", "--master", "master"};
+    int rc = 0;
+    rc = cli.run(gwCreateArgs);
+    assertEquals(0, rc);
+    assertTrue(outContent.toString(), outContent.toString().contains("gateway-identity has been successfully " +
+        "created."));
+
+    outContent.reset();
+    String[] gwCreateArgs2 = {"export-cert", "--type", "PEM"};
+    rc = 0;
+    rc = cli.run(gwCreateArgs2);
+    assertEquals(0, rc);
+    assertTrue(outContent.toString(), outContent.toString().contains("Certificate gateway-identity has been successfully exported to"));
+    assertTrue(outContent.toString(), outContent.toString().contains("gateway-identity.pem"));
+
+    outContent.reset();
+    String[] gwCreateArgs2_5 = {"export-cert"};
+    rc = 0;
+    rc = cli.run(gwCreateArgs2_5);
+    assertEquals(0, rc);
+    assertTrue(outContent.toString(), outContent.toString().contains("Certificate gateway-identity has been successfully exported to"));
+    assertTrue(outContent.toString(), outContent.toString().contains("gateway-identity.pem"));
+
+    outContent.reset();
+    String[] gwCreateArgs3 = {"export-cert", "--type", "JKS"};
+    rc = 0;
+    rc = cli.run(gwCreateArgs3);
+    assertEquals(0, rc);
+    assertTrue(outContent.toString(), outContent.toString().contains("Certificate gateway-identity has been successfully exported to"));
+    assertTrue(outContent.toString(), outContent.toString().contains("gateway-client-trust.jks"));
+
+    outContent.reset();
+    String[] gwCreateArgs4 = {"export-cert", "--type", "invalid"};
+    rc = 0;
+    rc = cli.run(gwCreateArgs4);
+    assertEquals(0, rc);
+    assertTrue(outContent.toString(), outContent.toString().contains("Invalid type for export file provided."));
+  }
+
+  @Test
   public void testCreateMaster() throws Exception {
     GatewayConfigImpl config = new GatewayConfigImpl();
     FileUtils.deleteQuietly( new File( config.getGatewaySecurityDir() ) );

http://git-wip-us.apache.org/repos/asf/knox/blob/e5ef45aa/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java
index 936356d..9035b85 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/impl/X509CertificateUtil.java
@@ -17,6 +17,8 @@
  */
 package org.apache.hadoop.gateway.services.security.impl;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
@@ -25,13 +27,20 @@ import java.lang.reflect.Method;
 import java.math.BigInteger;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.Date;
 
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.commons.codec.binary.Base64;
 import org.apache.hadoop.gateway.i18n.GatewaySpiMessages;
 
 public class X509CertificateUtil {
@@ -263,5 +272,32 @@ public class X509CertificateUtil {
     String fieldValue = (String) privateStringField.get(obj);
     return fieldValue;
   }
+
+  public static void writeCertificateToFile(Certificate cert, final File file)
+       throws CertificateEncodingException, IOException {
+    byte[] bytes = cert.getEncoded();
+    final FileOutputStream out = new FileOutputStream( file );
+    Base64 encoder = new Base64( 76, "\n".getBytes( "ASCII" ) );
+    try {
+      out.write( "-----BEGIN CERTIFICATE-----\n".getBytes( "ASCII" ) );
+      out.write( encoder.encodeToString( bytes ).getBytes( "ASCII" ) );
+      out.write( "-----END CERTIFICATE-----\n".getBytes( "ASCII" ) );
+    } finally {
+      out.close();
+    }
+  }
+
+  public static void writeCertificateToJKS(Certificate cert, final File file)
+      throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
+    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+
+    char[] password = "changeme".toCharArray();
+    ks.load(null, password);
+    ks.setCertificateEntry("gateway-identity", cert);
+
+    FileOutputStream fos = new FileOutputStream(file);
+    ks.store(fos, password);
+    fos.close();
+  }
 }