You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ze...@apache.org on 2018/05/03 02:10:53 UTC

directory-kerby git commit: Add HTTPS certification deployment tool.

Repository: directory-kerby
Updated Branches:
  refs/heads/has-project aab4c7409 -> b5b9595f1


Add HTTPS certification deployment tool.


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

Branch: refs/heads/has-project
Commit: b5b9595f19b4509168b9dbc08cfe6247d925e7e1
Parents: aab4c74
Author: zenglinx <fr...@intel.com>
Authored: Thu May 3 10:10:29 2018 +0800
Committer: zenglinx <fr...@intel.com>
Committed: Thu May 3 10:10:29 2018 +0800

----------------------------------------------------------------------
 has/doc/deploy-https.md                         |  28 +-
 has/doc/has-start.md                            |   4 +-
 .../src/main/resources/ssl-client.conf.template |   1 +
 .../server/hadmin/local/HadminLocalTool.java    |   8 +-
 .../cmd/AddPrincipalsAndDeployKeytabsCmd.java   |  15 +-
 .../hadmin/local/cmd/DeployHTTPSCertsCmd.java   | 310 +++++++++++++++++++
 6 files changed, 355 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b5b9595f/has/doc/deploy-https.md
----------------------------------------------------------------------
diff --git a/has/doc/deploy-https.md b/has/doc/deploy-https.md
index 4ae328c..05568fc 100644
--- a/has/doc/deploy-https.md
+++ b/has/doc/deploy-https.md
@@ -38,7 +38,33 @@ When you finish, the {trustAll} file will have the certificates from all nodes.
 keytool -list -v -keystore {trustAll}
 ```
 
-## 7. Edit the Configuration files
+## 7. Use Hadmin tool to create new certificate files when adding machines of cluster
+When adding machines of cluster, we also provide shell tool to create keystore files and update the truststore file.
+With this tool, user just need one operation, and the certificate and configuration files will be generated and deployed.
+
+```
+cd HAS/has-dist
+echo node1,node2,node3 > hosts.txt
+
+// Start local hadmin tool
+sh bin/hadmin-local.sh <conf_dir> -k <keytab>
+
+// deploy_certs [Hosts-File] [truststore_file] [truststore_password] [Where-to-Deploy] [SSH-Port] [UserName] [Password]
+// truststore_file: The absolute path of the above trustAll file
+// truststore_password: Password of the truststore file
+// Where-to-Deploy: The place to store the keystore, truststore, ssl-client.conf
+// SSH-Port: The port of SSH
+// UserName: The host user name
+// Password: The host password
+// All the hosts with the same user and password
+HadminLocalTool.local: deploy_https hosts.txt /etc/has/truststore.jks 123456 /etc/has 22 username password
+HadminLocalTool.local: exit
+```
+
+> Note that the same keypassword and password of keystore is given in the generated ssl-client.conf.
+
+
+## 8. Edit the Configuration files
 > Deploy {keystore} and {trustAll} files and config /<conf-dir>/ssl-server.conf for HAS server
 ```
 ssl.server.keystore.location = {path to keystore}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b5b9595f/has/doc/has-start.md
