HADOOP-12942. hadoop credential commands non-obviously use password of "none" (Mike Yoder via lmccay)
Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/acb509b2 Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/acb509b2 Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/acb509b2 Branch: refs/heads/HDFS-1312 Commit: acb509b2fa0bbe6e00f8a90aec37f63a09463afa Parents: dee279b Author: Larry McCay <lmc...@hortonworks.com> Authored: Wed May 11 14:30:07 2016 -0400 Committer: Larry McCay <lmc...@hortonworks.com> Committed: Wed May 11 14:30:07 2016 -0400 ---------------------------------------------------------------------- .../hadoop/crypto/key/JavaKeyStoreProvider.java | 90 ++++++++-- .../apache/hadoop/crypto/key/KeyProvider.java | 32 ++++ .../org/apache/hadoop/crypto/key/KeyShell.java | 128 ++++++++------ .../alias/AbstractJavaKeyStoreProvider.java | 150 ++++++++++++----- .../security/alias/CredentialProvider.java | 37 +++- .../hadoop/security/alias/CredentialShell.java | 168 +++++++++++-------- .../security/alias/JavaKeyStoreProvider.java | 6 +- .../alias/LocalJavaKeyStoreProvider.java | 4 +- .../src/site/markdown/CommandsManual.md | 18 +- .../crypto/key/TestKeyProviderFactory.java | 2 +- .../apache/hadoop/crypto/key/TestKeyShell.java | 32 +++- .../hadoop/security/alias/TestCredShell.java | 55 +++++- .../alias/TestCredentialProviderFactory.java | 5 +- 13 files changed, 516 insertions(+), 211 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/JavaKeyStoreProvider.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/JavaKeyStoreProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/JavaKeyStoreProvider.java index c6d60a3..c7c1779 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/JavaKeyStoreProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/JavaKeyStoreProvider.java @@ -44,6 +44,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.URI; import java.net.URL; +import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; @@ -88,7 +89,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; @InterfaceAudience.Private public class JavaKeyStoreProvider extends KeyProvider { private static final String KEY_METADATA = "KeyMetadata"; - private static Logger LOG = + private static final Logger LOG = LoggerFactory.getLogger(JavaKeyStoreProvider.class); public static final String SCHEME_NAME = "jceks"; @@ -103,8 +104,8 @@ public class JavaKeyStoreProvider extends KeyProvider { private final URI uri; private final Path path; private final FileSystem fs; - private final FsPermission permissions; - private final KeyStore keyStore; + private FsPermission permissions; + private KeyStore keyStore; private char[] password; private boolean changed = false; private Lock readLock; @@ -131,13 +132,28 @@ public class JavaKeyStoreProvider extends KeyProvider { this.uri = uri; path = ProviderUtils.unnestUri(uri); fs = path.getFileSystem(conf); + locateKeystore(); + ReadWriteLock lock = new ReentrantReadWriteLock(true); + readLock = lock.readLock(); + writeLock = lock.writeLock(); + } + + /** + * The password is either found in the environment or in a file. This + * routine implements the logic for locating the password in these + * locations. + * @return The password as a char []; null if not found. + * @throws IOException + */ + private char[] locatePassword() throws IOException { + char[] pass = null; // Get the password file from the conf, if not present from the user's // environment var if (System.getenv().containsKey(KEYSTORE_PASSWORD_ENV_VAR)) { - password = System.getenv(KEYSTORE_PASSWORD_ENV_VAR).toCharArray(); + pass = System.getenv(KEYSTORE_PASSWORD_ENV_VAR).toCharArray(); } - if (password == null) { - String pwFile = conf.get(KEYSTORE_PASSWORD_FILE_KEY); + if (pass == null) { + String pwFile = getConf().get(KEYSTORE_PASSWORD_FILE_KEY); if (pwFile != null) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); URL pwdFile = cl.getResource(pwFile); @@ -146,14 +162,23 @@ public class JavaKeyStoreProvider extends KeyProvider { throw new IOException("Password file does not exists"); } try (InputStream is = pwdFile.openStream()) { - password = IOUtils.toString(is).trim().toCharArray(); + pass = IOUtils.toString(is).trim().toCharArray(); } } } - if (password == null) { - password = KEYSTORE_PASSWORD_DEFAULT; - } + return pass; + } + + /** + * Open up and initialize the keyStore. + * @throws IOException + */ + private void locateKeystore() throws IOException { try { + password = locatePassword(); + if (password == null) { + password = KEYSTORE_PASSWORD_DEFAULT; + } Path oldPath = constructOldPath(path); Path newPath = constructNewPath(path); keyStore = KeyStore.getInstance(SCHEME_NAME); @@ -175,19 +200,14 @@ public class JavaKeyStoreProvider extends KeyProvider { permissions = perm; } catch (KeyStoreException e) { throw new IOException("Can't create keystore", e); - } catch (NoSuchAlgorithmException e) { - throw new IOException("Can't load keystore " + path, e); - } catch (CertificateException e) { + } catch (GeneralSecurityException e) { throw new IOException("Can't load keystore " + path, e); } - ReadWriteLock lock = new ReentrantReadWriteLock(true); - readLock = lock.readLock(); - writeLock = lock.writeLock(); } /** * Try loading from the user specified path, else load from the backup - * path in case Exception is not due to bad/wrong password + * path in case Exception is not due to bad/wrong password. * @param path Actual path to load from * @param backupPath Backup path (_OLD) * @return The permissions of the loaded file @@ -256,7 +276,7 @@ public class JavaKeyStoreProvider extends KeyProvider { if (perm == null) { keyStore.load(null, password); LOG.debug("KeyStore initialized anew successfully !!"); - perm = new FsPermission("700"); + perm = new FsPermission("600"); } return perm; } @@ -322,6 +342,40 @@ public class JavaKeyStoreProvider extends KeyProvider { } @Override + public boolean needsPassword() throws IOException { + return (null == locatePassword()); + } + + @VisibleForTesting + public static final String NO_PASSWORD_WARN = + "WARNING: You have accepted the use of the default provider password\n" + + "by not configuring a password in one of the two following locations:\n"; + public static final String NO_PASSWORD_ERROR = + "ERROR: The provider cannot find a password in the expected " + + "locations.\nPlease supply a password using one of the " + + "following two mechanisms:\n"; + @VisibleForTesting public static final String NO_PASSWORD_INSTRUCTIONS = + " o In the environment variable " + + KEYSTORE_PASSWORD_ENV_VAR + "\n" + + " o In a file referred to by the configuration entry\n" + + " " + KEYSTORE_PASSWORD_FILE_KEY + ".\n" + + "Please review the documentation regarding provider passwords at\n" + + "http://hadoop.apache.org/docs/current/hadoop-project-dist/" + + "hadoop-common/CredentialProviderAPI.html#Keystore_Passwords\n"; + @VisibleForTesting public static final String NO_PASSWORD_CONT = + "Continuing with the default provider password.\n"; + + @Override + public String noPasswordWarning() { + return NO_PASSWORD_WARN + NO_PASSWORD_INSTRUCTIONS + NO_PASSWORD_CONT; + } + + @Override + public String noPasswordError() { + return NO_PASSWORD_ERROR + NO_PASSWORD_INSTRUCTIONS; + } + + @Override public KeyVersion getKeyVersion(String versionName) throws IOException { readLock.lock(); try { http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java index a0675c2..9733e45 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java @@ -607,4 +607,36 @@ public abstract class KeyProvider { } throw new IOException("Can't find KeyProvider for key " + keyName); } + + /** + * Does this provider require a password? This means that a password is + * required for normal operation, and it has not been found through normal + * means. If true, the password should be provided by the caller using + * setPassword(). + * @return Whether or not the provider requires a password + * @throws IOException + */ + public boolean needsPassword() throws IOException { + return false; + } + + /** + * If a password for the provider is needed, but is not provided, this will + * return a warning and instructions for supplying said password to the + * provider. + * @return A warning and instructions for supplying the password + */ + public String noPasswordWarning() { + return null; + } + + /** + * If a password for the provider is needed, but is not provided, this will + * return an error message and instructions for supplying said password to + * the provider. + * @return An error message and instructions for supplying the password + */ + public String noPasswordError() { + return null; + } } http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java index c69dc82..fcc2105 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configured; import org.apache.hadoop.crypto.key.KeyProvider.Metadata; @@ -46,14 +47,22 @@ public class KeyShell extends Configured implements Tool { " [" + DeleteCommand.USAGE + "]\n" + " [" + ListCommand.USAGE + "]\n"; private static final String LIST_METADATA = "keyShell.list.metadata"; + @VisibleForTesting public static final String NO_VALID_PROVIDERS = + "There are no valid (non-transient) providers configured.\n" + + "No action has been taken. Use the -provider option to specify\n" + + "a provider. If you want to use a transient provider then you\n" + + "MUST use the -provider argument."; private boolean interactive = true; private Command command = null; - /** allows stdout to be captured if necessary */ - public PrintStream out = System.out; - /** allows stderr to be captured if necessary */ - public PrintStream err = System.err; + /** If true, fail if the provider requires a password and none is given. */ + private boolean strict = false; + + /** allows stdout to be captured if necessary. */ + @VisibleForTesting public PrintStream out = System.out; + /** allows stderr to be captured if necessary. */ + @VisibleForTesting public PrintStream err = System.err; private boolean userSuppliedProvider = false; @@ -76,7 +85,7 @@ public class KeyShell extends Configured implements Tool { return exitCode; } if (command.validate()) { - command.execute(); + command.execute(); } else { exitCode = 1; } @@ -88,7 +97,7 @@ public class KeyShell extends Configured implements Tool { } /** - * Parse the command line arguments and initialize the data + * Parse the command line arguments and initialize the data. * <pre> * % hadoop key create keyName [-size size] [-cipher algorithm] * [-provider providerPath] @@ -171,6 +180,8 @@ public class KeyShell extends Configured implements Tool { getConf().setBoolean(LIST_METADATA, true); } else if ("-f".equals(args[i]) || ("-force".equals(args[i]))) { interactive = false; + } else if (args[i].equals("-strict")) { + strict = true; } else if ("-help".equals(args[i])) { printKeyShellUsage(); return 1; @@ -199,7 +210,7 @@ public class KeyShell extends Configured implements Tool { out.println(command.getUsage()); } else { out.println("=========================================================" + - "======"); + "======"); out.println(CreateCommand.USAGE + ":\n\n" + CreateCommand.DESC); out.println("=========================================================" + "======"); @@ -221,16 +232,16 @@ public class KeyShell extends Configured implements Tool { } protected KeyProvider getKeyProvider() { - KeyProvider provider = null; + KeyProvider prov = null; List<KeyProvider> providers; try { providers = KeyProviderFactory.getProviders(getConf()); if (userSuppliedProvider) { - provider = providers.get(0); + prov = providers.get(0); } else { for (KeyProvider p : providers) { if (!p.isTransient()) { - provider = p; + prov = p; break; } } @@ -238,11 +249,14 @@ public class KeyShell extends Configured implements Tool { } catch (IOException e) { e.printStackTrace(err); } - return provider; + if (prov == null) { + out.println(NO_VALID_PROVIDERS); + } + return prov; } protected void printProviderWritten() { - out.println(provider + " has been updated."); + out.println(provider + " has been updated."); } protected void warnIfTransientProvider() { @@ -258,12 +272,13 @@ public class KeyShell extends Configured implements Tool { private class ListCommand extends Command { public static final String USAGE = - "list [-provider <provider>] [-metadata] [-help]"; + "list [-provider <provider>] [-strict] [-metadata] [-help]"; public static final String DESC = "The list subcommand displays the keynames contained within\n" + "a particular provider as configured in core-site.xml or\n" + "specified with the -provider argument. -metadata displays\n" + - "the metadata."; + "the metadata. If -strict is supplied, fail immediately if\n" + + "the provider requires a password and none is given."; private boolean metadata = false; @@ -271,10 +286,6 @@ public class KeyShell extends Configured implements Tool { boolean rc = true; provider = getKeyProvider(); if (provider == null) { - out.println("There are no non-transient KeyProviders configured.\n" - + "Use the -provider option to specify a provider. If you\n" - + "want to list a transient provider then you must use the\n" - + "-provider argument."); rc = false; } metadata = getConf().getBoolean(LIST_METADATA, false); @@ -310,12 +321,15 @@ public class KeyShell extends Configured implements Tool { } private class RollCommand extends Command { - public static final String USAGE = "roll <keyname> [-provider <provider>] [-help]"; + public static final String USAGE = + "roll <keyname> [-provider <provider>] [-strict] [-help]"; public static final String DESC = - "The roll subcommand creates a new version for the specified key\n" + - "within the provider indicated using the -provider argument\n"; + "The roll subcommand creates a new version for the specified key\n" + + "within the provider indicated using the -provider argument.\n" + + "If -strict is supplied, fail immediately if the provider requires\n" + + "a password and none is given."; - String keyName = null; + private String keyName = null; public RollCommand(String keyName) { this.keyName = keyName; @@ -325,14 +339,11 @@ public class KeyShell extends Configured implements Tool { boolean rc = true; provider = getKeyProvider(); if (provider == null) { - out.println("There are no valid KeyProviders configured. The key\n" + - "has not been rolled. Use the -provider option to specify\n" + - "a provider."); rc = false; } if (keyName == null) { out.println("Please provide a <keyname>.\n" + - "See the usage description by using -help."); + "See the usage description by using -help."); rc = false; } return rc; @@ -368,15 +379,17 @@ public class KeyShell extends Configured implements Tool { private class DeleteCommand extends Command { public static final String USAGE = - "delete <keyname> [-provider <provider>] [-f] [-help]"; + "delete <keyname> [-provider <provider>] [-strict] [-f] [-help]"; public static final String DESC = "The delete subcommand deletes all versions of the key\n" + "specified by the <keyname> argument from within the\n" + "provider specified by -provider. The command asks for\n" + - "user confirmation unless -f is specified."; + "user confirmation unless -f is specified. If -strict is\n" + + "supplied, fail immediately if the provider requires a\n" + + "password and none is given."; - String keyName = null; - boolean cont = true; + private String keyName = null; + private boolean cont = true; public DeleteCommand(String keyName) { this.keyName = keyName; @@ -386,8 +399,6 @@ public class KeyShell extends Configured implements Tool { public boolean validate() { provider = getKeyProvider(); if (provider == null) { - out.println("There are no valid KeyProviders configured. Nothing\n" - + "was deleted. Use the -provider option to specify a provider."); return false; } if (keyName == null) { @@ -438,22 +449,23 @@ public class KeyShell extends Configured implements Tool { private class CreateCommand extends Command { public static final String USAGE = - "create <keyname> [-cipher <cipher>] [-size <size>]\n" + - " [-description <description>]\n" + - " [-attr <attribute=value>]\n" + - " [-provider <provider>] [-help]"; + "create <keyname> [-cipher <cipher>] [-size <size>]\n" + + " [-description <description>]\n" + + " [-attr <attribute=value>]\n" + + " [-provider <provider>] [-strict]\n" + + " [-help]"; public static final String DESC = - "The create subcommand creates a new key for the name specified\n" + - "by the <keyname> argument within the provider specified by the\n" + - "-provider argument. You may specify a cipher with the -cipher\n" + - "argument. The default cipher is currently \"AES/CTR/NoPadding\".\n" + - "The default keysize is 128. You may specify the requested key\n" + - "length using the -size argument. Arbitrary attribute=value\n" + - "style attributes may be specified using the -attr argument.\n" + - "-attr may be specified multiple times, once per attribute.\n"; - - final String keyName; - final Options options; + "The create subcommand creates a new key for the name specified\n" + + "by the <keyname> argument within the provider specified by the\n" + + "-provider argument. You may specify a cipher with the -cipher\n" + + "argument. The default cipher is currently \"AES/CTR/NoPadding\".\n" + + "The default keysize is 128. You may specify the requested key\n" + + "length using the -size argument. Arbitrary attribute=value\n" + + "style attributes may be specified using the -attr argument.\n" + + "-attr may be specified multiple times, once per attribute.\n"; + + private final String keyName; + private final Options options; public CreateCommand(String keyName, Options options) { this.keyName = keyName; @@ -462,16 +474,24 @@ public class KeyShell extends Configured implements Tool { public boolean validate() { boolean rc = true; - provider = getKeyProvider(); - if (provider == null) { - out.println("There are no valid KeyProviders configured. No key\n" + - " was created. You can use the -provider option to specify\n" + - " a provider to use."); - rc = false; + try { + provider = getKeyProvider(); + if (provider == null) { + rc = false; + } else if (provider.needsPassword()) { + if (strict) { + out.println(provider.noPasswordError()); + rc = false; + } else { + out.println(provider.noPasswordWarning()); + } + } + } catch (IOException e) { + e.printStackTrace(err); } if (keyName == null) { out.println("Please provide a <keyname>. See the usage description" + - " with -help."); + " with -help."); rc = false; } return rc; http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/AbstractJavaKeyStoreProvider.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/AbstractJavaKeyStoreProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/AbstractJavaKeyStoreProvider.java index 9656261..895b715 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/AbstractJavaKeyStoreProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/AbstractJavaKeyStoreProvider.java @@ -18,6 +18,7 @@ package org.apache.hadoop.security.alias; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.io.IOUtils; @@ -34,6 +35,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URL; +import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -70,60 +72,28 @@ public abstract class AbstractJavaKeyStoreProvider extends CredentialProvider { private Path path; private final URI uri; - private final KeyStore keyStore; + private KeyStore keyStore; private char[] password = null; private boolean changed = false; private Lock readLock; private Lock writeLock; + private final Configuration conf; protected AbstractJavaKeyStoreProvider(URI uri, Configuration conf) throws IOException { this.uri = uri; - initFileSystem(uri, conf); - // Get the password from the user's environment - if (System.getenv().containsKey(CREDENTIAL_PASSWORD_NAME)) { - password = System.getenv(CREDENTIAL_PASSWORD_NAME).toCharArray(); - } - // if not in ENV get check for file - if (password == null) { - String pwFile = conf.get(KEYSTORE_PASSWORD_FILE_KEY); - if (pwFile != null) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - URL pwdFile = cl.getResource(pwFile); - if (pwdFile != null) { - try (InputStream is = pwdFile.openStream()) { - password = IOUtils.toString(is).trim().toCharArray(); - } - } - } - } - if (password == null) { - password = KEYSTORE_PASSWORD_DEFAULT.toCharArray(); - } - try { - keyStore = KeyStore.getInstance("jceks"); - if (keystoreExists()) { - stashOriginalFilePermissions(); - try (InputStream in = getInputStreamForFile()) { - keyStore.load(in, password); - } - } else { - createPermissions("700"); - // required to create an empty keystore. *sigh* - keyStore.load(null, password); - } - } catch (KeyStoreException e) { - throw new IOException("Can't create keystore", e); - } catch (NoSuchAlgorithmException e) { - throw new IOException("Can't load keystore " + getPathAsString(), e); - } catch (CertificateException e) { - throw new IOException("Can't load keystore " + getPathAsString(), e); - } + this.conf = conf; + initFileSystem(uri); + locateKeystore(); ReadWriteLock lock = new ReentrantReadWriteLock(true); readLock = lock.readLock(); writeLock = lock.writeLock(); } + protected Configuration getConf() { + return conf; + } + public Path getPath() { return path; } @@ -189,7 +159,7 @@ public abstract class AbstractJavaKeyStoreProvider extends CredentialProvider { protected abstract void stashOriginalFilePermissions() throws IOException; - protected void initFileSystem(URI keystoreUri, Configuration conf) + protected void initFileSystem(URI keystoreUri) throws IOException { path = ProviderUtils.unnestUri(keystoreUri); if (LOG.isDebugEnabled()) { @@ -332,6 +302,102 @@ public abstract class AbstractJavaKeyStoreProvider extends CredentialProvider { } } + /** + * The password is either found in the environment or in a file. This + * routine implements the logic for locating the password in these + * locations. + * + * @return The password as a char []; null if not found. + * @throws IOException + */ + private char[] locatePassword() throws IOException { + char[] pass = null; + if (System.getenv().containsKey(CREDENTIAL_PASSWORD_NAME)) { + pass = System.getenv(CREDENTIAL_PASSWORD_NAME).toCharArray(); + } + // if not in ENV get check for file + if (pass == null) { + String pwFile = conf.get(KEYSTORE_PASSWORD_FILE_KEY); + if (pwFile != null) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + URL pwdFile = cl.getResource(pwFile); + if (pwdFile != null) { + try (InputStream is = pwdFile.openStream()) { + pass = IOUtils.toString(is).trim().toCharArray(); + } + } + } + } + return pass; + } + + /** + * Open up and initialize the keyStore. + * + * @throws IOException + */ + private void locateKeystore() throws IOException { + try { + password = locatePassword(); + if (password == null) { + password = KEYSTORE_PASSWORD_DEFAULT.toCharArray(); + } + KeyStore ks; + ks = KeyStore.getInstance("jceks"); + if (keystoreExists()) { + stashOriginalFilePermissions(); + try (InputStream in = getInputStreamForFile()) { + ks.load(in, password); + } + } else { + createPermissions("600"); + // required to create an empty keystore. *sigh* + ks.load(null, password); + } + keyStore = ks; + } catch (KeyStoreException e) { + throw new IOException("Can't create keystore", e); + } catch (GeneralSecurityException e) { + throw new IOException("Can't load keystore " + getPathAsString(), e); + } + } + + @Override + public boolean needsPassword() throws IOException { + return (null == locatePassword()); + } + + @VisibleForTesting + public static final String NO_PASSWORD_WARN = + "WARNING: You have accepted the use of the default provider password\n" + + "by not configuring a password in one of the two following locations:\n"; + @VisibleForTesting + public static final String NO_PASSWORD_ERROR = + "ERROR: The provider cannot find a password in the expected " + + "locations.\nPlease supply a password using one of the " + + "following two mechanisms:\n"; + @VisibleForTesting + public static final String NO_PASSWORD_INSTRUCTIONS = + " o In the environment variable " + + CREDENTIAL_PASSWORD_NAME + "\n" + + " o In a file referred to by the configuration entry\n" + + " " + KEYSTORE_PASSWORD_FILE_KEY + ".\n" + + "Please review the documentation regarding provider passwords at\n" + + "http://hadoop.apache.org/docs/current/hadoop-project-dist/" + + "hadoop-common/CredentialProviderAPI.html#Keystore_Passwords\n"; + @VisibleForTesting public static final String NO_PASSWORD_CONT = + "Continuing with the default provider password.\n"; + + @Override + public String noPasswordWarning() { + return NO_PASSWORD_WARN + NO_PASSWORD_INSTRUCTIONS + NO_PASSWORD_CONT; + } + + @Override + public String noPasswordError() { + return NO_PASSWORD_ERROR + NO_PASSWORD_INSTRUCTIONS; + } + @Override public String toString() { return uri.toString(); http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialProvider.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialProvider.java index 63c1cb4..1a18ca1 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialProvider.java @@ -36,7 +36,7 @@ import org.apache.hadoop.classification.InterfaceStability; @InterfaceStability.Unstable public abstract class CredentialProvider { public static final String CLEAR_TEXT_FALLBACK - = "hadoop.security.credential.clear-text-fallback"; + = "hadoop.security.credential.clear-text-fallback"; /** * The combination of both the alias and the actual credential value. @@ -87,7 +87,8 @@ public abstract class CredentialProvider { } /** - * Ensures that any changes to the credentials are written to persistent store. + * Ensures that any changes to the credentials are written to persistent + * store. * @throws IOException */ public abstract void flush() throws IOException; @@ -123,4 +124,36 @@ public abstract class CredentialProvider { * @throws IOException */ public abstract void deleteCredentialEntry(String name) throws IOException; + + /** + * Does this provider require a password? This means that a password is + * required for normal operation, and it has not been found through normal + * means. If true, the password should be provided by the caller using + * setPassword(). + * @return Whether or not the provider requires a password + * @throws IOException + */ + public boolean needsPassword() throws IOException { + return false; + } + + /** + * If a password for the provider is needed, but is not provided, this will + * return a warning and instructions for supplying said password to the + * provider. + * @return A warning and instructions for supplying the password + */ + public String noPasswordWarning() { + return null; + } + + /** + * If a password for the provider is needed, but is not provided, this will + * return an error message and instructions for supplying said password to + * the provider. + * @return An error message and instructions for supplying the password + */ + public String noPasswordError() { + return null; + } } http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java index 265ed16..a047ac1 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/CredentialShell.java @@ -26,6 +26,7 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; +import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configured; import org.apache.hadoop.util.Tool; @@ -37,24 +38,34 @@ import org.apache.hadoop.util.ToolRunner; */ public class CredentialShell extends Configured implements Tool { final static private String USAGE_PREFIX = "Usage: hadoop credential " + - "[generic options]\n"; + "[generic options]\n"; final static private String COMMANDS = - " [--help]\n" + + " [-help]\n" + " [" + CreateCommand.USAGE + "]\n" + " [" + DeleteCommand.USAGE + "]\n" + " [" + ListCommand.USAGE + "]\n"; + @VisibleForTesting + public static final String NO_VALID_PROVIDERS = + "There are no valid (non-transient) providers configured.\n" + + "No action has been taken. Use the -provider option to specify\n" + + "a provider. If you want to use a transient provider then you\n" + + "MUST use the -provider argument."; private boolean interactive = true; private Command command = null; - /** allows stdout to be captured if necessary */ - public PrintStream out = System.out; - /** allows stderr to be captured if necessary */ - public PrintStream err = System.err; + /** If true, fail if the provider requires a password and none is given. */ + private boolean strict = false; + + /** Allows stdout to be captured if necessary. */ + @VisibleForTesting public PrintStream out = System.out; + /** Allows stderr to be captured if necessary. */ + @VisibleForTesting public PrintStream err = System.err; private boolean userSuppliedProvider = false; private String value = null; private PasswordReader passwordReader; + private boolean isHelp = false; @Override public int run(String[] args) throws Exception { @@ -64,10 +75,12 @@ public class CredentialShell extends Configured implements Tool { if (exitCode != 0) { return exitCode; } - if (command.validate()) { + if (!isHelp) { + if (command.validate()) { command.execute(); - } else { - exitCode = 1; + } else { + exitCode = 1; + } } } catch (Exception e) { e.printStackTrace(err); @@ -77,7 +90,7 @@ public class CredentialShell extends Configured implements Tool { } /** - * Parse the command line arguments and initialize the data + * Parse the command line arguments and initialize the data. * <pre> * % hadoop credential create alias [-provider providerPath] * % hadoop credential list [-provider providerPath] @@ -130,6 +143,8 @@ public class CredentialShell extends Configured implements Tool { args[++i]); } else if (args[i].equals("-f") || (args[i].equals("-force"))) { interactive = false; + } else if (args[i].equals("-strict")) { + strict = true; } else if (args[i].equals("-v") || (args[i].equals("-value"))) { value = args[++i]; } else if (args[i].equals("-help")) { @@ -145,13 +160,13 @@ public class CredentialShell extends Configured implements Tool { } private void printCredShellUsage() { + isHelp = true; out.println(USAGE_PREFIX + COMMANDS); if (command != null) { out.println(command.getUsage()); - } - else { + } else { out.println("=========================================================" + - "======"); + "======"); out.println(CreateCommand.USAGE + ":\n\n" + CreateCommand.DESC); out.println("=========================================================" + "======"); @@ -170,17 +185,16 @@ public class CredentialShell extends Configured implements Tool { } protected CredentialProvider getCredentialProvider() { - CredentialProvider provider = null; + CredentialProvider prov = null; List<CredentialProvider> providers; try { providers = CredentialProviderFactory.getProviders(getConf()); if (userSuppliedProvider) { - provider = providers.get(0); - } - else { + prov = providers.get(0); + } else { for (CredentialProvider p : providers) { if (!p.isTransient()) { - provider = p; + prov = p; break; } } @@ -188,11 +202,14 @@ public class CredentialShell extends Configured implements Tool { } catch (IOException e) { e.printStackTrace(err); } - return provider; + if (prov == null) { + out.println(NO_VALID_PROVIDERS); + } + return prov; } protected void printProviderWritten() { - out.println(provider.getClass().getName() + " has been updated."); + out.println("Provider " + provider.toString() + " has been updated."); } protected void warnIfTransientProvider() { @@ -207,35 +224,32 @@ public class CredentialShell extends Configured implements Tool { } private class ListCommand extends Command { - public static final String USAGE = "list [-provider provider-path]"; + public static final String USAGE = + "list [-provider provider-path] [-strict]"; public static final String DESC = "The list subcommand displays the aliases contained within \n" + - "a particular provider - as configured in core-site.xml or " + - "indicated\nthrough the -provider argument."; + "a particular provider - as configured in core-site.xml or\n" + + "indicated through the -provider argument. If -strict is supplied,\n" + + "fail immediately if the provider requires a password and none is\n" + + "provided."; public boolean validate() { - boolean rc = true; provider = getCredentialProvider(); - if (provider == null) { - out.println("There are no non-transient CredentialProviders configured.\n" - + "Consider using the -provider option to indicate the provider\n" - + "to use. If you want to list a transient provider then you\n" - + "you MUST use the -provider argument."); - rc = false; - } - return rc; + return (provider != null); } public void execute() throws IOException { List<String> aliases; try { aliases = provider.getAliases(); - out.println("Listing aliases for CredentialProvider: " + provider.toString()); + out.println("Listing aliases for CredentialProvider: " + + provider.toString()); for (String alias : aliases) { out.println(alias); } } catch (IOException e) { - out.println("Cannot list aliases for CredentialProvider: " + provider.toString() + out.println("Cannot list aliases for CredentialProvider: " + + provider.toString() + ": " + e.getMessage()); throw e; } @@ -249,15 +263,17 @@ public class CredentialShell extends Configured implements Tool { private class DeleteCommand extends Command { public static final String USAGE = - "delete <alias> [-f] [-provider provider-path]"; + "delete <alias> [-f] [-provider provider-path] [-strict]"; public static final String DESC = "The delete subcommand deletes the credential\n" + "specified as the <alias> argument from within the provider\n" + "indicated through the -provider argument. The command asks for\n" + - "confirmation unless the -f option is specified."; + "confirmation unless the -f option is specified. If -strict is\n" + + "supplied, fail immediately if the provider requires a password\n" + + "and none is given."; - String alias = null; - boolean cont = true; + private String alias = null; + private boolean cont = true; public DeleteCommand(String alias) { this.alias = alias; @@ -267,10 +283,6 @@ public class CredentialShell extends Configured implements Tool { public boolean validate() { provider = getCredentialProvider(); if (provider == null) { - out.println("There are no valid CredentialProviders configured.\n" - + "Nothing will be deleted.\n" - + "Consider using the -provider option to indicate the provider" - + " to use."); return false; } if (alias == null) { @@ -298,16 +310,17 @@ public class CredentialShell extends Configured implements Tool { public void execute() throws IOException { warnIfTransientProvider(); - out.println("Deleting credential: " + alias + " from CredentialProvider: " - + provider.toString()); + out.println("Deleting credential: " + alias + + " from CredentialProvider: " + provider.toString()); if (cont) { try { provider.deleteCredentialEntry(alias); - out.println(alias + " has been successfully deleted."); + out.println("Credential " + alias + + " has been successfully deleted."); provider.flush(); printProviderWritten(); } catch (IOException e) { - out.println(alias + " has NOT been deleted."); + out.println("Credential " + alias + " has NOT been deleted."); throw e; } } @@ -320,14 +333,17 @@ public class CredentialShell extends Configured implements Tool { } private class CreateCommand extends Command { - public static final String USAGE = - "create <alias> [-provider provider-path]"; + public static final String USAGE = "create <alias> [-value alias-value] " + + "[-provider provider-path] [-strict]"; public static final String DESC = - "The create subcommand creates a new credential for the name specified\n" + - "as the <alias> argument within the provider indicated through\n" + - "the -provider argument."; + "The create subcommand creates a new credential for the name\n" + + "specified as the <alias> argument within the provider indicated\n" + + "through the -provider argument. If -strict is supplied, fail\n" + + "immediately if the provider requires a password and none is given.\n" + + "If -value is provided, use that for the value of the credential\n" + + "instead of prompting the user."; - String alias = null; + private String alias = null; public CreateCommand(String alias) { this.alias = alias; @@ -335,13 +351,20 @@ public class CredentialShell extends Configured implements Tool { public boolean validate() { boolean rc = true; - provider = getCredentialProvider(); - if (provider == null) { - out.println("There are no valid CredentialProviders configured." + - "\nCredential will not be created.\n" - + "Consider using the -provider option to indicate the provider" + - " to use."); - rc = false; + try { + provider = getCredentialProvider(); + if (provider == null) { + rc = false; + } else if (provider.needsPassword()) { + if (strict) { + out.println(provider.noPasswordError()); + rc = false; + } else { + out.println(provider.noPasswordWarning()); + } + } + } catch (IOException e) { + e.printStackTrace(err); } if (alias == null) { out.println("There is no alias specified. Please provide the" + @@ -358,19 +381,20 @@ public class CredentialShell extends Configured implements Tool { if (value != null) { // testing only credential = value.toCharArray(); - } - else { - credential = promptForCredential(); + } else { + credential = promptForCredential(); } provider.createCredentialEntry(alias, credential); - out.println(alias + " has been successfully created."); provider.flush(); + out.println(alias + " has been successfully created."); printProviderWritten(); } catch (InvalidParameterException e) { - out.println(alias + " has NOT been created. " + e.getMessage()); + out.println("Credential " + alias + " has NOT been created. " + + e.getMessage()); throw e; } catch (IOException e) { - out.println(alias + " has NOT been created. " + e.getMessage()); + out.println("Credential " + alias + " has NOT been created. " + + e.getMessage()); throw e; } } @@ -391,16 +415,20 @@ public class CredentialShell extends Configured implements Tool { boolean noMatch; do { - char[] newPassword1 = c.readPassword("Enter password: "); - char[] newPassword2 = c.readPassword("Enter password again: "); + char[] newPassword1 = c.readPassword("Enter alias password: "); + char[] newPassword2 = c.readPassword("Enter alias password again: "); noMatch = !Arrays.equals(newPassword1, newPassword2); if (noMatch) { - if (newPassword1 != null) Arrays.fill(newPassword1, ' '); + if (newPassword1 != null) { + Arrays.fill(newPassword1, ' '); + } c.format("Passwords don't match. Try again.%n"); } else { cred = newPassword1; } - if (newPassword2 != null) Arrays.fill(newPassword2, ' '); + if (newPassword2 != null) { + Arrays.fill(newPassword2, ' '); + } } while (noMatch); return cred; } @@ -416,7 +444,7 @@ public class CredentialShell extends Configured implements Tool { passwordReader = reader; } - // to facilitate testing since Console is a final class... + /** To facilitate testing since Console is a final class. */ public static class PasswordReader { public char[] readPassword(String prompt) { Console console = System.console(); http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/JavaKeyStoreProvider.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/JavaKeyStoreProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/JavaKeyStoreProvider.java index d02ebde..52f39ef 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/JavaKeyStoreProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/JavaKeyStoreProvider.java @@ -83,10 +83,10 @@ public class JavaKeyStoreProvider extends AbstractJavaKeyStoreProvider { permissions = s.getPermission(); } - protected void initFileSystem(URI uri, Configuration conf) + protected void initFileSystem(URI uri) throws IOException { - super.initFileSystem(uri, conf); - fs = getPath().getFileSystem(conf); + super.initFileSystem(uri); + fs = getPath().getFileSystem(getConf()); } /** http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/LocalJavaKeyStoreProvider.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/LocalJavaKeyStoreProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/LocalJavaKeyStoreProvider.java index 61744c7..1891cd3 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/LocalJavaKeyStoreProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/alias/LocalJavaKeyStoreProvider.java @@ -121,9 +121,9 @@ public final class LocalJavaKeyStoreProvider extends } @Override - protected void initFileSystem(URI uri, Configuration conf) + protected void initFileSystem(URI uri) throws IOException { - super.initFileSystem(uri, conf); + super.initFileSystem(uri); try { file = new File(new URI(getPath().toString())); if (LOG.isDebugEnabled()) { http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md b/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md index fe3e7c1..178a504 100644 --- a/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md +++ b/hadoop-common-project/hadoop-common/src/site/markdown/CommandsManual.md @@ -104,9 +104,9 @@ Usage: `hadoop credential <subcommand> [options]` | COMMAND\_OPTION | Description | |:---- |:---- | -| create *alias* [-provider *provider-path*] | Prompts the user for a credential to be stored as the given alias. The *hadoop.security.credential.provider.path* within the core-site.xml file will be used unless a `-provider` is indicated. | -| delete *alias* [-provider *provider-path*] [-f] | Deletes the credential with the provided alias. The *hadoop.security.credential.provider.path* within the core-site.xml file will be used unless a `-provider` is indicated. The command asks for confirmation unless `-f` is specified | -| list [-provider *provider-path*] | Lists all of the credential aliases The *hadoop.security.credential.provider.path* within the core-site.xml file will be used unless a `-provider` is indicated. | +| create *alias* [-provider *provider-path*] [-strict] [-value *credential-value*] | Prompts the user for a credential to be stored as the given alias. The *hadoop.security.credential.provider.path* within the core-site.xml file will be used unless a `-provider` is indicated. The `-strict` flag will cause the command to fail if the provider uses a default password. Use `-value` flag to supply the credential value (a.k.a. the alias password) instead of being prompted. | +| delete *alias* [-provider *provider-path*] [-strict] [-f] | Deletes the credential with the provided alias. The *hadoop.security.credential.provider.path* within the core-site.xml file will be used unless a `-provider` is indicated. The `-strict` flag will cause the command to fail if the provider uses a default password. The command asks for confirmation unless `-f` is specified | +| list [-provider *provider-path*] [-strict] | Lists all of the credential aliases The *hadoop.security.credential.provider.path* within the core-site.xml file will be used unless a `-provider` is indicated. The `-strict` flag will cause the command to fail if the provider uses a default password. | Command to manage credentials, passwords and secrets within credential providers. @@ -116,6 +116,8 @@ indicates that the current user's credentials file should be consulted through t When utilizing the credential command it will often be for provisioning a password or secret to a particular credential store provider. In order to explicitly indicate which provider store to use the `-provider` option should be used. Otherwise, given a path of multiple providers, the first non-transient provider will be used. This may or may not be the one that you intended. +Providers frequently require that a password or other secret is supplied. If the provider requires a password and is unable to find one, it will use a default password and emit a warning message that the default password is being used. If the `-strict` flag is supplied, the warning message becomes an error message and the command returns immediately with an error status. + Example: `hadoop credential list -provider jceks://file/tmp/test.jceks` ### `distch` @@ -190,14 +192,16 @@ Usage: `hadoop key <subcommand> [options]` | COMMAND\_OPTION | Description | |:---- |:---- | -| create *keyname* [-cipher *cipher*] [-size *size*] [-description *description*] [-attr *attribute=value*] [-provider *provider*] [-help] | Creates a new key for the name specified by the *keyname* argument within the provider specified by the `-provider` argument. You may specify a cipher with the `-cipher` argument. The default cipher is currently "AES/CTR/NoPadding". The default keysize is 128. You may specify the requested key length using the `-size` argument. Arbitrary attribute=value style attributes may be specified using the `-attr` argument. `-attr` may be specified multiple times, once per attribute. | -| roll *keyname* [-provider *provider*] [-help] | Creates a new version for the specified key within the provider indicated using the `-provider` argument | -| delete *keyname* [-provider *provider*] [-f] [-help] | Deletes all versions of the key specified by the *keyname* argument from within the provider specified by `-provider`. The command asks for user confirmation unless `-f` is specified. | -| list [-provider *provider*] [-metadata] [-help] | Displays the keynames contained within a particular provider as configured in core-site.xml or specified with the `-provider` argument. `-metadata` displays the metadata. | +| create *keyname* [-cipher *cipher*] [-size *size*] [-description *description*] [-attr *attribute=value*] [-provider *provider*] [-strict] [-help] | Creates a new key for the name specified by the *keyname* argument within the provider specified by the `-provider` argument. The `-strict` flag will cause the command to fail if the provider uses a default password. You may specify a cipher with the `-cipher` argument. The default cipher is currently "AES/CTR/NoPadding". The default keysize is 128. You may specify the requested key length using the `-size` argument. Arbitrary attribute=value style attributes may be specified using the `-attr` argument. `-attr` may be specified multiple times, once per attribute. | +| roll *keyname* [-provider *provider*] [-strict] [-help] | Creates a new version for the specified key within the provider indicated using the `-provider` argument. The `-strict` flag will cause the command to fail if the provider uses a default password. | +| delete *keyname* [-provider *provider*] [-strict] [-f] [-help] | Deletes all versions of the key specified by the *keyname* argument from within the provider specified by `-provider`. The `-strict` flag will cause the command to fail if the provider uses a default password. The command asks for user confirmation unless `-f` is specified. | +| list [-provider *provider*] [-strict] [-metadata] [-help] | Displays the keynames contained within a particular provider as configured in core-site.xml or specified with the `-provider` argument. The `-strict` flag will cause the command to fail if the provider uses a default password. `-metadata` displays the metadata. | | -help | Prints usage of this command | Manage keys via the KeyProvider. For details on KeyProviders, see the [Transparent Encryption Guide](../hadoop-hdfs/TransparentEncryption.html). +Providers frequently require that a password or other secret is supplied. If the provider requires a password and is unable to find one, it will use a default password and emit a warning message that the default password is being used. If the `-strict` flag is supplied, the warning message becomes an error message and the command returns immediately with an error status. + NOTE: Some KeyProviders (e.g. org.apache.hadoop.crypto.key.JavaKeyStoreProvider) does not support uppercase key names. ### `trace` http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderFactory.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderFactory.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderFactory.java index ef09d94..53785bc 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderFactory.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderFactory.java @@ -267,7 +267,7 @@ public class TestKeyProviderFactory { Path path = ProviderUtils.unnestUri(new URI(ourUrl)); FileSystem fs = path.getFileSystem(conf); FileStatus s = fs.getFileStatus(path); - assertTrue(s.getPermission().toString().equals("rwx------")); + assertTrue(s.getPermission().toString().equals("rw-------")); assertTrue(file + " should exist", file.isFile()); // Corrupt file and Check if JKS can reload from _OLD file http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyShell.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyShell.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyShell.java index 9e4a9ad..eaf064e 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyShell.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyShell.java @@ -115,6 +115,12 @@ public class TestKeyShell { assertEquals(0, rc); assertTrue(outContent.toString().contains(keyName + " has been " + "successfully created")); + assertTrue(outContent.toString() + .contains(JavaKeyStoreProvider.NO_PASSWORD_WARN)); + assertTrue(outContent.toString() + .contains(JavaKeyStoreProvider.NO_PASSWORD_INSTRUCTIONS)); + assertTrue(outContent.toString() + .contains(JavaKeyStoreProvider.NO_PASSWORD_CONT)); String listOut = listKeys(ks, false); assertTrue(listOut.contains(keyName)); @@ -129,7 +135,7 @@ public class TestKeyShell { rc = ks.run(args2); assertEquals(0, rc); assertTrue(outContent.toString().contains("key1 has been successfully " + - "rolled.")); + "rolled.")); deleteKey(ks, keyName); @@ -192,8 +198,7 @@ public class TestKeyShell { ks.setConf(new Configuration()); rc = ks.run(args1); assertEquals(1, rc); - assertTrue(outContent.toString().contains("There are no valid " + - "KeyProviders configured.")); + assertTrue(outContent.toString().contains(KeyShell.NO_VALID_PROVIDERS)); } @Test @@ -207,7 +212,7 @@ public class TestKeyShell { rc = ks.run(args1); assertEquals(0, rc); assertTrue(outContent.toString().contains("WARNING: you are modifying a " + - "transient provider.")); + "transient provider.")); } @Test @@ -221,8 +226,23 @@ public class TestKeyShell { ks.setConf(config); rc = ks.run(args1); assertEquals(1, rc); - assertTrue(outContent.toString().contains("There are no valid " + - "KeyProviders configured.")); + assertTrue(outContent.toString().contains(KeyShell.NO_VALID_PROVIDERS)); + } + + @Test + public void testStrict() throws Exception { + outContent.reset(); + int rc = 0; + KeyShell ks = new KeyShell(); + ks.setConf(new Configuration()); + final String[] args1 = {"create", "hello", "-provider", jceksProvider, + "-strict"}; + rc = ks.run(args1); + assertEquals(1, rc); + assertTrue(outContent.toString() + .contains(JavaKeyStoreProvider.NO_PASSWORD_ERROR)); + assertTrue(outContent.toString() + .contains(JavaKeyStoreProvider.NO_PASSWORD_INSTRUCTIONS)); } @Test http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java index f4541fc..5ba7cf0 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredShell.java @@ -63,6 +63,12 @@ public class TestCredShell { assertEquals(outContent.toString(), 0, rc); assertTrue(outContent.toString().contains("credential1 has been successfully " + "created.")); + assertTrue(outContent.toString() + .contains(AbstractJavaKeyStoreProvider.NO_PASSWORD_WARN)); + assertTrue(outContent.toString() + .contains(AbstractJavaKeyStoreProvider.NO_PASSWORD_INSTRUCTIONS)); + assertTrue(outContent.toString() + .contains(AbstractJavaKeyStoreProvider.NO_PASSWORD_CONT)); outContent.reset(); String[] args2 = {"list", "-provider", @@ -97,8 +103,8 @@ public class TestCredShell { cs.setConf(new Configuration()); rc = cs.run(args1); assertEquals(1, rc); - assertTrue(outContent.toString().contains("There are no valid " + - "CredentialProviders configured.")); + assertTrue(outContent.toString().contains( + CredentialShell.NO_VALID_PROVIDERS)); } @Test @@ -132,8 +138,8 @@ public class TestCredShell { cs.setConf(config); rc = cs.run(args1); assertEquals(1, rc); - assertTrue(outContent.toString().contains("There are no valid " + - "CredentialProviders configured.")); + assertTrue(outContent.toString().contains( + CredentialShell.NO_VALID_PROVIDERS)); } @Test @@ -225,6 +231,47 @@ public class TestCredShell { assertEquals("Expected empty argument on " + cmd + " to return 1", 1, shell.init(new String[] { cmd })); } + } + @Test + public void testStrict() throws Exception { + outContent.reset(); + String[] args1 = {"create", "credential1", "-value", "p@ssw0rd", + "-provider", jceksProvider, "-strict"}; + int rc = 1; + CredentialShell cs = new CredentialShell(); + cs.setConf(new Configuration()); + rc = cs.run(args1); + assertEquals(outContent.toString(), 1, rc); + assertFalse(outContent.toString().contains("credential1 has been " + + "successfully created.")); + assertTrue(outContent.toString() + .contains(AbstractJavaKeyStoreProvider.NO_PASSWORD_ERROR)); + assertTrue(outContent.toString() + .contains(AbstractJavaKeyStoreProvider.NO_PASSWORD_INSTRUCTIONS)); + } + + @Test + public void testHelp() throws Exception { + outContent.reset(); + String[] args1 = {"-help"}; + int rc = 0; + CredentialShell cs = new CredentialShell(); + cs.setConf(new Configuration()); + rc = cs.run(args1); + assertEquals(outContent.toString(), 0, rc); + assertTrue(outContent.toString().contains("Usage")); + } + + @Test + public void testHelpCreate() throws Exception { + outContent.reset(); + String[] args1 = {"create", "-help"}; + int rc = 0; + CredentialShell cs = new CredentialShell(); + cs.setConf(new Configuration()); + rc = cs.run(args1); + assertEquals(outContent.toString(), 0, rc); + assertTrue(outContent.toString().contains("Usage")); } } http://git-wip-us.apache.org/repos/asf/hadoop/blob/acb509b2/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredentialProviderFactory.java ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredentialProviderFactory.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredentialProviderFactory.java index 567adbb..354dade 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredentialProviderFactory.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/alias/TestCredentialProviderFactory.java @@ -213,7 +213,7 @@ public class TestCredentialProviderFactory { Path path = ProviderUtils.unnestUri(new URI(ourUrl)); FileSystem fs = path.getFileSystem(conf); FileStatus s = fs.getFileStatus(path); - assertTrue(s.getPermission().toString().equals("rwx------")); + assertTrue(s.getPermission().toString().equals("rw-------")); assertTrue(file + " should exist", file.isFile()); // check permission retention after explicit change @@ -235,7 +235,8 @@ public class TestCredentialProviderFactory { Path path = ProviderUtils.unnestUri(new URI(ourUrl)); FileSystem fs = path.getFileSystem(conf); FileStatus s = fs.getFileStatus(path); - assertTrue("Unexpected permissions: " + s.getPermission().toString(), s.getPermission().toString().equals("rwx------")); + assertTrue("Unexpected permissions: " + s.getPermission().toString(), + s.getPermission().toString().equals("rw-------")); assertTrue(file + " should exist", file.isFile()); // check permission retention after explicit change --------------------------------------------------------------------- To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org For additional commands, e-mail: common-commits-h...@hadoop.apache.org