小米的HDFS承载了公司内多个部门几十条业务线的几十PB数据,这些数据有些是安全级别非常高的用户隐私数据,也有被广泛被多个业务线使用的基础数据,不同的业务之间有着复杂的数据依赖。因此,如何管理好这些数据的授权,并尽可能自动化低成本的做好权限管理,是很重要的一部分工作。本文系统的描述了HDFS权限管理体系中与用户关联最紧密的授权相关内容,希望通过本文让大家对权限管理有一个清晰的了解。
HDFS的权限管理是被用户最常问到的问题之一,HDFS实现了一个和POSIX系统类似的文件和目录的权限模型,同时还支持POSIX ACLs规范,由于POSIX ACLs本身就比较复杂,还改变了一些传统POSIX权限体系的语义,因此大部分用户在使用过程中都会遇到以下一些困惑,例如:umask是干什么用的?ACLs里的mask作用是什么?为什么我明明给用户A添加了rwx权限的ACLs,A却仍然写失败?什么权限会继承?什么权限不会继承?为什么创建的这个目录继承的权限跟预期的不一致?
权限控制一般分为两部分,鉴权(Authentication)和授权(Authorization)。鉴权用于鉴定用户身份(例如鉴定你是否是用户foo),授权用于授予给定用户指定的权限(例如将文件f授予用户bar读写权限)。本文只讨论HDFS的授权体系的工作机制,对于常用的权限管理命令先不做介绍,欢迎关注后续文章了解。
HDFS权限管理概述
HDFS实现了一个和POSIX系统类似的文件和目录的权限模型。每个文件和目录有一个所有者(owner)和一个组(group)。文件或目录对其所有者、同组的其他用户以及所有其他用户(other)分别有着不同的权限。对文件而言,当读取这个文件时需要有r权限,当写入或者追加到文件时需要有w权限。对目录而言,当列出目录内容时需要具有r权限,当新建或删除子文件或子目录时需要有w权限,当访问目录的子节点时需要有x权限。总的来说,文件或目录的权限就是它的模式(mode)。HDFS采用了Unix表示和显示模式的习惯,包括使用八进制数来表示权限。当新建一个文件或目录,它的所有者即客户进程的用户,它的所属组是父目录的组(BSD的规定)。
HDFS还提供了对POSIX ACLs(Access Control Lists)的支持,通过NameNode的配置项来控制对ACLs的支持是打开还是关闭。ACLs使得用户可以在权限模型之外提供更加灵活的授权。由于传统的POSIX权限模型对于大部分类Unix系统用户来说都比较熟悉了,因此ACLs将是本文档的重点。
下文会解决HDFS实现的传统POSIX权限模型中最常见的疑问,然后系统的介绍ACLs,最后给出详细的例子和场景来说明ACLs是如何工作的。
传统的POSIX权限模型
HDFS实现的是类似于POSIX系统的权限模型,如前所述,使用过Linux/Unix系统的用户对该模型应该都非常熟悉,就不再赘述。这里先对下文中最常被问到的问题做一些说明:
1、访问某个路径时,用户必须具备该路径上每个目录的执行(x)权限,路径中最后一个目录/文件除外。例如 ls /user/foo/data操作要求用户必须具有根目录(/),user目录,foo目录的执行权限。
2、创建一个文件或者目录时,owner是客户进程的用户,group则继承父目录
3、新建文件或目录的模式(mode)由client在rpc调用时传递给NameNode,它受配置参数umask的约束。新文件的模式是666 & ^umask,新目录的模式是777 & ^umask,即文件默认是没有执行(x)权限的。如果在 create(path, permission, …) 方法中指定了权限参数P,新文件的模式是P & ^umask & 666,如果在mkdirs(path, permission ) 方法中指定了权限参数P,新目录的模式是P & ^umask & 777。
例1:如果umask是022(默认值),那么新文件的模式就是644,新目录的模式就是755,即umask擦除掉了group和other的写权限。
例2:如果umask是027,那么新文件的模式就是650,新目录的模式就是750,即umask擦除掉了group的写权限,以及other的读写执行权限。
4、umask通过client端hdfs-site.xml中的fs.permissions.umask-mode配置项来指定,默认是022。
5、只有超级用户才可以调用chown来修改目录和文件的owner。
ACLs概念介绍
HDFS在传统的POSIX权限模型之外,还支持POSIX ACLs (Access Control Lists)。通过ACLs可以做到更加灵活的授权。
默认情况下对ACLs的支持是关闭的,可以通过设置dfs.namenode.acls.enabled为true来打开。
ACL条目
一个ACLs由一系列条目(entry)组成,下表列出了条目的类型及格式,共有六种类型的ACL条目。
最小ACLs和扩展ACLs
如果ACLs与文件/目录模式权限位(file mode permission bits)完全对应,则称为最小ACLs(minimal ACLs),它们有3个ACL条目(即上面的owner、owning group和others三种类型的条目,由于与传统的POSIX权限模型完全对应,因此不需要指定用户名,称为无名条目)。
拥有超过3个条目的ACLs称为扩展ACLs(extended ACLs),扩展ACLs会包含一个mask条目以及给其他指定用户和组授权的条目,即有名ACL条目(named entry),与最小ACLs中无名条目相对应。
有名ACL条目即上表中named user和named group两种类型。这两类ACLs如下图所示:
ACLs对模式位中组类(group class)权限的影响
在POSIX ACLs规范中,有名条目属于组类(group class)的权限,组类权限还包含owning group条目。由于文件模式权限位中的组类权限可能比某些有名条目的权限小(例如组类权限位是r-x,但是用户foo的ACL条目是user:foo:rwx),这就与POSIX.1中规范的传统权限模型有所冲突,因此ACLs规范POSIX 1003.1e对组类权限进行了重定义:在新的语义下,它表示所有属于组类权限的ACL条目的权限上界。这样做得以保证只遵守POSIX.1规范的应用程序在不知晓ACLs的情况下不会在组权限上遇到问题。
在最小ACLs中,组类权限和owning group条目权限是完全相同的(参考上图的例子),在扩展ACLs组类权限中可能有某些有名条目的权限比owning group条目高,所以owning group条目的权限和组类权限会不一样(参考上图中的例子,owning group权限是r—,组类权限是rw-)。这个问题通过引入mask得到了解决,在最小ACLs中,组类权限对应到owning group条目,在扩展ACLs中,组类权限对应到mask条目的权限。mask的作用如下图所示:
对于最小ACLs来说,文件模式权限中的owner、group和other分别映射到ACLs owner、owning group和others条目,对于扩展ACLs则是分别映射到ACLs owner、mask和others条目。当应用程序修改文件模式权限位(例如使用chmod命令)时,对应的ACL条目也会随之改变。同样的,修改这些ACL条目也会改变文件的模式权限(mode)。
注意,ACLs owner和others条目不属于组类权限,所以他们不会被mask。
默认ACLs
前面讨论的ACL条目定义了文件/目录当前的访问权限,称为访问ACLs(access ACLs),另一种类型叫做默认ACLs(default ACL),它定义了当一个文件系统对象被创建时如何从父目录继承权限。只有目录可以被设置默认ACLs,默认ACLs不会用于权限检查,仅用于权限继承。
创建一个新的目录时,如果父目录设置了默认ACLs,则新目录会继承父目录的默认ACLs作为自己的访问ACLs,同时也作为自己的默认ACLs。新的文件则只会继承父目录的默认ACLs作为自己的访问ACLs。
从父目录默认ACLs继承来的权限并非最终的权限,由于在创建新的目录/文件时client一定会传给NameNode一个文件模式权限(见“传统的POSIX权限模型”一节说明3),两者的计算结果才是最终的权限。计算方式采用与运算,即取继承的ACLs中某类权限与模式权限中对应类别权限的交集。下面是一个例子:
ACLs的示例
下面给展示几个ACLs的示例,方便大家理解。
先看一个最小ACLs的例子,目录/user/foo:
修改/user/foo的模式权限位为755,则对应的ACL条目也跟着发生了变化。
给/user/foo目录添加一条ACLs,此时/user/foo的ACLs就变成了扩展ACLs。我们可以观察到在新增了bar的ACL条目之外,同时还新增了一个mask条目。该mask条目是自动添加的,取值是所有组类型条目的并集。同时扩展ACLs的模式权限位中组类权限也改为映射到了mask条目,因此使用ls命令查看/user/foo的模式权限位,又变成了775。
注意,此时以组用户的身份访问/user/foo目录,仍然只有r-x权限,而不是ls看到的rwx权限了。
再次修改/user/foo的模式权限位从组类权限中去掉w权限,mask条目也跟着发生了变化。同时用户bar的权限也受mask限制变成了r-x,即getfacl结果中显示的effective:r-x。
给/user/foo添加一条默认ACL条目。虽然只给用户joe添加了一条默认ACL条目,但是对于一个完整的ACLs来说所需的其他条目都已经被自动从访问ACLs中复制了过来。
现在创建一个/user/foo的子目录subdir。为了更直观的观察到继承的ACLs权限和client传过来的permission是如何共同作用的,我们先把default ACLs中的other条目给修改成只有读权限。
可以看到/user/foo的default ACLs都已经被subdir继承了,但是有两点需要注意:
(1)由于umask设置的是022,所以新建目录的权限应该是755,但实际是754。这是因为继承的ACLs other条目权限和client传递过来的permission权限中的other类别权限做了与运算,最终other的权限是r--
(2)同时继承的mask是rwx,但是和client传递过来的permission权限中的组类权限(r-x)做过与操作之后就变成了r-x
在/user/foo目录下创建一个文件,会继承默认ACLs中的权限,但文件本身不再有默认ACLs。
参考文献: