HDFS-13193. Various Improvements for BlockTokenSecretManager. Contributed by BELUGA BEHR.
This commit is contained in:
parent
28f644bf25
commit
225d877092
|
@ -22,18 +22,17 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.fs.StorageType;
|
||||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||||
import org.apache.hadoop.hdfs.protocol.datatransfer.InvalidEncryptionKeyException;
|
import org.apache.hadoop.hdfs.protocol.datatransfer.InvalidEncryptionKeyException;
|
||||||
import org.apache.hadoop.io.WritableUtils;
|
import org.apache.hadoop.io.WritableUtils;
|
||||||
|
@ -41,11 +40,12 @@ import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.security.token.SecretManager;
|
import org.apache.hadoop.security.token.SecretManager;
|
||||||
import org.apache.hadoop.security.token.Token;
|
import org.apache.hadoop.security.token.Token;
|
||||||
import org.apache.hadoop.util.Time;
|
import org.apache.hadoop.util.Time;
|
||||||
|
import org.apache.hadoop.util.Timer;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.apache.hadoop.fs.StorageType;
|
import com.google.common.collect.HashMultiset;
|
||||||
import org.apache.hadoop.util.Timer;
|
import com.google.common.collect.Multiset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BlockTokenSecretManager can be instantiated in 2 modes, master mode
|
* BlockTokenSecretManager can be instantiated in 2 modes, master mode
|
||||||
|
@ -153,8 +153,9 @@ public class BlockTokenSecretManager extends
|
||||||
|
|
||||||
/** Initialize block keys */
|
/** Initialize block keys */
|
||||||
private synchronized void generateKeys() {
|
private synchronized void generateKeys() {
|
||||||
if (!isMaster)
|
if (!isMaster) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Need to set estimated expiry dates for currentKey and nextKey so that if
|
* Need to set estimated expiry dates for currentKey and nextKey so that if
|
||||||
* NN crashes, DN can still expire those keys. NN will stop using the newly
|
* NN crashes, DN can still expire those keys. NN will stop using the newly
|
||||||
|
@ -179,9 +180,9 @@ public class BlockTokenSecretManager extends
|
||||||
|
|
||||||
/** Export block keys, only to be used in master mode */
|
/** Export block keys, only to be used in master mode */
|
||||||
public synchronized ExportedBlockKeys exportKeys() {
|
public synchronized ExportedBlockKeys exportKeys() {
|
||||||
if (!isMaster)
|
if (!isMaster) {
|
||||||
return null;
|
return null;
|
||||||
if (LOG.isDebugEnabled())
|
}
|
||||||
LOG.debug("Exporting access keys");
|
LOG.debug("Exporting access keys");
|
||||||
return new ExportedBlockKeys(true, keyUpdateInterval, tokenLifetime,
|
return new ExportedBlockKeys(true, keyUpdateInterval, tokenLifetime,
|
||||||
currentKey, allKeys.values().toArray(new BlockKey[0]));
|
currentKey, allKeys.values().toArray(new BlockKey[0]));
|
||||||
|
@ -203,18 +204,19 @@ public class BlockTokenSecretManager extends
|
||||||
*/
|
*/
|
||||||
public synchronized void addKeys(ExportedBlockKeys exportedKeys)
|
public synchronized void addKeys(ExportedBlockKeys exportedKeys)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (isMaster || exportedKeys == null)
|
if (isMaster || exportedKeys == null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
LOG.info("Setting block keys");
|
LOG.info("Setting block keys");
|
||||||
removeExpiredKeys();
|
removeExpiredKeys();
|
||||||
this.currentKey = exportedKeys.getCurrentKey();
|
this.currentKey = exportedKeys.getCurrentKey();
|
||||||
BlockKey[] receivedKeys = exportedKeys.getAllKeys();
|
BlockKey[] receivedKeys = exportedKeys.getAllKeys();
|
||||||
for (int i = 0; i < receivedKeys.length; i++) {
|
for (int i = 0; i < receivedKeys.length; i++) {
|
||||||
if (receivedKeys[i] == null)
|
if (receivedKeys[i] != null) {
|
||||||
continue;
|
|
||||||
this.allKeys.put(receivedKeys[i].getKeyId(), receivedKeys[i]);
|
this.allKeys.put(receivedKeys[i].getKeyId(), receivedKeys[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update block keys if update time > update interval.
|
* Update block keys if update time > update interval.
|
||||||
|
@ -231,8 +233,9 @@ public class BlockTokenSecretManager extends
|
||||||
* Update block keys, only to be used in master mode
|
* Update block keys, only to be used in master mode
|
||||||
*/
|
*/
|
||||||
synchronized boolean updateKeys() throws IOException {
|
synchronized boolean updateKeys() throws IOException {
|
||||||
if (!isMaster)
|
if (!isMaster) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
LOG.info("Updating block keys");
|
LOG.info("Updating block keys");
|
||||||
removeExpiredKeys();
|
removeExpiredKeys();
|
||||||
|
@ -283,10 +286,10 @@ public class BlockTokenSecretManager extends
|
||||||
ExtendedBlock block, BlockTokenIdentifier.AccessMode mode,
|
ExtendedBlock block, BlockTokenIdentifier.AccessMode mode,
|
||||||
StorageType[] storageTypes, String[] storageIds) throws InvalidToken {
|
StorageType[] storageTypes, String[] storageIds) throws InvalidToken {
|
||||||
checkAccess(id, userId, block, mode);
|
checkAccess(id, userId, block, mode);
|
||||||
if (storageTypes != null && storageTypes.length > 0) {
|
if (ArrayUtils.isNotEmpty(storageTypes)) {
|
||||||
checkAccess(id.getStorageTypes(), storageTypes, "StorageTypes");
|
checkAccess(id.getStorageTypes(), storageTypes, "StorageTypes");
|
||||||
}
|
}
|
||||||
if (storageIds != null && storageIds.length > 0) {
|
if (ArrayUtils.isNotEmpty(storageIds)) {
|
||||||
checkAccess(id.getStorageIds(), storageIds, "StorageIDs");
|
checkAccess(id.getStorageIds(), storageIds, "StorageIDs");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,26 +299,26 @@ public class BlockTokenSecretManager extends
|
||||||
throws InvalidToken {
|
throws InvalidToken {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Checking access for user=" + userId + ", block=" + block
|
LOG.debug("Checking access for user=" + userId + ", block=" + block
|
||||||
+ ", access mode=" + mode + " using " + id.toString());
|
+ ", access mode=" + mode + " using " + id);
|
||||||
}
|
}
|
||||||
if (userId != null && !userId.equals(id.getUserId())) {
|
if (userId != null && !userId.equals(id.getUserId())) {
|
||||||
throw new InvalidToken("Block token with " + id.toString()
|
throw new InvalidToken("Block token with " + id
|
||||||
+ " doesn't belong to user " + userId);
|
+ " doesn't belong to user " + userId);
|
||||||
}
|
}
|
||||||
if (!id.getBlockPoolId().equals(block.getBlockPoolId())) {
|
if (!id.getBlockPoolId().equals(block.getBlockPoolId())) {
|
||||||
throw new InvalidToken("Block token with " + id.toString()
|
throw new InvalidToken("Block token with " + id
|
||||||
+ " doesn't apply to block " + block);
|
+ " doesn't apply to block " + block);
|
||||||
}
|
}
|
||||||
if (id.getBlockId() != block.getBlockId()) {
|
if (id.getBlockId() != block.getBlockId()) {
|
||||||
throw new InvalidToken("Block token with " + id.toString()
|
throw new InvalidToken("Block token with " + id
|
||||||
+ " doesn't apply to block " + block);
|
+ " doesn't apply to block " + block);
|
||||||
}
|
}
|
||||||
if (isExpired(id.getExpiryDate())) {
|
if (isExpired(id.getExpiryDate())) {
|
||||||
throw new InvalidToken("Block token with " + id.toString()
|
throw new InvalidToken("Block token with " + id
|
||||||
+ " is expired.");
|
+ " is expired.");
|
||||||
}
|
}
|
||||||
if (!id.getAccessModes().contains(mode)) {
|
if (!id.getAccessModes().contains(mode)) {
|
||||||
throw new InvalidToken("Block token with " + id.toString()
|
throw new InvalidToken("Block token with " + id
|
||||||
+ " doesn't have " + mode + " permission");
|
+ " doesn't have " + mode + " permission");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,26 +332,23 @@ public class BlockTokenSecretManager extends
|
||||||
*/
|
*/
|
||||||
public static <T> void checkAccess(T[] candidates, T[] requested, String msg)
|
public static <T> void checkAccess(T[] candidates, T[] requested, String msg)
|
||||||
throws InvalidToken {
|
throws InvalidToken {
|
||||||
if (requested.length == 0) {
|
if (ArrayUtils.isEmpty(requested)) {
|
||||||
throw new InvalidToken("The request has no " + msg + ". "
|
throw new InvalidToken("The request has no " + msg + ". "
|
||||||
+ "This is probably a configuration error.");
|
+ "This is probably a configuration error.");
|
||||||
}
|
}
|
||||||
if (candidates.length == 0) {
|
if (ArrayUtils.isEmpty(candidates)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List unseenCandidates = new ArrayList<T>();
|
Multiset<T> c = HashMultiset.create(Arrays.asList(candidates));
|
||||||
unseenCandidates.addAll(Arrays.asList(candidates));
|
|
||||||
for (T req : requested) {
|
for (T req : requested) {
|
||||||
final int index = unseenCandidates.indexOf(req);
|
if (!c.remove(req)) {
|
||||||
if (index == -1) {
|
|
||||||
throw new InvalidToken("Block token with " + msg + " "
|
throw new InvalidToken("Block token with " + msg + " "
|
||||||
+ Arrays.toString(candidates)
|
+ Arrays.toString(candidates)
|
||||||
+ " not valid for access with " + msg + " "
|
+ " not valid for access with " + msg + " "
|
||||||
+ Arrays.toString(requested));
|
+ Arrays.toString(requested));
|
||||||
}
|
}
|
||||||
Collections.swap(unseenCandidates, index, unseenCandidates.size()-1);
|
|
||||||
unseenCandidates.remove(unseenCandidates.size()-1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +367,7 @@ public class BlockTokenSecretManager extends
|
||||||
}
|
}
|
||||||
checkAccess(id, userId, block, mode, storageTypes, storageIds);
|
checkAccess(id, userId, block, mode, storageTypes, storageIds);
|
||||||
if (!Arrays.equals(retrievePassword(id), token.getPassword())) {
|
if (!Arrays.equals(retrievePassword(id), token.getPassword())) {
|
||||||
throw new InvalidToken("Block token with " + id.toString()
|
throw new InvalidToken("Block token with " + id
|
||||||
+ " doesn't have the correct token password");
|
+ " doesn't have the correct token password");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,12 +416,13 @@ public class BlockTokenSecretManager extends
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
key = currentKey;
|
key = currentKey;
|
||||||
}
|
}
|
||||||
if (key == null)
|
if (key == null) {
|
||||||
throw new IllegalStateException("currentKey hasn't been initialized.");
|
throw new IllegalStateException("currentKey hasn't been initialized.");
|
||||||
|
}
|
||||||
identifier.setExpiryDate(timer.now() + tokenLifetime);
|
identifier.setExpiryDate(timer.now() + tokenLifetime);
|
||||||
identifier.setKeyId(key.getKeyId());
|
identifier.setKeyId(key.getKeyId());
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Generating block token for " + identifier.toString());
|
LOG.debug("Generating block token for " + identifier);
|
||||||
}
|
}
|
||||||
return createPassword(identifier.getBytes(), key.getKey());
|
return createPassword(identifier.getBytes(), key.getKey());
|
||||||
}
|
}
|
||||||
|
@ -438,7 +439,7 @@ public class BlockTokenSecretManager extends
|
||||||
public byte[] retrievePassword(BlockTokenIdentifier identifier)
|
public byte[] retrievePassword(BlockTokenIdentifier identifier)
|
||||||
throws InvalidToken {
|
throws InvalidToken {
|
||||||
if (isExpired(identifier.getExpiryDate())) {
|
if (isExpired(identifier.getExpiryDate())) {
|
||||||
throw new InvalidToken("Block token with " + identifier.toString()
|
throw new InvalidToken("Block token with " + identifier
|
||||||
+ " is expired.");
|
+ " is expired.");
|
||||||
}
|
}
|
||||||
BlockKey key = null;
|
BlockKey key = null;
|
||||||
|
@ -447,7 +448,7 @@ public class BlockTokenSecretManager extends
|
||||||
}
|
}
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new InvalidToken("Can't re-compute password for "
|
throw new InvalidToken("Can't re-compute password for "
|
||||||
+ identifier.toString() + ", since the required block key (keyID="
|
+ identifier + ", since the required block key (keyID="
|
||||||
+ identifier.getKeyId() + ") doesn't exist.");
|
+ identifier.getKeyId() + ") doesn't exist.");
|
||||||
}
|
}
|
||||||
return createPassword(identifier.getBytes(), key.getKey());
|
return createPassword(identifier.getBytes(), key.getKey());
|
||||||
|
|
Loading…
Reference in New Issue