You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by ae...@apache.org on 2016/05/13 18:00:55 UTC
[18/41] hadoop git commit: HADOOP-12942. hadoop credential commands
non-obviously use password of "none" (Mike Yoder via lmccay)
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 <lm...@hortonworks.com>
Authored: Wed May 11 14:30:07 2016 -0400
Committer: Larry McCay <lm...@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-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org