From 881c77107ea3a4f4d12c2ea5a809bf573f6718e7 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Mon, 1 Dec 2014 21:53:18 -0800 Subject: [PATCH] HADOOP-11341. KMS support for whitelist key ACLs. Contributed by Arun Suresh. (cherry picked from commit 31b4d2daa14a7f6e8ee73fd3160e126d8db62ffb) --- .../hadoop-common/CHANGES.txt | 2 + .../hadoop/crypto/key/kms/server/KMSACLs.java | 31 +++++++++++-- .../key/kms/server/KMSConfiguration.java | 1 + .../hadoop-kms/src/site/apt/index.apt.vm | 38 ++++++++++++++-- .../hadoop/crypto/key/kms/server/TestKMS.java | 43 +++++++++++++++++-- 5 files changed, 105 insertions(+), 10 deletions(-) diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index bd14149aee1..00212f66cd4 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -39,6 +39,8 @@ Release 2.7.0 - UNRELEASED HADOOP-11257: Update "hadoop jar" documentation to warn against using it for launching yarn jars (iwasakims via cmccabe) + HADOOP-11341. KMS support for whitelist key ACLs. (Arun Suresh via wang) + OPTIMIZATIONS HADOOP-11323. WritableComparator#compare keeps reference to byte array. diff --git a/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSACLs.java b/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSACLs.java index 530fe1102b4..0217589a4c0 100644 --- a/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSACLs.java +++ b/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSACLs.java @@ -73,6 +73,8 @@ public class KMSACLs implements Runnable, KeyACLs { private volatile Map> keyAcls; private final Map defaultKeyAcls = new HashMap(); + private final Map whitelistKeyAcls = + new HashMap(); private ScheduledExecutorService executorService; private long lastReload; @@ -151,11 +153,21 @@ public class KMSACLs implements Runnable, KeyACLs { String aclStr = conf.get(confKey); if (aclStr != null) { if (aclStr.equals("*")) { - LOG.info("Default Key ACL for KEY_OP '{}' is set to '*'", keyOp); + LOG.info("Default Key ACL for KEY_OP '{}' is set to '*'", keyOp); } defaultKeyAcls.put(keyOp, new AccessControlList(aclStr)); } } + if (!whitelistKeyAcls.containsKey(keyOp)) { + String confKey = KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + keyOp; + String aclStr = conf.get(confKey); + if (aclStr != null) { + if (aclStr.equals("*")) { + LOG.info("Whitelist Key ACL for KEY_OP '{}' is set to '*'", keyOp); + } + whitelistKeyAcls.put(keyOp, new AccessControlList(aclStr)); + } + } } } @@ -229,13 +241,23 @@ public class KMSACLs implements Runnable, KeyACLs { @Override public boolean hasAccessToKey(String keyName, UserGroupInformation ugi, KeyOpType opType) { + return checkKeyAccess(keyName, ugi, opType) + || checkKeyAccess(whitelistKeyAcls, ugi, opType); + } + + private boolean checkKeyAccess(String keyName, UserGroupInformation ugi, + KeyOpType opType) { Map keyAcl = keyAcls.get(keyName); if (keyAcl == null) { - // Get KeyAcl map of DEFAULT KEY. + // If No key acl defined for this key, check to see if + // there are key defaults configured for this operation keyAcl = defaultKeyAcls; } - // If No key acl defined for this key, check to see if - // there are key defaults configured for this operation + return checkKeyAccess(keyAcl, ugi, opType); + } + + private boolean checkKeyAccess(Map keyAcl, + UserGroupInformation ugi, KeyOpType opType) { AccessControlList acl = keyAcl.get(opType); if (acl == null) { // If no acl is specified for this operation, @@ -246,6 +268,7 @@ public class KMSACLs implements Runnable, KeyACLs { } } + @Override public boolean isACLPresent(String keyName, KeyOpType opType) { return (keyAcls.containsKey(keyName) || defaultKeyAcls.containsKey(opType)); diff --git a/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSConfiguration.java b/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSConfiguration.java index bd61ca7edf5..a67c68ee0a2 100644 --- a/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSConfiguration.java +++ b/hadoop-common-project/hadoop-kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSConfiguration.java @@ -39,6 +39,7 @@ public class KMSConfiguration { public static final String KEY_ACL_PREFIX = "key.acl."; public static final String DEFAULT_KEY_ACL_PREFIX = "default.key.acl."; + public static final String WHITELIST_KEY_ACL_PREFIX = "whitelist.key.acl."; // Property to set the backing KeyProvider public static final String KEY_PROVIDER_URI = CONFIG_PREFIX + diff --git a/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm b/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm index 76eb1a66c44..5d67b3bdecf 100644 --- a/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm +++ b/hadoop-common-project/hadoop-kms/src/site/apt/index.apt.vm @@ -467,10 +467,20 @@ $ keytool -genkey -alias tomcat -keyalg RSA is possible to configure a default key access control for a subset of the operation types. + It is also possible to configure a "whitelist" key ACL for a subset of the + operation types. The whitelist key ACL is a whitelist in addition to the + explicit or default per-key ACL. That is, if no per-key ACL is explicitly + set, a user will be granted access if they are present in the default per-key + ACL or the whitelist key ACL. If a per-key ACL is explicitly set, a user + will be granted access if they are present in the per-key ACL or the + whitelist key ACL. + If no ACL is configured for a specific key AND no default ACL is configured - for the requested operation, then access will be DENIED. + AND no root key ACL is configured for the requested operation, + then access will be DENIED. - <> The default ACL does not support <<>> operation qualifier. + <> The default and whitelist key ACL does not support <<>> + operation qualifier. +---+ @@ -491,7 +501,7 @@ $ keytool -genkey -alias tomcat -keyalg RSA key.acl.testKey3.DECRYPT_EEK - * + admink3 ACL for decryptEncryptedKey operations. @@ -514,6 +524,28 @@ $ keytool -genkey -alias tomcat -keyalg RSA + + whitelist.key.acl.MANAGEMENT + admin1 + + whitelist ACL for MANAGEMENT operations for all keys. + + + + + + whitelist.key.acl.DECRYPT_EEK + admin1 + + whitelist ACL for DECRYPT_EEK operations for all keys. + + + default.key.acl.MANAGEMENT user1,user2 diff --git a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java index 97901c851fd..d840646f449 100644 --- a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java +++ b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java @@ -617,13 +617,15 @@ public class TestKMS { for (KMSACLs.Type type : KMSACLs.Type.values()) { conf.set(type.getAclConfigKey(), type.toString()); } - conf.set(KMSACLs.Type.CREATE.getAclConfigKey(),"CREATE,ROLLOVER,GET,SET_KEY_MATERIAL,GENERATE_EEK"); - conf.set(KMSACLs.Type.ROLLOVER.getAclConfigKey(),"CREATE,ROLLOVER,GET,SET_KEY_MATERIAL,GENERATE_EEK"); + conf.set(KMSACLs.Type.CREATE.getAclConfigKey(),"CREATE,ROLLOVER,GET,SET_KEY_MATERIAL,GENERATE_EEK,DECRYPT_EEK"); + conf.set(KMSACLs.Type.ROLLOVER.getAclConfigKey(),"CREATE,ROLLOVER,GET,SET_KEY_MATERIAL,GENERATE_EEK,DECRYPT_EEK"); conf.set(KMSACLs.Type.GENERATE_EEK.getAclConfigKey(),"CREATE,ROLLOVER,GET,SET_KEY_MATERIAL,GENERATE_EEK"); conf.set(KMSACLs.Type.DECRYPT_EEK.getAclConfigKey(),"CREATE,ROLLOVER,GET,SET_KEY_MATERIAL,GENERATE_EEK"); - conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "test_key.MANAGEMENT", "CREATE"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "some_key.MANAGEMENT", "ROLLOVER"); + conf.set(KMSConfiguration.WHITELIST_KEY_ACL_PREFIX + "MANAGEMENT", "DECRYPT_EEK"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "all_access.ALL", "GENERATE_EEK"); conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "all_access.DECRYPT_EEK", "ROLLOVER"); conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "MANAGEMENT", "ROLLOVER"); @@ -676,6 +678,41 @@ public class TestKMS { } }); + // Test whitelist key access.. + // DECRYPT_EEK is whitelisted for MANAGEMENT operations only + doAs("DECRYPT_EEK", new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + KeyProvider kp = new KMSClientProvider(uri, conf); + try { + Options options = new KeyProvider.Options(conf); + Map attributes = options.getAttributes(); + HashMap newAttribs = new HashMap(attributes); + newAttribs.put("key.acl.name", "some_key"); + options.setAttributes(newAttribs); + KeyProvider.KeyVersion kv = kp.createKey("kk0", options); + Assert.assertNull(kv.getMaterial()); + KeyVersion rollVersion = kp.rollNewVersion("kk0"); + Assert.assertNull(rollVersion.getMaterial()); + KeyProviderCryptoExtension kpce = + KeyProviderCryptoExtension.createKeyProviderCryptoExtension(kp); + try { + kpce.generateEncryptedKey("kk0"); + Assert.fail("User [DECRYPT_EEK] should not be allowed to generate_eek on kk0"); + } catch (Exception e) { + // Ignore + } + newAttribs = new HashMap(attributes); + newAttribs.put("key.acl.name", "all_access"); + options.setAttributes(newAttribs); + kp.createKey("kkx", options); + } catch (Exception ex) { + Assert.fail(ex.getMessage()); + } + return null; + } + }); + doAs("ROLLOVER", new PrivilegedExceptionAction() { @Override public Void run() throws Exception {