前言
数据的安全性是一直被大家所重视的.对于一个存有大规模数据量的成熟企业来说,如何做到数据不丢失,不损坏,不窃取就显得格外重要了.而HDFS恰恰满足了”海量数据规模”的特点,所以如果我们用HDFS存储大量的非结构化的数据,我们如何保证其中数据的安全性呢?在之前的文章中,有提到过一个”Encryption Zone”数据加密空间的概念.Encryption Zone可以保证用户在指定的加密空间路径下,数据是被加/解密的,而且对于用户来说完全透明.详细信息可点击HDFS数据加密空间.但是其实再仔细分析数据加密空间的原理,你会发现他其实是一个局部数据安全处理的过程.因为他有一个路径空间的限制.而且我要每次创建我想要加密的特定路径.那么在HDFS中是否还有其他的安全认证机制,可以弥补这点,做到全局的数据验证呢?本文就带大家了解HDFS中2大验证体系BlockToken验证和Sasl认证.
BlockToken验证
首先介绍的验证机制是BlockToken验证,因为相比较而言,BlockToken验证比Sasl确实也要简单一些.而且BlockToken在Sasl中也会被用到.细看BlockToken这个词,可以被拆分为2个词,Block,Token.我们可以得出以下2点关键信息.
1.BlockToken是针对Block块级别的验证.
2.Token的意思是令牌的意思,基本是用做访问时的验证.
OK,大体有了这么一个理解之后,继续来看一下BlockToken如何做具体的验证的,要解决这个问题,需要弄清楚下面3个问题:
- BlockToken如何生成的?
- BlockToken在哪里被验证?
- BlockToken如何被验证的?
在下面的阐述中,将会一一揭开这些答案.
BlockToken的结构分析
BlockToken的体系结构分析可以帮助我们了解他是如何产生的.如果你仔细观察,查找的话,应该很容易就找到相关类,就是BlockPoolTokenSecretManager.BlockToken就是由这个类调用产生的.但是真正产生BlockToken的操作其实是由其存储的BlockTokenSecretManager做的.所以这里就有一层关系:
BlockPoolTokenSecretManager包含BlockTokenSecretManager,并且每一个blockPool对应一个BlockTokenSecretManager
最终是如下的map存储关系.
private final Map<String, BlockTokenSecretManager> map =
new HashMap<String, BlockTokenSecretManager>();
可能会有人有疑问了,为什么按照BlockPool分出这么多的BlockTokenSecretManager,全局维护一个Manager不是更好吗?我的个人看法是HDFS这么做还是想做隔离,blockPool是在每次NameNode做format时产生的,代表着独立的存储空间.所有的Block在各自所属的BlockPool下是全局唯一的.HDFS中是可以有多个BlockPool的..回到前面说的过程,重新来看一下BlockToken的生成调用过程.
/**
* See {@link BlockTokenSecretManager#generateToken(ExtendedBlock, EnumSet)}
*/
public Token<BlockTokenIdentifier> generateToken(ExtendedBlock b,
EnumSet<AccessMode> of) throws IOException {
// 选择block所属的BlockPool去生成Token
return get(b.getBlockPoolId()).generateToken(b, of);
}
然后进行实际调用方法
/** Generate an block token for current user */
public Token<BlockTokenIdentifier> generateToken(ExtendedBlock block,
EnumSet<BlockTokenIdentifier.AccessMode> modes) throws IOException {
UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
String userID = (ugi == null ? null : ugi.getShortUserName());
return generateToken(userID, block, modes);
}
/** Generate a block token for a specified user */
public Token<BlockTokenIdentifier> generateToken(String userId,
ExtendedBlock block, EnumSet<BlockTokenIdentifier.AccessMode> modes) throws IOException {
// 将block相关信息,用户信息,访问模式信息设置入对象中,并返回
BlockTokenIdentifier id = new BlockTokenIdentifier(userId, block
.getBlockPoolId(), block.getBlockId(), modes);
return new Token<BlockTokenIdentifier>(id, this);
}
blockToken在创建block块的时候,会被构建
private LocatedBlock createLocatedBlock(final BlockInfo blk, final long pos,
final AccessMode mode) throws IOException {
final LocatedBlock lb = createLocatedBlock(blk, pos);
// 设置blockToken
if (mode != null) {
setBlockToken(lb, mode);
}
return lb;
}
进入setBlockToken方法
public void setBlockToken(final LocatedBlock b,
final AccessMode mode) throws IOException {
// 如果开启了BlockToken验证功能
if (isBlockTokenEnabled()) {
// Use cached UGI if serving RPC calls.
if (b.isStriped()) {
Preconditions.checkState(b instanceof LocatedStripedBlock);
LocatedStripedBlock sb = (LocatedStripedBlock) b;
byte[] indices = sb.getBlockIndices();
Token<BlockTokenIdentifier>[] blockTokens = new Token[indices.length];
ExtendedBlock internalBlock = new ExtendedBlock(b.getBlock());
for (int i = 0; i < indices.length; i++) {
internalBlock.setBlockId(b.getBlock().getBlockId() + indices[i]);
// 生成blockToken对象
blockTokens[i] = blockTokenSecretManager.generateToken(
NameNode.getRemoteUser().getShortUserName(),
internalBlock, EnumSet.of(mode));
}
sb.setBlockTokens(blockTokens);
} else {
// // 生成blockToken对象并设置到block中
b.setBlockToken(blockTokenSecretManager.generateToken(
NameNode.getRemoteUser().getShortUserName(),
b.getBlock(), EnumSet.of(mode)));
}
}
}
这就是blockToken从产