Overview
HDFS 实现了文件和目录的权限模型,很多跟POSIX模型共享。 每个文件、目录都关联到用户、组。文件、目录对于拥有者、组中的其他用户、其他用户有着不同的权限设置。对于文件,读取需要r
权限,写入/追加需要w
权限。对于目录,列出目录内容需要r
权限,创建、删除文件或目录需要w
权限,访问目录子项需要x
权限。
与POSIX模型不同,因为没有可执行文件,所以文件没有setuid
or setgid
bits。对于目录,也没有setuid
or setgid
bits作为简化。粘滞位sticky bit
可以被作用于目录,放置除superuser、目录所有者、文件所有者之外的任何用户删除、移动目录内的文件。粘滞位sticky bit
对于文件不生效。 文件或目录的权限我们称为 mod
。一般来说,mdoe的展示使用Unix风格,比如描述的8进制数使用。当创建一个文件/目录后,它的所有者即为客户端进程的用户,它的组是父目录的组(BSD规则)。
HDFS 还支持POSIX ACLs (Access Control Lists),给指定用户、组配置更新颗粒度的规则来加强权限管理。ACLs稍后会详细介绍。
每个访问HDFS的客户端进程身份都有两部分组成:用户名,组列表。无论什么时候,HDFS都必须会客户端进程访问的文件或目录foo做权限检查,
- 如果用户名匹配foo的owner,那么校验owner权限;
- 如果foo的group匹配group lists内的任一项,那么校验group权限;
- 否则校验foo的其他权限;
如果权限校验失败,客户端操作失败。
User Identity
自 Hadoop 0.22 起,Hadoop 支持两种不同的操作模式来判断用户的身份,通过hadoop.security.authentication
属性指定:
-
simple
客户端进程的身份由主机操作系统判断。在类Unix系统上,用户名等同于
whoami
。 -
kerberos
客户端进程的身份由它的Kerberos凭证判断。比如,在一个Kerberized environment,一个用户可以通过
kinit
功能来获取Kerberos ticket-granting-ticket (TGT) ,并使用klist
来判断当前的委托人。当把一个Kerberos principal 映射到一个 HDFS 用户名,除了主要组件外的其他所有组件都被丢弃。例如,一个principal todd/foobar@CORP.COMPANY.COM 将以HDFS上的simple用户名todd进行操作。
不管是哪种模式,用户身份机制都是HDFS的外部机制。HDFS中没有用于创建用户、建立组、处理用户凭据的规定。
Group Mapping
一旦username被判定,如上,groups list将由group mapping service 判断,配置在hadoop.security.group.mapping
属性。参见 Hadoop Groups Mapping 获取更多内容。
Permission Checks
每次HDFS操作都需要用户拥有指定权限(读,写,执行的组合),通过文件的 ownership,group membership or 其他权限进行授权。 一个操作会进行路径多个组件的权限检查,而不仅是最后一个组件。另外,一些操作依赖于路径owner的检查。
所有的操作都需要遍历访问(traversal access)。遍历访问需要对路径上的所有存在的组件拥有执行权限,最终路径组件除外。比如,任一访问 /foo/bar/baz 的操作,调用者需要拥有/, /foo and /foo/bar
的可执行权限。
- 如下表格描述了HDFS对路径每个组件的权限检查。
-
Ownership:
检查调用者是否path的owner。通常,改变元数据的ownership或permission的操作需要调用者是owner。 -
Parent:
当前请求path的父目录。比如,对于path/foo/bar/baz
,parent 是/foo/bar
。 -
Ancestor:
当前请求path最后存在组件。 比如,对于 path/foo/bar/baz
,如果/foo/bar
存在的话,ancestor path 是/foo/bar
。如果/foo
存在,而/foo/bar
不存在的话,ancestor path 是/foo
。 -
Final:
当前请求path最终组件。比如,对于path/foo/bar/baz
,final path 组件就是/foo/bar/baz
。 -
Sub-tree:
如果path是一个目录,该目录和它的递归子目录。比如,对于 path/foo/bar/baz
,有2个sub-directoriesbuz
和boo
,sub-tree 是/foo/bar/baz
,/foo/bar/baz/buz
,/foo/bar/baz/boo
。
-
Ownership:
Operation | Ownership | Parent | Ancestor | Final | Sub-tree |
---|---|---|---|---|---|
append | NO | N/A | N/A | WRITE | N/A |
concat | NO [2] | WRITE (sources) | N/A | READ (sources), WRITE (destination) | N/A |
create | NO | N/A | WRITE | WRITE [1] | N/A |
createSnapshot | YES | N/A | N/A | N/A | N/A |
delete | NO [2] | WRITE | N/A | N/A | READ, WRITE, EXECUTE |
deleteSnapshot | YES | N/A | N/A | N/A | N/A |
getAclStatus | NO | N/A | N/A | N/A | N/A |
getBlockLocations | NO | N/A | N/A | READ | N/A |
getContentSummary | NO | N/A | N/A | N/A | READ, EXECUTE |
getFileInfo | NO | N/A | N/A | N/A | N/A |
getFileLinkInfo | NO | N/A | N/A | N/A | N/A |
getLinkTarget | NO | N/A | N/A | N/A | N/A |
getListing | NO | N/A | N/A | READ, EXECUTE | N/A |
getSnapshotDiffReport | NO | N/A | N/A | READ | READ |
getStoragePolicy | NO | N/A | N/A | READ | N/A |
getXAttrs | NO | N/A | N/A | READ | N/A |
listXAttrs | NO | EXECUTE | N/A | N/A | N/A |
mkdirs | NO | N/A | WRITE | N/A | N/A |
modifyAclEntries | YES | N/A | N/A | N/A | N/A |
removeAcl | YES | N/A | N/A | N/A | N/A |
removeAclEntries | YES | N/A | N/A | N/A | N/A |
removeDefaultAcl | YES | N/A | N/A | N/A | N/A |
removeXAttr | NO [2] | N/A | N/A | WRITE | N/A |
rename | NO [2] | WRITE (source) | WRITE (destination) | N/A | N/A |
renameSnapshot | YES | N/A | N/A | N/A | N/A |
setAcl | YES | N/A | N/A | N/A | N/A |
setOwner | YES [3] | N/A | N/A | N/A | N/A |
setPermission | YES | N/A | N/A | N/A | N/A |
setReplication | NO | N/A | N/A | WRITE | N/A |
setStoragePolicy | NO | N/A | N/A | WRITE | N/A |
setTimes | NO | N/A | N/A | WRITE | N/A |
setXAttr | NO [2] | N/A | N/A | WRITE | N/A |
truncate | NO | N/A | N/A | WRITE | N/A |
[1] 如果调用使用overwrite配置,并且该路径已经存在文件,那么create
操作只需要对最终路径组件拥有写权限即可。
[2] 如果设置了sticky bit ,对父目录写权限的检查操作,同样也会检查ownership。
[3] 调用 setOwner
来改变文件的拥有着需要 HDFS super-user 访问权限。变更组不需要HDFS super-user 访问权限,但是调用者必须是文件的拥有者并且是指定组的成员。
Understanding the Implementation
每个文件、目录操作都会将完全路径发送给NameNode,对每个操作都会沿着path进行权限检查。客户端框架隐式的将用户身份与到NameNode的连接关联,减少对现有客户端API的改动的需求。常见一种情况,当某文件的一个操作已经成功完成了,再操作的时候会失败,因为该路径上的文件、目录已经不存在了。举个例子,当客户端第一次开始读取一个文件,它向NameNode发出第一个请求,以发现文件的第一个块的位置。第二个请求查找其他的块可能会失败。另一方面,删除一个文件并不会撤回客户端对该文件的访问,该客户端已经知道该文件的块。通过添加权限,客户端对文件的访问可以在请求之间被撤回。同样,变更权限不会撤回客户端的访问,该客户端已经知道文件的块。
Changes to the File System API
如果权限检查失败,所有使用路径参数的方法都会抛出 AccessControlException
。
新方法:
public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException;
public boolean mkdirs(Path f, FsPermission permission) throws IOException;
public void setPermission(Path p, FsPermission permission) throws IOException;
public void setOwner(Path p, String username, String groupname) throws IOException;
-
public FileStatus getFileStatus(Path f) throws IOException;
将额外返回user, group and 路径相关的mode。
新文件、目录的mode受umask设置限制,umask设置是配置项。
当使用现有方法 create(path, …)
(不带权限参数),新文件的 mode 是0666 & ^umask
。
当使用新方法 create(path, permission, …)
(带权限参数 P) ,新文件的 mode 是 P & ^umask & 0666
。
当使用现有方法 mkdirs(path)
(不带权限参数)创建一个新目录, 新目录的 mode 是0777 & ^umask
。
当使用新方法 mkdirs(path, permission)
(带权限参数 P), 新目录的 mode 是 P & ^umask & 0777
。
Changes to the Application Shell
新操作:
- chmod [-R] mode file ...
仅文件的拥有着或super-user允许变更文件的mode。 - chgrp [-R] group file ...
调用chgrp的用户必须属于指定组,并且是文件的拥有者,或者是super-user。 - chown [-R] [owner][:[group]] file ...
文件的owner仅可以由super-user更改。 - ls file ...
- lsr file ...
输出被重新格式化以显示 owner, group and mode。过时了,使用 -ls -R 替代。
The Super-User
谁启动NameNode,谁就是super-user。 super-user可以执行任意操作,权限校验从不失败。HDFS super-user 不必是NameNode 主机上的super-user,也不是说集群内的说有主机都需要有这个super-user。
如果在个人电脑上实验运行HDFS,为方便起见无须任何配置该用户即成为安装的super-user。
另外,管理员还可以通过配置参数标识一个特定组。该组内的成员也是super-users。
The Web Server
默认,web server的身份是一个可配置项。即,NameNode不知道真实用户的身份,但是web server以管理员选定的用户的身份(用户/组)行动。 除非选择的身份匹配super-user,部分命名空间是不可以被web server访问的。
ACLs (Access Control Lists)
除了传统的POSIX权限模型,HDFS还支持POSIX ACLs (Access Control Lists)。ACLs 对于实现区分用户、组的自然组织层次结构的权限需求非常有用。ACL提供了一个方法,可以给指定用户、组设置不同的权限,而不仅仅是文件的拥有着和所属组。
默认,ACLs的支持是关闭的,并且NameNode不允许创建ACLs。要开启ACLs的支持,在NameNode配置内设置dfs.namenode.acls.enabled
为 true。
一个ACL由一系列ACL entries组成。每条ACL entry 命名了特定用户/组,并授于/拒绝 读、写、执行
权限。比如:
user::rw-
user:bruce:rwx #effective:r--
group::r-x #effective:r--
group:sales:rwx #effective:r--
mask::r--
other::r--
ACL entries 由type,可选的 name 和 permission 组成。为了便于展示,‘:’ 用作分隔符。 在这个范例内,文件的owner有read-write权限,文件的group有read-execute权限,others有读取权限。因此,等同于设置文件的权限为654。
另外,有2个扩展ACL entries给用户 bruce 和组 sales,并全部赋予所有权限。mask 是一个特殊的ACL entry,过滤给所有命名的user entries 和命名的group entries 以及非命名的group entry的权限。 在范例内,mask仅有写权限,并且我们可以看到几个ACL entries的有效权限被相应的过滤了。
每个ACL都必要要有一个mask。如果在设置ACL的时候用户没有提供mask,那么会通过计算来自动插入,计算将被过滤的所有entries的权限的并集。
在拥有ACL的文件上运行 chmod
实际上改变了mask的权限。既然mask作为过滤器,这有效的约束了所有扩展ACLs的权限,而不仅是变更group entry并且可能会丢失其他扩展ACL entries。
模型还有效区分了“access ACL”
和“default ACL”
。
“access ACL”
,定义权限检查期间强制执行的规则;“default ACL”
,定义新子文件或子目录创建期间自动接受的规则。
比如:
user::rwx
group::r-x
other::r-x
default:user::rwx
default:user:bruce:rwx #effective:r-x
default:group::r-x
default:group:sales:rwx #effective:r-x
default:mask::r-x
default:other::r-x
只有目录可以拥有默认 ACL。当一个新文件或者子目录创建,它会自动的拷贝父级的默认ACL作为自己的ACL。新子目录还将其作为默认 ACL。这样,当新目录创建时,默认 ACL 会被复制到该文件系统数的任意深度层。
新子级的ACL的确切权限值将由mode参数进行过滤。考虑到默认umask 是 022,则新目录是755,新文件是644。mode参数给unnamed user (file owner), the mask and other过滤过滤拷贝过来的权限值。 使用这个特定范例ACL,并创建一个mod 755的子目录,该mode过滤器对最终结果没有影响。但是,如果我们考虑创建一个mode 644 的文件,mode过滤器会导致新文件的ACL接受unnamed user(file owner)的读写权限,mask的读权限,以及其他的读权限。 该mask意味着named user bruce 和 named group sales 的有效权限是只读。
注意,拷贝发生在创建新文件或子目录的时候。对父级默认ACL的后续修改不会影响存在的子级。
默认 ACL 必须拥有所有必要的ACL entries,包括unnamed user (file owner), unnamed group (file group) and other entries。当设置默认ACL的时候,如果用户没有指定其中某个entry,那么entries会自动插入,通过拷贝访问ACL相应的权限、如果没有访问ACL则拷贝permission bits来实现。默认ACL必须拥有mask。如上所属,如果没有指定会自动插入一个计算的值。
对拥有ACL的文件,权限的检查算法变更为:
如果用户名匹配file owner,那么检查owner permission;
否则如果用户名匹配named user entries之一,那么要检查这些权限,以mask权限过滤;
否则如果组匹配groups list的成员,并且如果这些通过mask过滤的权限获取访问,那这些权限被使用;
否则如果由一个named group entry 匹配groups list成员,并且如果这些通过mask过滤的权限获取访问,那这些权限被使用;
否则如果文件所属组或任一 named group entry 匹配 groups list成员,但是访问没有被这些权限授权,那么访问被拒绝;
其他情况下,文件的other权限会被测试。
最佳实践依赖传统的权限位来实现大多数权限需求,并定义较少数量的ACL来加强,用一些异常规则来扩充权限位。与只有权限位的文件相比,具有ACL的文件在NameNode中会增加内存开销。
ACLs File System API
新方法:
public void modifyAclEntries(Path path, List<AclEntry> aclSpec) throws IOException;
public void removeAclEntries(Path path, List<AclEntry> aclSpec) throws IOException;
public void public void removeDefaultAcl(Path path) throws IOException;
public void removeAcl(Path path) throws IOException;
public void setAcl(Path path, List<AclEntry> aclSpec) throws IOException;
public AclStatus getAclStatus(Path path) throws IOException;
ACLs Shell Commands
-
hdfs dfs -getfacl [-R] <path>
显示文件/目录的ACL。如果一个目录拥有默认ACL,那么 getfacl还会显示默认ACL。
-
hdfs dfs -setfacl [-R] [-b |-k -m |-x <acl_spec> <path>] |[--set <acl_spec> <path>]
设置文件/目录的ACL。
-
hdfs dfs -ls <args>
ls
会在拥有ACL的文件、目录的权限string上附加一个‘+’字符。
参考 File System Shell 文档获取命令的全部内容。
Configuration Parameters
dfs.permissions.enabled = true
如果是,则使用这里描述的权限系统。如果否,关闭权限检查,但是其他行为不变。从一个参数值切换到其他值不会改变mode、文件或目录的owner or group。无论权限是开还是关,chmod
、 chgrp
、 chown
and setfacl
总是会检查权限。 这些功能仅在权限上下文有用,因此不存在向后兼容问题。此外,这允许管理员可靠的设置所有者和权限,在打开常规权限检查前。
dfs.web.ugi = webuser,webgroup
web server使用的用户名。设置为super-user允许所有客户端看到任何内容。改为其他未使用的身份允许web客户端仅可以查看"other"权限可见的内容。其他将其他组添加到逗号分隔列表内。
dfs.permissions.superusergroup = supergroup
super-users 的组。
fs.permissions.umask-mode = 0022
当创建文件和目录时使用的 umask 。对于配置文件,可以使用十进制值 18。
dfs.cluster.administrators = ACL-for-admins
集群的管理员被配置为ACL。这控制谁可以访问HDFS默认的servlets 等。
dfs.namenode.acls.enabled = true
设置为true,开启HDFS ACLs (Access Control Lists)的支持。默认,ACLs是关闭的。当ACLs关闭的时候,NameNode拒绝所有设置ACL的企图。 .
dfs.namenode.posix.acl.inheritance.enabled
设置为true,开启 POSIX风格的ACL继承。默认开启。当它被启动时,并且create请求来自于兼容客户端,NameNode会从父目录应用默认ACL来创建mode并忽视客户端umask。如果没有默认ACL,它将应用客户端umask。