----------------------------------------------------------------------
diff --git a/has/doc/has-start.md b/has/doc/has-start.md
index 5d92ae7..46b1d95 100644
--- a/has/doc/has-start.md
+++ b/has/doc/has-start.md
@@ -201,12 +201,12 @@ echo { \
 // Start local hadmin tool
 sh bin/hadmin-local.sh <conf_dir> -k <keytab>
 
-// deploy_keytabs [HostRoles-File] [Where-to-Deploy] [UserName] [Password]
+// deploy_keytabs [HostRoles-File] [Where-to-Deploy] [SSH-Port] [UserName] [Password]
 // Where-to-Deploy: The place to store the keytabs
 // UserName: The host user name
 // Password: The host password
 // All the hosts with the same user and password
-HadminLocalTool.local: deploy_keytabs hosts.txt /etc/has/ username password
+HadminLocalTool.local: deploy_keytabs hosts.txt 22 /etc/has/ username password
 HadminLocalTool.local: exit
 ```
 Note: The admin.keytab file is created by the kdcinit. In local hadmin tool, you can type "?" for help.

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b5b9595f/has/has-client/src/main/resources/ssl-client.conf.template
----------------------------------------------------------------------
diff --git a/has/has-client/src/main/resources/ssl-client.conf.template b/has/has-client/src/main/resources/ssl-client.conf.template
index c5ca70a..ed5a63f 100644
--- a/has/has-client/src/main/resources/ssl-client.conf.template
+++ b/has/has-client/src/main/resources/ssl-client.conf.template
@@ -18,3 +18,4 @@
 
 ssl.client.truststore.location = _location_
 ssl.client.truststore.password = _password_
+ssl.server.keystore.password = _keyPassword_
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b5b9595f/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/HadminLocalTool.java
----------------------------------------------------------------------
diff --git a/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/HadminLocalTool.java b/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/HadminLocalTool.java
index 7e4741a..267501a 100644
--- a/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/HadminLocalTool.java
+++ b/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/HadminLocalTool.java
@@ -27,6 +27,7 @@ import org.apache.kerby.has.tool.server.hadmin.local.cmd.AddPrincipalsAndDeployK
 import org.apache.kerby.has.tool.server.hadmin.local.cmd.AddPrincipalsCmd;
 import org.apache.kerby.has.tool.server.hadmin.local.cmd.ChangePasswordCmd;
 import org.apache.kerby.has.tool.server.hadmin.local.cmd.DeletePrincipalCmd;
+import org.apache.kerby.has.tool.server.hadmin.local.cmd.DeployHTTPSCertsCmd;
 import org.apache.kerby.has.tool.server.hadmin.local.cmd.DisableConfigureCmd;
 import org.apache.kerby.has.tool.server.hadmin.local.cmd.EnableConfigureCmd;
 import org.apache.kerby.has.tool.server.hadmin.local.cmd.ExportKeytabsCmd;
@@ -103,7 +104,9 @@ public class HadminLocalTool {
         + "disable_configure, disable\n"
         + "                         Disable configure\n"
         + "deploy_keytabs, depkeytabs\n"
-        + "                         Deploy keytabs\n";
+        + "                         Deploy keytabs\n"
+        + "deploy_https, dephttps\n"
+        + "                         Deploy https\n";
 
     private static void execute(LocalHasAdmin hadmin, String input) throws HasException {
         // Omit the leading and trailing whitespace.
@@ -155,6 +158,9 @@ public class HadminLocalTool {
         } else if (cmd.startsWith("deploy_keytabs")
             || cmd.startsWith("depkeytabs")) {
             executor = new AddPrincipalsAndDeployKeytabsCmd(hadmin);
+        } else if (cmd.startsWith("deploy_https")
+            || cmd.startsWith("dephttps")) {
+            executor = new DeployHTTPSCertsCmd(hadmin);
         } else {
             System.out.println(LEGAL_COMMANDS);
             return;

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b5b9595f/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsCmd.java
----------------------------------------------------------------------
diff --git a/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsCmd.java b/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsCmd.java
index 21edf20..991ff56 100644
--- a/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsCmd.java
+++ b/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsCmd.java
@@ -41,9 +41,9 @@ import java.util.List;
 
 public class AddPrincipalsAndDeployKeytabsCmd extends HadminCmd {
     private static final String USAGE
-        = "\nUsage: deploy_keytabs [HostRoles-File] [Where-to-Deploy] [UserName] [Password]\n"
+        = "\nUsage: deploy_keytabs [HostRoles-File] [Where-to-Deploy] [SSH-Port] [UserName] [Password]\n"
         + "\tExample:\n"
-        + "\t\tdeploy_keytabs hostroles.txt /etc/has/ username password\n";
+        + "\t\tdeploy_keytabs hostroles.txt /etc/has/ 22 username password\n";
 
     public AddPrincipalsAndDeployKeytabsCmd(LocalHasAdmin hadmin) {
         super(hadmin);
@@ -52,7 +52,7 @@ public class AddPrincipalsAndDeployKeytabsCmd extends HadminCmd {
     @Override
     public void execute(String[] items) throws HasException {
 
-        if (items.length < 4 || items.length > 5) {
+        if (items.length < 5 || items.length > 6) {
             System.err.println(USAGE);
             return;
         }
@@ -62,10 +62,11 @@ public class AddPrincipalsAndDeployKeytabsCmd extends HadminCmd {
             throw new HasException("HostRoles file is not exists.");
         }
         String pathToDeploy = items[2];
-        String username = items[3];
+        int port = Integer.valueOf(items[3]);
+        String username = items[4];
         String password = "";
-        if (items.length == 5) {
-            password = items[4];
+        if (items.length == 6) {
+            password = items[5];
         }
 
         BufferedReader reader;
@@ -123,7 +124,7 @@ public class AddPrincipalsAndDeployKeytabsCmd extends HadminCmd {
             JSch jsch = new JSch();
             Session session;
             try {
-                session = jsch.getSession(username, hostname);
+                session = jsch.getSession(username, hostname, port);
             } catch (JSchException e) {
                 throw new HasException(e.getMessage());
             }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/b5b9595f/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java
----------------------------------------------------------------------
diff --git a/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java b/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java
new file mode 100644
index 0000000..4a16f65
--- /dev/null
+++ b/has/has-tool/has-server-tool/src/main/java/org/apache/kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java
@@ -0,0 +1,310 @@
+/**
+ *  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.kerby.has.tool.server.hadmin.local.cmd;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
+import org.apache.commons.text.CharacterPredicates;
+import org.apache.commons.text.RandomStringGenerator;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHasAdmin;
+import org.apache.kerby.util.IOUtil;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+
+import javax.security.auth.x500.X500Principal;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * HTTPS certifications deploy tool.
+ */
+public class DeployHTTPSCertsCmd extends HadminCmd {
+    private static final String USAGE
+            = "\nUsage: deploy_certs [Hosts-File] [truststore_file] [truststore_password]"
+            + " [Where-to-Deploy] [SSH-Port] [UserName] [Password]\n"
+            + "\tExample:\n"
+            + "\t\tdeploy_https hosts.txt /etc/has/truststore.jks 123456 /etc/has 22 username password\n";
+
+    public DeployHTTPSCertsCmd(LocalHasAdmin hadmin) {
+        super(hadmin);
+    }
+
+    private static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(1024);
+        return keyGen.genKeyPair();
+    }
+
+    private static X509Certificate generateCertificate(String args, KeyPair pair)
+            throws CertificateEncodingException, InvalidKeyException, IllegalStateException,
+            NoSuchAlgorithmException, SignatureException {
+
+        Date from = new Date();
+        Date to = new Date(from.getTime() + 90 * 86400000L);
+        BigInteger sn = new BigInteger(64, new SecureRandom());
+        X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+        X500Principal dnName = new X500Principal(args);
+
+        certGen.setSerialNumber(sn);
+        certGen.setIssuerDN(dnName);
+        certGen.setNotBefore(from);
+        certGen.setNotAfter(to);
+        certGen.setSubjectDN(dnName);
+        certGen.setPublicKey(pair.getPublic());
+        certGen.setSignatureAlgorithm("SHA1withRSA");
+
+        return certGen.generate(pair.getPrivate());
+    }
+
+    private static File saveKeyStore(String fileName, KeyStore ks, String password)
+            throws GeneralSecurityException, IOException {
+        File keystoreFile = new File(fileName);
+        if (keystoreFile.exists() && !keystoreFile.delete()) {
+            throw new IOException("Failed to delete original file: " + fileName);
+        }
+        FileOutputStream out = new FileOutputStream(keystoreFile);
+        ks.store(out, password.toCharArray());
+        out.close();
+        return keystoreFile;
+    }
+
+    private File createClientSSLConfig(String trustStorePath, String trustStorePassword,
+                                       String keyStorePassword) throws HasException {
+        String resourcePath = "/ssl-client.conf.template";
+        InputStream templateResource = getClass().getResourceAsStream(resourcePath);
+        File sslConfigFile = new File("ssl-client.conf");
+        try {
+            String content = IOUtil.readInput(templateResource);
+            content = content.replaceAll("_location_", trustStorePath);
+            content = content.replaceAll("_password_", trustStorePassword);
+            content = content.replaceAll("_keyPassword_", keyStorePassword);
+
+            IOUtil.writeFile(content, sslConfigFile);
+            return sslConfigFile;
+        } catch (IOException e) {
+            throw new HasException("Failed to create client ssl configuration file", e);
+        }
+    }
+
+    private final class KeyStoreInfo {
+        KeyStore keyStore;
+        String keyPasswd;
+
+        private KeyStoreInfo(KeyStore keyStore, String keyPasswd) {
+            this.keyStore = keyStore;
+            this.keyPasswd = keyPasswd;
+        }
+
+        private String getKeyPasswd() {
+            return this.keyPasswd;
+        }
+
+        private KeyStore getKeyStore() {
+            return this.keyStore;
+        }
+    }
+
+    @Override
+    public void execute(String[] items) throws HasException {
+
+        if (items.length < 7 || items.length > 8) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        File hostFile = new File(items[1]);
+        if (!hostFile.exists()) {
+            throw new HasException("Host file is not exist.");
+        }
+        String truststoreFile = items[2];
+        String truststoreSecret = items[3];
+        String pathToDeploy = items[4];
+        int port = Integer.valueOf(items[5]);
+        String username = items[6];
+        String password = "";
+        if (items.length == 8) {
+            password = items[7];
+        }
+
+        // Get hosts from host file
+        BufferedReader reader;
+        try {
+            reader = new BufferedReader(new FileReader(hostFile));
+        } catch (FileNotFoundException e) {
+            throw new HasException("The hosts file: " + hostFile
+                + "is not exist. " + e.getMessage());
+        }
+        StringBuilder sb = new StringBuilder();
+        String tempString;
+        try {
+            while ((tempString = reader.readLine()) != null) {
+                sb.append(tempString);
+            }
+        } catch (IOException e1) {
+            throw new HasException("Failed to read file: " + e1.getMessage());
+        }
+        String[] hostArray = sb.toString().replace(" ", "").split(",");
+
+        // Get truststore from truststore file
+        Map<String, KeyStoreInfo> keyStoreInfoMap = new HashMap<>(16);
+        KeyStore trustStore;
+        try {
+            trustStore = KeyStore.getInstance("JKS");
+            FileInputStream in = new FileInputStream(truststoreFile);
+            trustStore.load(in, truststoreSecret.toCharArray());
+        } catch (Exception e2) {
+            throw new HasException("Failed to get truststore from the file: "
+                + truststoreFile, e2);
+        }
+        RandomStringGenerator generator = new RandomStringGenerator.Builder()
+            .withinRange('a', 'z')
+            .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
+            .build();
+
+        // Generate keystore map
+        for (String hostname : hostArray) {
+            try {
+                InetAddress inetAddress = InetAddress.getLocalHost();
+                String localHostname = inetAddress.getHostName();
+                if (hostname.equals(localHostname)) {
+                    continue;
+                }
+            } catch (UnknownHostException e3) {
+                throw new HasException("Failed to get local hostname.", e3);
+            }
+
+            KeyStore ks;
+            try {
+                KeyPair cKP = generateKeyPair();
+                String keyPassword = generator.generate(15);
+                X509Certificate cert = generateCertificate("CN=" + hostname + ", O=has", cKP);
+                ks = KeyStore.getInstance("JKS");
+                ks.load(null, null);
+                ks.setKeyEntry(hostname, cKP.getPrivate(), keyPassword.toCharArray(),
+                    new Certificate[]{cert});
+                KeyStoreInfo keyStoreInfo = new KeyStoreInfo(ks, keyPassword);
+                keyStoreInfoMap.put(hostname, keyStoreInfo);
+                trustStore.setCertificateEntry(hostname, cert);
+            } catch (Exception e4) {
+                throw new HasException("Failed to generate keystore.", e4);
+            }
+        }
+
+        File finalTrustStoreFile;
+        try {
+            finalTrustStoreFile = saveKeyStore(truststoreFile, trustStore, password);
+        } catch (Exception e5) {
+            throw new HasException("Failed to generate trust store files.", e5);
+        }
+
+        // Generate keystore, truststore, ssl config files and transfer them to destination
+        for (String hostname : hostArray) {
+            List<File> files = new ArrayList<>(3);
+            try {
+                KeyStoreInfo keyStoreInfo = keyStoreInfoMap.get(hostname);
+                File file = saveKeyStore(hostname + "_keystore.jks",
+                    keyStoreInfo.getKeyStore(), keyStoreInfo.getKeyPasswd());
+                files.add(file);
+                files.add(finalTrustStoreFile);
+                files.add(createClientSSLConfig(pathToDeploy + "/truststore.jks",
+                    truststoreSecret, keyStoreInfo.getKeyPasswd()));
+            } catch (Exception e6) {
+                throw new HasException("Failed to generate key store files.", e6);
+            }
+
+            JSch jsch = new JSch();
+            Session session;
+            try {
+                session = jsch.getSession(username, hostname, port);
+            } catch (JSchException e7) {
+                throw new HasException(e7.getMessage());
+            }
+            session.setPassword(password);
+
+            java.util.Properties config = new java.util.Properties();
+            config.put("StrictHostKeyChecking", "no");
+            session.setConfig(config);
+
+            ChannelSftp channel;
+            try {
+                session.connect();
+                channel = (ChannelSftp) session.openChannel("sftp");
+                channel.connect();
+            } catch (JSchException e8) {
+                throw new HasException("Failed to set the session: " + e8.getMessage());
+            }
+            try {
+                String path = "";
+                String[] paths = pathToDeploy.split("/");
+                for (int i = 1; i < paths.length; i++) {
+                    path = path + "/" + paths[i];
+                    try {
+                        channel.cd(path);
+                    } catch (SftpException e9) {
+                        if (e9.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                            channel.mkdir(path);
+                        } else {
+                            throw new HasException(e9.getMessage());
+                        }
+                    }
+                }
+            } catch (SftpException e10) {
+                throw new HasException("Failed to mkdir path: " + e10);
+            }
+
+            for (File file : files) {
+                try {
+                    channel.put(file.getAbsolutePath(), file.getName());
+                } catch (SftpException e10) {
+                    throw new HasException("Failed to send the https cert files.", e10);
+                }
+            }
+            channel.disconnect();
+        }
+    }
+}


Re: directory-kerby git commit: Add HTTPS certification deployment tool.

Posted by Colm O hEigeartaigh <co...@apache.org>.
Hi,

Shouldn't this commit go on trunk instead? What is the current difference
between the has_project and trunk branches?

Colm.

On Thu, May 3, 2018 at 3:10 AM, <ze...@apache.org> wrote:

> Repository: directory-kerby
> Updated Branches:
>   refs/heads/has-project aab4c7409 -> b5b9595f1
>
>
> Add HTTPS certification deployment tool.
>
>
> Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
> Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/
> commit/b5b9595f
> Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/b5b9595f
> Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/b5b9595f
>
> Branch: refs/heads/has-project
> Commit: b5b9595f19b4509168b9dbc08cfe6247d925e7e1
> Parents: aab4c74
> Author: zenglinx <fr...@intel.com>
> Authored: Thu May 3 10:10:29 2018 +0800
> Committer: zenglinx <fr...@intel.com>
> Committed: Thu May 3 10:10:29 2018 +0800
>
> ----------------------------------------------------------------------
>  has/doc/deploy-https.md                         |  28 +-
>  has/doc/has-start.md                            |   4 +-
>  .../src/main/resources/ssl-client.conf.template |   1 +
>  .../server/hadmin/local/HadminLocalTool.java    |   8 +-
>  .../cmd/AddPrincipalsAndDeployKeytabsCmd.java   |  15 +-
>  .../hadmin/local/cmd/DeployHTTPSCertsCmd.java   | 310 +++++++++++++++++++
>  6 files changed, 355 insertions(+), 11 deletions(-)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/b5b9595f/has/doc/deploy-https.md
> ----------------------------------------------------------------------
> diff --git a/has/doc/deploy-https.md b/has/doc/deploy-https.md
> index 4ae328c..05568fc 100644
> --- a/has/doc/deploy-https.md
> +++ b/has/doc/deploy-https.md
> @@ -38,7 +38,33 @@ When you finish, the {trustAll} file will have the
> certificates from all nodes.
>  keytool -list -v -keystore {trustAll}
>  ```
>
> -## 7. Edit the Configuration files
> +## 7. Use Hadmin tool to create new certificate files when adding
> machines of cluster
> +When adding machines of cluster, we also provide shell tool to create
> keystore files and update the truststore file.
> +With this tool, user just need one operation, and the certificate and
> configuration files will be generated and deployed.
> +
> +```
> +cd HAS/has-dist
> +echo node1,node2,node3 > hosts.txt
> +
> +// Start local hadmin tool
> +sh bin/hadmin-local.sh <conf_dir> -k <keytab>
> +
> +// deploy_certs [Hosts-File] [truststore_file] [truststore_password]
> [Where-to-Deploy] [SSH-Port] [UserName] [Password]
> +// truststore_file: The absolute path of the above trustAll file
> +// truststore_password: Password of the truststore file
> +// Where-to-Deploy: The place to store the keystore, truststore,
> ssl-client.conf
> +// SSH-Port: The port of SSH
> +// UserName: The host user name
> +// Password: The host password
> +// All the hosts with the same user and password
> +HadminLocalTool.local: deploy_https hosts.txt /etc/has/truststore.jks
> 123456 /etc/has 22 username password
> +HadminLocalTool.local: exit
> +```
> +
> +> Note that the same keypassword and password of keystore is given in the
> generated ssl-client.conf.
> +
> +
> +## 8. Edit the Configuration files
>  > Deploy {keystore} and {trustAll} files and config
> /<conf-dir>/ssl-server.conf for HAS server
>  ```
>  ssl.server.keystore.location = {path to keystore}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/b5b9595f/has/doc/has-start.md
> ----------------------------------------------------------------------
> diff --git a/has/doc/has-start.md b/has/doc/has-start.md
> index 5d92ae7..46b1d95 100644
> --- a/has/doc/has-start.md
> +++ b/has/doc/has-start.md
> @@ -201,12 +201,12 @@ echo { \
>  // Start local hadmin tool
>  sh bin/hadmin-local.sh <conf_dir> -k <keytab>
>
> -// deploy_keytabs [HostRoles-File] [Where-to-Deploy] [UserName] [Password]
> +// deploy_keytabs [HostRoles-File] [Where-to-Deploy] [SSH-Port]
> [UserName] [Password]
>  // Where-to-Deploy: The place to store the keytabs
>  // UserName: The host user name
>  // Password: The host password
>  // All the hosts with the same user and password
> -HadminLocalTool.local: deploy_keytabs hosts.txt /etc/has/ username
> password
> +HadminLocalTool.local: deploy_keytabs hosts.txt 22 /etc/has/ username
> password
>  HadminLocalTool.local: exit
>  ```
>  Note: The admin.keytab file is created by the kdcinit. In local hadmin
> tool, you can type "?" for help.
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/b5b9595f/has/has-client/src/main/resources/ssl-client.conf.template
> ----------------------------------------------------------------------
> diff --git a/has/has-client/src/main/resources/ssl-client.conf.template
> b/has/has-client/src/main/resources/ssl-client.conf.template
> index c5ca70a..ed5a63f 100644
> --- a/has/has-client/src/main/resources/ssl-client.conf.template
> +++ b/has/has-client/src/main/resources/ssl-client.conf.template
> @@ -18,3 +18,4 @@
>
>  ssl.client.truststore.location = _location_
>  ssl.client.truststore.password = _password_
> +ssl.server.keystore.password = _keyPassword_
> \ No newline at end of file
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/b5b9595f/has/has-tool/has-server-tool/src/main/java/
> org/apache/kerby/has/tool/server/hadmin/local/HadminLocalTool.java
> ----------------------------------------------------------------------
> diff --git a/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/HadminLocalTool.java
> b/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/HadminLocalTool.java
> index 7e4741a..267501a 100644
> --- a/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/HadminLocalTool.java
> +++ b/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/HadminLocalTool.java
> @@ -27,6 +27,7 @@ import org.apache.kerby.has.tool.
> server.hadmin.local.cmd.AddPrincipalsAndDeployK
>  import org.apache.kerby.has.tool.server.hadmin.local.cmd.
> AddPrincipalsCmd;
>  import org.apache.kerby.has.tool.server.hadmin.local.cmd.
> ChangePasswordCmd;
>  import org.apache.kerby.has.tool.server.hadmin.local.cmd.
> DeletePrincipalCmd;
> +import org.apache.kerby.has.tool.server.hadmin.local.cmd.
> DeployHTTPSCertsCmd;
>  import org.apache.kerby.has.tool.server.hadmin.local.cmd.
> DisableConfigureCmd;
>  import org.apache.kerby.has.tool.server.hadmin.local.cmd.
> EnableConfigureCmd;
>  import org.apache.kerby.has.tool.server.hadmin.local.cmd.
> ExportKeytabsCmd;
> @@ -103,7 +104,9 @@ public class HadminLocalTool {
>          + "disable_configure, disable\n"
>          + "                         Disable configure\n"
>          + "deploy_keytabs, depkeytabs\n"
> -        + "                         Deploy keytabs\n";
> +        + "                         Deploy keytabs\n"
> +        + "deploy_https, dephttps\n"
> +        + "                         Deploy https\n";
>
>      private static void execute(LocalHasAdmin hadmin, String input)
> throws HasException {
>          // Omit the leading and trailing whitespace.
> @@ -155,6 +158,9 @@ public class HadminLocalTool {
>          } else if (cmd.startsWith("deploy_keytabs")
>              || cmd.startsWith("depkeytabs")) {
>              executor = new AddPrincipalsAndDeployKeytabsCmd(hadmin);
> +        } else if (cmd.startsWith("deploy_https")
> +            || cmd.startsWith("dephttps")) {
> +            executor = new DeployHTTPSCertsCmd(hadmin);
>          } else {
>              System.out.println(LEGAL_COMMANDS);
>              return;
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/b5b9595f/has/has-tool/has-server-tool/src/main/java/
> org/apache/kerby/has/tool/server/hadmin/local/cmd/
> AddPrincipalsAndDeployKeytabsCmd.java
> ----------------------------------------------------------------------
> diff --git a/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsCmd.java
> b/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsC
> md.java
> index 21edf20..991ff56 100644
> --- a/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsC
> md.java
> +++ b/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/cmd/AddPrincipalsAndDeployKeytabsC
> md.java
> @@ -41,9 +41,9 @@ import java.util.List;
>
>  public class AddPrincipalsAndDeployKeytabsCmd extends HadminCmd {
>      private static final String USAGE
> -        = "\nUsage: deploy_keytabs [HostRoles-File] [Where-to-Deploy]
> [UserName] [Password]\n"
> +        = "\nUsage: deploy_keytabs [HostRoles-File] [Where-to-Deploy]
> [SSH-Port] [UserName] [Password]\n"
>          + "\tExample:\n"
> -        + "\t\tdeploy_keytabs hostroles.txt /etc/has/ username
> password\n";
> +        + "\t\tdeploy_keytabs hostroles.txt /etc/has/ 22 username
> password\n";
>
>      public AddPrincipalsAndDeployKeytabsCmd(LocalHasAdmin hadmin) {
>          super(hadmin);
> @@ -52,7 +52,7 @@ public class AddPrincipalsAndDeployKeytabsCmd extends
> HadminCmd {
>      @Override
>      public void execute(String[] items) throws HasException {
>
> -        if (items.length < 4 || items.length > 5) {
> +        if (items.length < 5 || items.length > 6) {
>              System.err.println(USAGE);
>              return;
>          }
> @@ -62,10 +62,11 @@ public class AddPrincipalsAndDeployKeytabsCmd extends
> HadminCmd {
>              throw new HasException("HostRoles file is not exists.");
>          }
>          String pathToDeploy = items[2];
> -        String username = items[3];
> +        int port = Integer.valueOf(items[3]);
> +        String username = items[4];
>          String password = "";
> -        if (items.length == 5) {
> -            password = items[4];
> +        if (items.length == 6) {
> +            password = items[5];
>          }
>
>          BufferedReader reader;
> @@ -123,7 +124,7 @@ public class AddPrincipalsAndDeployKeytabsCmd extends
> HadminCmd {
>              JSch jsch = new JSch();
>              Session session;
>              try {
> -                session = jsch.getSession(username, hostname);
> +                session = jsch.getSession(username, hostname, port);
>              } catch (JSchException e) {
>                  throw new HasException(e.getMessage());
>              }
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/b5b9595f/has/has-tool/has-server-tool/src/main/java/
> org/apache/kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java
> ----------------------------------------------------------------------
> diff --git a/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java
> b/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java
> new file mode 100644
> index 0000000..4a16f65
> --- /dev/null
> +++ b/has/has-tool/has-server-tool/src/main/java/org/apache/
> kerby/has/tool/server/hadmin/local/cmd/DeployHTTPSCertsCmd.java
> @@ -0,0 +1,310 @@
> +/**
> + *  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.kerby.has.tool.server.hadmin.local.cmd;
> +
> +import com.jcraft.jsch.ChannelSftp;
> +import com.jcraft.jsch.JSch;
> +import com.jcraft.jsch.JSchException;
> +import com.jcraft.jsch.Session;
> +import com.jcraft.jsch.SftpException;
> +import org.apache.commons.text.CharacterPredicates;
> +import org.apache.commons.text.RandomStringGenerator;
> +import org.apache.kerby.has.common.HasException;
> +import org.apache.kerby.has.server.admin.LocalHasAdmin;
> +import org.apache.kerby.util.IOUtil;
> +import org.bouncycastle.x509.X509V1CertificateGenerator;
> +
> +import javax.security.auth.x500.X500Principal;
> +import java.io.BufferedReader;
> +import java.io.File;
> +import java.io.FileInputStream;
> +import java.io.FileNotFoundException;
> +import java.io.FileOutputStream;
> +import java.io.FileReader;
> +import java.io.InputStream;
> +import java.io.IOException;
> +import java.math.BigInteger;
> +import java.net.InetAddress;
> +import java.net.UnknownHostException;
> +import java.security.GeneralSecurityException;
> +import java.security.InvalidKeyException;
> +import java.security.KeyPair;
> +import java.security.KeyStore;
> +import java.security.KeyPairGenerator;
> +import java.security.NoSuchAlgorithmException;
> +import java.security.SecureRandom;
> +import java.security.SignatureException;
> +import java.security.cert.Certificate;
> +import java.security.cert.CertificateEncodingException;
> +import java.security.cert.X509Certificate;
> +import java.util.Date;
> +import java.util.Map;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.ArrayList;
> +
> +/**
> + * HTTPS certifications deploy tool.
> + */
> +public class DeployHTTPSCertsCmd extends HadminCmd {
> +    private static final String USAGE
> +            = "\nUsage: deploy_certs [Hosts-File] [truststore_file]
> [truststore_password]"
> +            + " [Where-to-Deploy] [SSH-Port] [UserName] [Password]\n"
> +            + "\tExample:\n"
> +            + "\t\tdeploy_https hosts.txt /etc/has/truststore.jks 123456
> /etc/has 22 username password\n";
> +
> +    public DeployHTTPSCertsCmd(LocalHasAdmin hadmin) {
> +        super(hadmin);
> +    }
> +
> +    private static KeyPair generateKeyPair() throws
> NoSuchAlgorithmException {
> +        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
> +        keyGen.initialize(1024);
> +        return keyGen.genKeyPair();
> +    }
> +
> +    private static X509Certificate generateCertificate(String args,
> KeyPair pair)
> +            throws CertificateEncodingException, InvalidKeyException,
> IllegalStateException,
> +            NoSuchAlgorithmException, SignatureException {
> +
> +        Date from = new Date();
> +        Date to = new Date(from.getTime() + 90 * 86400000L);
> +        BigInteger sn = new BigInteger(64, new SecureRandom());
> +        X509V1CertificateGenerator certGen = new
> X509V1CertificateGenerator();
> +        X500Principal dnName = new X500Principal(args);
> +
> +        certGen.setSerialNumber(sn);
> +        certGen.setIssuerDN(dnName);
> +        certGen.setNotBefore(from);
> +        certGen.setNotAfter(to);
> +        certGen.setSubjectDN(dnName);
> +        certGen.setPublicKey(pair.getPublic());
> +        certGen.setSignatureAlgorithm("SHA1withRSA");
> +
> +        return certGen.generate(pair.getPrivate());
> +    }
> +
> +    private static File saveKeyStore(String fileName, KeyStore ks, String
> password)
> +            throws GeneralSecurityException, IOException {
> +        File keystoreFile = new File(fileName);
> +        if (keystoreFile.exists() && !keystoreFile.delete()) {
> +            throw new IOException("Failed to delete original file: " +
> fileName);
> +        }
> +        FileOutputStream out = new FileOutputStream(keystoreFile);
> +        ks.store(out, password.toCharArray());
> +        out.close();
> +        return keystoreFile;
> +    }
> +
> +    private File createClientSSLConfig(String trustStorePath, String
> trustStorePassword,
> +                                       String keyStorePassword) throws
> HasException {
> +        String resourcePath = "/ssl-client.conf.template";
> +        InputStream templateResource = getClass().getResourceAsStream(
> resourcePath);
> +        File sslConfigFile = new File("ssl-client.conf");
> +        try {
> +            String content = IOUtil.readInput(templateResource);
> +            content = content.replaceAll("_location_", trustStorePath);
> +            content = content.replaceAll("_password_",
> trustStorePassword);
> +            content = content.replaceAll("_keyPassword_",
> keyStorePassword);
> +
> +            IOUtil.writeFile(content, sslConfigFile);
> +            return sslConfigFile;
> +        } catch (IOException e) {
> +            throw new HasException("Failed to create client ssl
> configuration file", e);
> +        }
> +    }
> +
> +    private final class KeyStoreInfo {
> +        KeyStore keyStore;
> +        String keyPasswd;
> +
> +        private KeyStoreInfo(KeyStore keyStore, String keyPasswd) {
> +            this.keyStore = keyStore;
> +            this.keyPasswd = keyPasswd;
> +        }
> +
> +        private String getKeyPasswd() {
> +            return this.keyPasswd;
> +        }
> +
> +        private KeyStore getKeyStore() {
> +            return this.keyStore;
> +        }
> +    }
> +
> +    @Override
> +    public void execute(String[] items) throws HasException {
> +
> +        if (items.length < 7 || items.length > 8) {
> +            System.err.println(USAGE);
> +            return;
> +        }
> +
> +        File hostFile = new File(items[1]);
> +        if (!hostFile.exists()) {
> +            throw new HasException("Host file is not exist.");
> +        }
> +        String truststoreFile = items[2];
> +        String truststoreSecret = items[3];
> +        String pathToDeploy = items[4];
> +        int port = Integer.valueOf(items[5]);
> +        String username = items[6];
> +        String password = "";
> +        if (items.length == 8) {
> +            password = items[7];
> +        }
> +
> +        // Get hosts from host file
> +        BufferedReader reader;
> +        try {
> +            reader = new BufferedReader(new FileReader(hostFile));
> +        } catch (FileNotFoundException e) {
> +            throw new HasException("The hosts file: " + hostFile
> +                + "is not exist. " + e.getMessage());
> +        }
> +        StringBuilder sb = new StringBuilder();
> +        String tempString;
> +        try {
> +            while ((tempString = reader.readLine()) != null) {
> +                sb.append(tempString);
> +            }
> +        } catch (IOException e1) {
> +            throw new HasException("Failed to read file: " +
> e1.getMessage());
> +        }
> +        String[] hostArray = sb.toString().replace(" ", "").split(",");
> +
> +        // Get truststore from truststore file
> +        Map<String, KeyStoreInfo> keyStoreInfoMap = new HashMap<>(16);
> +        KeyStore trustStore;
> +        try {
> +            trustStore = KeyStore.getInstance("JKS");
> +            FileInputStream in = new FileInputStream(truststoreFile);
> +            trustStore.load(in, truststoreSecret.toCharArray());
> +        } catch (Exception e2) {
> +            throw new HasException("Failed to get truststore from the
> file: "
> +                + truststoreFile, e2);
> +        }
> +        RandomStringGenerator generator = new
> RandomStringGenerator.Builder()
> +            .withinRange('a', 'z')
> +            .filteredBy(CharacterPredicates.LETTERS,
> CharacterPredicates.DIGITS)
> +            .build();
> +
> +        // Generate keystore map
> +        for (String hostname : hostArray) {
> +            try {
> +                InetAddress inetAddress = InetAddress.getLocalHost();
> +                String localHostname = inetAddress.getHostName();
> +                if (hostname.equals(localHostname)) {
> +                    continue;
> +                }
> +            } catch (UnknownHostException e3) {
> +                throw new HasException("Failed to get local hostname.",
> e3);
> +            }
> +
> +            KeyStore ks;
> +            try {
> +                KeyPair cKP = generateKeyPair();
> +                String keyPassword = generator.generate(15);
> +                X509Certificate cert = generateCertificate("CN=" +
> hostname + ", O=has", cKP);
> +                ks = KeyStore.getInstance("JKS");
> +                ks.load(null, null);
> +                ks.setKeyEntry(hostname, cKP.getPrivate(),
> keyPassword.toCharArray(),
> +                    new Certificate[]{cert});
> +                KeyStoreInfo keyStoreInfo = new KeyStoreInfo(ks,
> keyPassword);
> +                keyStoreInfoMap.put(hostname, keyStoreInfo);
> +                trustStore.setCertificateEntry(hostname, cert);
> +            } catch (Exception e4) {
> +                throw new HasException("Failed to generate keystore.",
> e4);
> +            }
> +        }
> +
> +        File finalTrustStoreFile;
> +        try {
> +            finalTrustStoreFile = saveKeyStore(truststoreFile,
> trustStore, password);
> +        } catch (Exception e5) {
> +            throw new HasException("Failed to generate trust store
> files.", e5);
> +        }
> +
> +        // Generate keystore, truststore, ssl config files and transfer
> them to destination
> +        for (String hostname : hostArray) {
> +            List<File> files = new ArrayList<>(3);
> +            try {
> +                KeyStoreInfo keyStoreInfo = keyStoreInfoMap.get(hostname);
> +                File file = saveKeyStore(hostname + "_keystore.jks",
> +                    keyStoreInfo.getKeyStore(),
> keyStoreInfo.getKeyPasswd());
> +                files.add(file);
> +                files.add(finalTrustStoreFile);
> +                files.add(createClientSSLConfig(pathToDeploy +
> "/truststore.jks",
> +                    truststoreSecret, keyStoreInfo.getKeyPasswd()));
> +            } catch (Exception e6) {
> +                throw new HasException("Failed to generate key store
> files.", e6);
> +            }
> +
> +            JSch jsch = new JSch();
> +            Session session;
> +            try {
> +                session = jsch.getSession(username, hostname, port);
> +            } catch (JSchException e7) {
> +                throw new HasException(e7.getMessage());
> +            }
> +            session.setPassword(password);
> +
> +            java.util.Properties config = new java.util.Properties();
> +            config.put("StrictHostKeyChecking", "no");
> +            session.setConfig(config);
> +
> +            ChannelSftp channel;
> +            try {
> +                session.connect();
> +                channel = (ChannelSftp) session.openChannel("sftp");
> +                channel.connect();
> +            } catch (JSchException e8) {
> +                throw new HasException("Failed to set the session: " +
> e8.getMessage());
> +            }
> +            try {
> +                String path = "";
> +                String[] paths = pathToDeploy.split("/");
> +                for (int i = 1; i < paths.length; i++) {
> +                    path = path + "/" + paths[i];
> +                    try {
> +                        channel.cd(path);
> +                    } catch (SftpException e9) {
> +                        if (e9.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
> +                            channel.mkdir(path);
> +                        } else {
> +                            throw new HasException(e9.getMessage());
> +                        }
> +                    }
> +                }
> +            } catch (SftpException e10) {
> +                throw new HasException("Failed to mkdir path: " + e10);
> +            }
> +
> +            for (File file : files) {
> +                try {
> +                    channel.put(file.getAbsolutePath(), file.getName());
> +                } catch (SftpException e10) {
> +                    throw new HasException("Failed to send the https cert
> files.", e10);
> +                }
> +            }
> +            channel.disconnect();
> +        }
> +    }
> +}
>
>


-- 
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com