HADOOP-11341. KMS support for whitelist key ACLs. Contributed by Arun Suresh.
(cherry picked from commit 31b4d2daa1
)
This commit is contained in:
parent
f9c5dec152
commit
881c77107e
|
@ -39,6 +39,8 @@ Release 2.7.0 - UNRELEASED
|
||||||
HADOOP-11257: Update "hadoop jar" documentation to warn against using it
|
HADOOP-11257: Update "hadoop jar" documentation to warn against using it
|
||||||
for launching yarn jars (iwasakims via cmccabe)
|
for launching yarn jars (iwasakims via cmccabe)
|
||||||
|
|
||||||
|
HADOOP-11341. KMS support for whitelist key ACLs. (Arun Suresh via wang)
|
||||||
|
|
||||||
OPTIMIZATIONS
|
OPTIMIZATIONS
|
||||||
|
|
||||||
HADOOP-11323. WritableComparator#compare keeps reference to byte array.
|
HADOOP-11323. WritableComparator#compare keeps reference to byte array.
|
||||||
|
|
|
@ -73,6 +73,8 @@ public class KMSACLs implements Runnable, KeyACLs {
|
||||||
private volatile Map<String, HashMap<KeyOpType, AccessControlList>> keyAcls;
|
private volatile Map<String, HashMap<KeyOpType, AccessControlList>> keyAcls;
|
||||||
private final Map<KeyOpType, AccessControlList> defaultKeyAcls =
|
private final Map<KeyOpType, AccessControlList> defaultKeyAcls =
|
||||||
new HashMap<KeyOpType, AccessControlList>();
|
new HashMap<KeyOpType, AccessControlList>();
|
||||||
|
private final Map<KeyOpType, AccessControlList> whitelistKeyAcls =
|
||||||
|
new HashMap<KeyOpType, AccessControlList>();
|
||||||
private ScheduledExecutorService executorService;
|
private ScheduledExecutorService executorService;
|
||||||
private long lastReload;
|
private long lastReload;
|
||||||
|
|
||||||
|
@ -151,11 +153,21 @@ public class KMSACLs implements Runnable, KeyACLs {
|
||||||
String aclStr = conf.get(confKey);
|
String aclStr = conf.get(confKey);
|
||||||
if (aclStr != null) {
|
if (aclStr != null) {
|
||||||
if (aclStr.equals("*")) {
|
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));
|
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
|
@Override
|
||||||
public boolean hasAccessToKey(String keyName, UserGroupInformation ugi,
|
public boolean hasAccessToKey(String keyName, UserGroupInformation ugi,
|
||||||
KeyOpType opType) {
|
KeyOpType opType) {
|
||||||
|
return checkKeyAccess(keyName, ugi, opType)
|
||||||
|
|| checkKeyAccess(whitelistKeyAcls, ugi, opType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkKeyAccess(String keyName, UserGroupInformation ugi,
|
||||||
|
KeyOpType opType) {
|
||||||
Map<KeyOpType, AccessControlList> keyAcl = keyAcls.get(keyName);
|
Map<KeyOpType, AccessControlList> keyAcl = keyAcls.get(keyName);
|
||||||
if (keyAcl == null) {
|
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;
|
keyAcl = defaultKeyAcls;
|
||||||
}
|
}
|
||||||
// If No key acl defined for this key, check to see if
|
return checkKeyAccess(keyAcl, ugi, opType);
|
||||||
// there are key defaults configured for this operation
|
}
|
||||||
|
|
||||||
|
private boolean checkKeyAccess(Map<KeyOpType, AccessControlList> keyAcl,
|
||||||
|
UserGroupInformation ugi, KeyOpType opType) {
|
||||||
AccessControlList acl = keyAcl.get(opType);
|
AccessControlList acl = keyAcl.get(opType);
|
||||||
if (acl == null) {
|
if (acl == null) {
|
||||||
// If no acl is specified for this operation,
|
// If no acl is specified for this operation,
|
||||||
|
@ -246,6 +268,7 @@ public class KMSACLs implements Runnable, KeyACLs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isACLPresent(String keyName, KeyOpType opType) {
|
public boolean isACLPresent(String keyName, KeyOpType opType) {
|
||||||
return (keyAcls.containsKey(keyName) || defaultKeyAcls.containsKey(opType));
|
return (keyAcls.containsKey(keyName) || defaultKeyAcls.containsKey(opType));
|
||||||
|
|
|
@ -39,6 +39,7 @@ public class KMSConfiguration {
|
||||||
|
|
||||||
public static final String KEY_ACL_PREFIX = "key.acl.";
|
public static final String KEY_ACL_PREFIX = "key.acl.";
|
||||||
public static final String DEFAULT_KEY_ACL_PREFIX = "default.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
|
// Property to set the backing KeyProvider
|
||||||
public static final String KEY_PROVIDER_URI = CONFIG_PREFIX +
|
public static final String KEY_PROVIDER_URI = CONFIG_PREFIX +
|
||||||
|
|
|
@ -467,10 +467,20 @@ $ keytool -genkey -alias tomcat -keyalg RSA
|
||||||
is possible to configure a default key access control for a subset of the
|
is possible to configure a default key access control for a subset of the
|
||||||
operation types.
|
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
|
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.
|
||||||
|
|
||||||
<<NOTE:>> The default ACL does not support <<<ALL>>> operation qualifier.
|
<<NOTE:>> The default and whitelist key ACL does not support <<<ALL>>>
|
||||||
|
operation qualifier.
|
||||||
|
|
||||||
+---+
|
+---+
|
||||||
<property>
|
<property>
|
||||||
|
@ -491,7 +501,7 @@ $ keytool -genkey -alias tomcat -keyalg RSA
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
<name>key.acl.testKey3.DECRYPT_EEK</name>
|
<name>key.acl.testKey3.DECRYPT_EEK</name>
|
||||||
<value>*</value>
|
<value>admink3</value>
|
||||||
<description>
|
<description>
|
||||||
ACL for decryptEncryptedKey operations.
|
ACL for decryptEncryptedKey operations.
|
||||||
</description>
|
</description>
|
||||||
|
@ -514,6 +524,28 @@ $ keytool -genkey -alias tomcat -keyalg RSA
|
||||||
</description>
|
</description>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>whitelist.key.acl.MANAGEMENT</name>
|
||||||
|
<value>admin1</value>
|
||||||
|
<description>
|
||||||
|
whitelist ACL for MANAGEMENT operations for all keys.
|
||||||
|
</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
'testKey3' key ACL is defined. Since a 'whitelist'
|
||||||
|
key is also defined for DECRYPT_EEK, in addition to
|
||||||
|
admink3, admin1 can also perform DECRYPT_EEK operations
|
||||||
|
on 'testKey3'
|
||||||
|
-->
|
||||||
|
<property>
|
||||||
|
<name>whitelist.key.acl.DECRYPT_EEK</name>
|
||||||
|
<value>admin1</value>
|
||||||
|
<description>
|
||||||
|
whitelist ACL for DECRYPT_EEK operations for all keys.
|
||||||
|
</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
<name>default.key.acl.MANAGEMENT</name>
|
<name>default.key.acl.MANAGEMENT</name>
|
||||||
<value>user1,user2</value>
|
<value>user1,user2</value>
|
||||||
|
|
|
@ -617,13 +617,15 @@ public class TestKMS {
|
||||||
for (KMSACLs.Type type : KMSACLs.Type.values()) {
|
for (KMSACLs.Type type : KMSACLs.Type.values()) {
|
||||||
conf.set(type.getAclConfigKey(), type.toString());
|
conf.set(type.getAclConfigKey(), type.toString());
|
||||||
}
|
}
|
||||||
conf.set(KMSACLs.Type.CREATE.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");
|
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.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(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 + "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.ALL", "GENERATE_EEK");
|
||||||
conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "all_access.DECRYPT_EEK", "ROLLOVER");
|
conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "all_access.DECRYPT_EEK", "ROLLOVER");
|
||||||
conf.set(KMSConfiguration.DEFAULT_KEY_ACL_PREFIX + "MANAGEMENT", "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<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void run() throws Exception {
|
||||||
|
KeyProvider kp = new KMSClientProvider(uri, conf);
|
||||||
|
try {
|
||||||
|
Options options = new KeyProvider.Options(conf);
|
||||||
|
Map<String, String> attributes = options.getAttributes();
|
||||||
|
HashMap<String,String> newAttribs = new HashMap<String, String>(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<String, String>(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<Void>() {
|
doAs("ROLLOVER", new PrivilegedExceptionAction<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void run() throws Exception {
|
public Void run() throws Exception {
|
||||||
|
|
Loading…
Reference in New Issue