SLIDER-811 added provider/alias options to client getcertstore command
Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/dd2a70bd Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/dd2a70bd Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/dd2a70bd Branch: refs/heads/feature/package_simplification_II Commit: dd2a70bd63a60178de8a5d67e5ab2a43ac281420 Parents: 148a435 Author: Billie Rinaldi <billie.rina...@gmail.com> Authored: Fri Mar 13 06:47:24 2015 -0700 Committer: Billie Rinaldi <billie.rina...@gmail.com> Committed: Fri Mar 13 10:36:46 2015 -0700 ---------------------------------------------------------------------- .../org/apache/slider/client/SliderClient.java | 50 +++++-- .../slider/common/params/ActionClientArgs.java | 11 +- .../apache/slider/common/params/Arguments.java | 1 + .../AMClientCertStoreRetrievalIT.groovy | 131 ++++++++++++++----- 4 files changed, 148 insertions(+), 45 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/dd2a70bd/slider-core/src/main/java/org/apache/slider/client/SliderClient.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java index 8c1a9b2..df3ba95 100644 --- a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java +++ b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java @@ -697,7 +697,7 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe return startCluster(clustername, createArgs); } - private void checkForCredentials(Configuration conf, + private static void checkForCredentials(Configuration conf, ConfTree tree) throws IOException { if (tree.credentials == null || tree.credentials.size()==0) { log.info("No credentials requested"); @@ -726,9 +726,6 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe br = new BufferedReader(new InputStreamReader(System.in)); } char[] pass = readPassword(alias, br); - if (pass == null) - throw new IOException("Could not read credentials for " + alias + - " from stdin"); credentialProvider.createCredentialEntry(alias, pass); credentialProvider.flush(); Arrays.fill(pass, ' '); @@ -742,9 +739,21 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe } } + private static char[] readOnePassword(String alias) throws IOException { + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(System.in)); + return readPassword(alias, br); + } finally { + if (br != null) { + br.close(); + } + } + } + // using a normal reader instead of a secure one, // because stdin is not hooked up to the command line - private char[] readPassword(String alias, BufferedReader br) + private static char[] readPassword(String alias, BufferedReader br) throws IOException { char[] cred = null; @@ -763,6 +772,9 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe } if (newPassword2 != null) Arrays.fill(newPassword2, ' '); } while (noMatch); + if (cred == null) + throw new IOException("Could not read credentials for " + alias + + " from stdin"); return cred; } @@ -944,7 +956,7 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe } if (clientInfo.name == null) { - throw new BadCommandArgumentsException("No applicaiton name specified\n" + throw new BadCommandArgumentsException("No application name specified\n" + CommonArgs.usage(serviceArgs, ACTION_CLIENT)); } @@ -975,14 +987,26 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe String password = clientInfo.password; if (password == null) { - // get a password - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - try { - password = String.valueOf(readPassword(type.name(), br)); - } finally { - if (br != null) { - br.close(); + String provider = clientInfo.provider; + String alias = clientInfo.alias; + if (provider != null && alias != null) { + Configuration conf = new Configuration(getConfig()); + conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, provider); + char[] chars = conf.getPassword(alias); + if (chars == null) { + CredentialProvider credentialProvider = + CredentialProviderFactory.getProviders(conf).get(0); + chars = readOnePassword(alias); + credentialProvider.createCredentialEntry(alias, chars); + credentialProvider.flush(); } + password = String.valueOf(chars); + Arrays.fill(chars, ' '); + } else { + log.info("No password and no provider/alias pair were provided, " + + "prompting for password"); + // get a password + password = String.valueOf(readOnePassword(type.name())); } } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/dd2a70bd/slider-core/src/main/java/org/apache/slider/common/params/ActionClientArgs.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/ActionClientArgs.java b/slider-core/src/main/java/org/apache/slider/common/params/ActionClientArgs.java index c7f8c2e..85d39ea 100644 --- a/slider-core/src/main/java/org/apache/slider/common/params/ActionClientArgs.java +++ b/slider-core/src/main/java/org/apache/slider/common/params/ActionClientArgs.java @@ -57,8 +57,17 @@ public class ActionClientArgs extends AbstractActionArgs { description = "The name of the application") public String name; + @Parameter(names = {ARG_PROVIDER}, + description = "The credential provider in which the password is stored") + public String provider; + + @Parameter(names = {ARG_ALIAS}, + description = "The credential provider alias associated with the password") + public String alias; + @Parameter(names = {ARG_PASSWORD}, - description = "The certificate store password") + description = "The certificate store password (alternative to " + + "provider/alias; if password is specified, those will be ignored)") public String password; @Parameter(names = {ARG_PACKAGE}, http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/dd2a70bd/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java b/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java index 14be81e..9164edc 100644 --- a/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java +++ b/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java @@ -27,6 +27,7 @@ package org.apache.slider.common.params; */ public interface Arguments { String ARG_ALL = "--all"; + String ARG_ALIAS = "--alias"; String ARG_APPLICATION = "--application"; String ARG_APP_HOME = "--apphome"; String ARG_BASE_PATH = "--basepath"; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/dd2a70bd/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AMClientCertStoreRetrievalIT.groovy ---------------------------------------------------------------------- diff --git a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AMClientCertStoreRetrievalIT.groovy b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AMClientCertStoreRetrievalIT.groovy index ab632d2..3163387 100644 --- a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AMClientCertStoreRetrievalIT.groovy +++ b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AMClientCertStoreRetrievalIT.groovy @@ -18,15 +18,14 @@ package org.apache.slider.funtest.lifecycle -import com.jcraft.jsch.Session import groovy.transform.CompileStatic import groovy.util.logging.Slf4j -import org.apache.bigtop.itest.shell.Shell -import org.apache.chaos.remote.RemoteServer -import org.apache.chaos.remote.SshCommands +import org.apache.hadoop.conf.Configuration +import org.apache.hadoop.fs.Path +import org.apache.hadoop.security.ProviderUtils import org.apache.hadoop.security.UserGroupInformation -import org.apache.hadoop.yarn.api.records.YarnApplicationState -import org.apache.hadoop.yarn.conf.YarnConfiguration +import org.apache.hadoop.security.alias.CredentialProvider +import org.apache.hadoop.security.alias.CredentialProviderFactory import org.apache.slider.common.SliderExitCodes import org.apache.slider.common.params.Arguments import org.apache.slider.common.params.SliderActions @@ -35,7 +34,6 @@ import org.apache.slider.funtest.framework.FuntestProperties import org.apache.slider.funtest.framework.SliderShell import org.junit.After import org.junit.Assert -import org.junit.BeforeClass import org.junit.Test import javax.net.ssl.TrustManager @@ -63,6 +61,21 @@ implements FuntestProperties, Arguments, SliderExitCodes, SliderActions { cleanup(APPLICATION_NAME) } + private static KeyStore loadKeystoreFromFile(String filename, + char[] password) { + FileInputStream is = null + try { + is = new FileInputStream(filename) + KeyStore keystore = KeyStore.getInstance("pkcs12") + keystore.load(is, password) + return keystore + } finally { + if (is != null) { + is.close() + } + } + } + @Test public void testRetrieveCertificateStores() throws Throwable { cleanup(APPLICATION_NAME) @@ -105,27 +118,9 @@ implements FuntestProperties, Arguments, SliderExitCodes, SliderActions { assert new File(filename).exists() - FileInputStream is = new FileInputStream(filename); - KeyStore keystore = KeyStore.getInstance("pkcs12"); - keystore.load(is, password.toCharArray()); - - Certificate certificate = keystore.getCertificate( - keystore.aliases().nextElement()); - Assert.assertNotNull(certificate); - - String hostname = InetAddress.localHost.canonicalHostName; - - if (certificate instanceof X509Certificate) { - X509Certificate x509cert = (X509Certificate) certificate; - - // Get subject - Principal principal = x509cert.getSubjectDN(); - String subjectDn = principal.getName(); - Assert.assertEquals("wrong DN", - "CN=" + hostname + ", OU=" + APPLICATION_NAME + ", OU=client", - subjectDn); + KeyStore keystore = loadKeystoreFromFile(filename, password.toCharArray()) - } + validateKeystore(keystore) filename = "/tmp/test.truststore" // ensure file doesn't exist @@ -142,15 +137,89 @@ implements FuntestProperties, Arguments, SliderExitCodes, SliderActions { assert new File(filename).exists() - is = new FileInputStream(filename); - KeyStore truststore = KeyStore.getInstance("pkcs12"); - truststore.load(is, password.toCharArray()); + KeyStore truststore = loadKeystoreFromFile(filename, password.toCharArray()) + + validateTruststore(keystore, truststore); + + // test retrieving using credential provider to provide password + filename = "/tmp/test.keystore" + String alias = "alias.for.password" + String providerString = "jceks://hdfs/user/" + + UserGroupInformation.getCurrentUser().getShortUserName() + "/test-" + + APPLICATION_NAME + ".jceks" + Path providerPath = ProviderUtils.unnestUri(new URI(providerString)) + + Configuration conf = loadSliderConf() + conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, providerString) + CredentialProvider provider = + CredentialProviderFactory.getProviders(conf).get(0) + provider.createCredentialEntry(alias, password.toCharArray()) + provider.flush() + assert clusterFS.exists(providerPath), "jks $providerString not created" + log.info("Created credential provider $providerString for test") + + // ensure file doesn't exist + new File(filename).delete(); + + shell = slider(EXIT_SUCCESS, + [ + ACTION_CLIENT, + ARG_GETCERTSTORE, + ARG_KEYSTORE, filename, + ARG_NAME, APPLICATION_NAME, + ARG_ALIAS, alias, + ARG_PROVIDER, providerString + ]) + + assert new File(filename).exists() + + keystore = loadKeystoreFromFile(filename, password.toCharArray()) + + validateKeystore(keystore) + + filename = "/tmp/test.truststore" + // ensure file doesn't exist + new File(filename).delete(); + + shell = slider(EXIT_SUCCESS, + [ + ACTION_CLIENT, + ARG_GETCERTSTORE, + ARG_TRUSTSTORE, filename, + ARG_NAME, APPLICATION_NAME, + ARG_ALIAS, alias, + ARG_PROVIDER, providerString + ]) + + assert new File(filename).exists() + + truststore = loadKeystoreFromFile(filename, password.toCharArray()) validateTruststore(keystore, truststore); } - private void validateTruststore(KeyStore keystore, KeyStore truststore) + private static void validateKeystore(KeyStore keystore) { + Certificate certificate = keystore.getCertificate( + keystore.aliases().nextElement()); + Assert.assertNotNull(certificate); + + String hostname = InetAddress.localHost.canonicalHostName; + + if (certificate instanceof X509Certificate) { + X509Certificate x509cert = (X509Certificate) certificate; + + // Get subject + Principal principal = x509cert.getSubjectDN(); + String subjectDn = principal.getName(); + Assert.assertEquals("wrong DN", + "CN=" + hostname + ", OU=" + APPLICATION_NAME + ", OU=client", + subjectDn); + + } + } + + private static void validateTruststore(KeyStore keystore, KeyStore truststore) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { // obtain server cert Certificate certificate = keystore.getCertificate(