【简介】
Hadoop安全需要解决两个问题:一个是认证,即解决用户身份合法性验证问题;另一个是授权,即解决认证用户的操作范围问题。
对于认证,Hadoop设计了Security特性和BlockToken方案;对于授权,设计了ACL机制。
本文重点讲述BlockToken相关的知识原理。
【BlockToken流程概述】
BlockToken方案使用HMAC(Hash Message Authentication Code)技术实现对合法请求的访问认证检查。
HMAC算法流程为:
1. 消息传递前,Alice和Bob约定共享密钥和HASH函数
2. Alice把要发送的消息使用共享密钥计算出HMAC值,然后将消息和HMAC发送给Bob
3. Bob接收到消息和HMAC值后,使用共享密钥独立计算消息本身的HMAC值,与接受到的HMAC值对比。
4. 如果二者的HMAC值相同,说明接收到的消息是完整的,并且是Alice发送的。
对于HDFS而言,NN就相当于上面的Alice,DN就相当于上面的Bob,而客户端在其中扮演数据中转的角色,具体流程为:
1. NN启动后进行初始化,生成共享密钥(下面都称为key)
2. DN在向NN注册时,NN将key信息同步给DN。
3. 客户端在进行文件读写时,需要先向NN请求获取block的信息,NN在处理请求时,生成对应的BlockToken,其中包含了通过key计算出的HMAC值。
4. 接下来,client继续向DN建立连接并进行实际block数据的读写请求,读写请求中会携带从NN中获取的BlockToken。
5. DN进行处理block读写请求前,对BlockToken进行校验,具体包括从BlockToken中获取用户、blockpoolid、blockID、以及访问权限,然后与实际请求的用户、读写的blockID、访问动作进行对比看是否相符;然后根据从NN获取的key计算出HMAC,并与传递过来的HMAC比较是否相等。
【BlockToken的相关实现】
从代码实现的流程来看,最关键的一个数据结构是LocatedBlock类,该类的成员除了包含block的相关信息(blockID、时间戳、副本信息等)、在文件中的偏移量、副本存储的位置(DN)信息外,还包含关键的BlockToken信息。
具体类代码如下所示:
Token类的代码如下所示:
kind为token的类型,对于BlockToken为常量"HDFS_BLOCK_TOKEN";
service用来表述请求的服务,通常为服务的"host:port",对于BlockToken则为空;
TokenRenewer是客户端生命周期内的renewer,防止token过期而进行的定期更新,对于BlockToken一般不设置,即BlockToken只在固定有效期内生效。
identifier是BlockTokenIdentifier的序列化结果。
password则是使用key通过MAC算法对identifier计算得到的密码(对应于HMAC)
BlockTokenIdentifier类信息如下代码所示:
包括用户ID,blockPool的ID,block的ID,访问类型,使用的key的ID,以及过期时间。
NN在处理读写block请求时,最终会调用BlockTokenSecretManager的generateToken方法生成BlockToken,相关代码如下:
在Token的构造方法中,会调用BlockTokenSecretManager的createPassword方法完成密码(对应于HMAC)的计算:
DN在处理block数据的读写请求前,最终调用BlockTokenSecretManager的checkaccess方法完成blocktoken的校验工作,具体代码为:
在retrievePassword方法中根据key计算出密码。
到这里就完成了BlockToken的全部流程。
【key的同步与过期处理】
从上面的代码中可以看出,NN和DN对序列化的identifier能计算出同一个password的关键在于使用同一个key。
然而key并不是固定不变的,而是有一定的有效期(默认10小时),超过有效期后,NN会进行更新,DN则通过定时的心跳从NN获取key完成与NN的同步。
但这里会存在问题。考虑这么一个场景,NN对key更新后,DN的心跳还未触发,客户端进行读写请求,此时的密码是NN新的key产生的,而DN中还未来得及更新key,这就会出现key不一致而导致BlockToken无法校验通过。
为了解决上述问题,NN中同时维护了3份key,每个key都有唯一的ID,这些key存放在一个map中。内部定时检测key是否过期,如果过期,则生成新的key添加到map中并滚动向前激活当前的key。
对应代码如下所示:
相关类成员:
初始化生成key(注意初始化时,当前key和nextkey的过期时间设置)
更新key:
DN向NN注册时,获取全量的key并保存,同样,每次心跳从NN获取的key也保存在map中。
另外,NN生成BlockToken时,将key的ID存放在identifier中,DN对BlockToken进行校验时,从identifier中获取key的ID并在内存中找到对应的key。如此一来,就可以保证NN和DN使用同一个key进行密码的计算了。
key的过期问题通过存3份可以解决,那么在HA模式下,如果ANN异常了,SNN接管成为新的ANN,此时key不一致的问题如何解决呢?
对此,hadoop的解决办法是让DN同时保存来自NN和SNN的key。
由于DN会同时向ANN和SNN注册,并发送心跳。这样,DN就会从ANN和SNN分别拿到3份key。因此,只要BlockToken中的key是来自NN的,不管是ANN还是SNN,都能保证使用相同的key进行密码的计算。
此外,在联邦模式下,DN会按照blockpool分别存储多个NN的key信息,保证来自不同NN的BlockToken都能使用对应的key正确计算出密码。
【性能考虑】
NN的性能考虑:
在NN中维护3份key,也就需要对3份key都定时检测是否过期,但是由于时间间隔很长,默认的检测间隔和过期时间都为10小时,因此内存中维护多份key不存在性能问题。
而读写block时需要先有BlockToken,BlockToken的生成和校验都需要计算密码,因此读写耗时的性能整体会有一定的损耗。实测发现,文件大小越小,读写耗时的损耗就越明显;而文件越大,由于读写耗时的基数本身相对较大,这样,因计算密码带来的损耗也就越来越不明显。
总结下,本文对BlockToken的概念进行了简单描述,然后从原理流程、源码实现讲述了BlockToken的鉴权流程,以及key过期的处理,最后以实际测试的经验对性能损耗做了一定的分析。
好了,本文就介绍到这里,原创不易,点赞,在看,分享是最好的支持, 谢谢~