天天看点

HDFS权限

hdfs实现了posix的文件目录的权限模型。每个文件和目录都关联一个owner和一个group。文件或者目录有独立分开的权限来对应到owner,对应到组成员,对应到其他用户。对于文件来说,读文件需要r权限,写或者append文件需要w权限。对于目录来说,展示目录里内容需要r权限,在目录里创建或删除文件或目录需要w权限,访问目录里的内容则需要x权限。

与posix模型相反的,hdfs没有对文件的setuid或者setgid位,因为hdfs文件是不可执行的。对于目录,为了简化,也不设置setuid或者setgid位。目录可以设置粘滞位,来保护目录内的文件不被其他非超级用户和文件owner的用户删除或移动。对于文件设置粘滞位是无效的。总的来说,文件或目录权限有其自身的模式。通常情况,对于权限模式是用unix的习惯来表示的,包括使用八进制的描述。当一个文件或者目录创建时,它的owner就是客户端进程,它的group就是所在父目录的group(这是bsd的规矩)。

hdfs也提供对于posix acl的可选的支持,以细粒度的规则来声明特定用户或组,以增强文件权限。acl会在后面详细讨论。

每个访问hdfs的客户端进程都包含两部分id,一部分是用户名,另一部分是组列表。每当hdfs必须对一个客户端进程访问一个文件或目录foo做权限检查时,

如果用户名匹配了foo的owner,那么校验owner权限;

否则如果foo的group与组列表中的任意一个group匹配,那么校验group权限;

否则校验foo的其他权限。

如果权限检查失败,那么该次客户端操作失败。

hadoop 0.22版本里,支持两种不同的操作模式来决定用户身份,在属性hadoop.security.authentication里配置:

simple

kerberos

一旦用户名已经决定了,那么group列表会由组映射服务来决定,这个服务由hadoop.security.group.mapping配置属性确定。默认实现是<code>org.apache.hadoop.security.jnibasedunixgroupsmappingwithfallback</code>,前提是java native interface (jni)可用。如果jni可用,实现方案会在hadoop内使用api来解析用户的group列表。如果jni不可用,那么shell实现方案<code>org.apache.hadoop.security.shellbasedunixgroupsmapping</code>会启用。这一方案利用<code>bash -c groups</code>命令(linux/unix环境),或者<code>net group</code>命令(windows环境)来解析用户组列表。

一个替代的实现,是通过直接连接一个ldap服务器来解析group列表,这个实现通过类<code>org.apache.hadoop.security.ldapgroupsmapping</code>来完成。然而这个提供者使用起来有两个要求,被要求的组在ldap上独占式驻留,并且要求不在unix服务器上物化实现。关于配置组映射服务的更多信息参见javadoc。

对于hdfs,用户到组的映射是通过namenode完成的。因此,namenode的主机系统配置决定了用户的组映射。

注意,hdfs以字符串的形式存储用户和组的文件和目录信息;不会像在unix系统上一样对用户和组id做数字的转换。

每个文件或者目录操作都会传完整的路径名给到namenode,每个操作都会要求检查权限。客户端框架会隐式的关联namenode连接和用户id,来减少改变现有客户端api的需求。一直以来,当一对文件的操作成功后,再重复该操作可能会导致失败,因为文件或者目录也许已经不再存在了。比如,当客户端一开始读取一个文件时,它会发起一个请求到namenode,用来定位第一个文件块的位置。第二个来寻找其他的块的请求可能失败。另一方面,删除文件并不会撤销已经知道文件块的客户端的访问。随着权限的增加,一个客户端对文件的访问可能在请求之间撤销。改变权限也不会撤销已经知道文件块的客户端的访问。

所有使用路径作为参数的方法在权限校验失败后会抛出一个<code>accesscontrolexception</code>的异常。

新方法:

<code>public fsdataoutputstream create(path f, fspermission permission, boolean overwrite, int buffersize, short replication, long blocksize, progressable progress) throws ioexception;</code>

<code>public boolean mkdirs(path f, fspermission permission) throws ioexception;</code>

<code>public void setpermission(path p, fspermission permission) throws ioexception;</code>

<code>public void setowner(path p, string username, string groupname) throws ioexception;</code>

<code>public filestatus getfilestatus(path f) throws ioexception;</code>

会额外返回用户,组和与路径相关的权限mode。

新的文件或目录的mode由作为配置参数的umask来限制。当使用<code>create(path, …)</code>方法(无权限参数)时,新文件的mode是<code>0666 &amp; ^umask</code>。当使用<code>mkdirs(path)</code>方法(无权限参数)创建新目录时,新目录的mode是<code>0777 &amp; ^umask</code>。当使用新的<code>mkdirs(path, permission)</code>方法(权限参数为p)时,新目录的mode是<code>p &amp; ^umask &amp; 0777</code>。

新操作:

<code>chmod [-r] mode file …</code>

只有文件的owner或者超级用户允许来变更文件的权限mode。

<code>chgrp [-r] group file …</code>

调用<code>chgrp</code>的用户必须属于对应的group并且是文件的owner,或者是超级用户。

<code>chown [-r] [owner][:[group]] file …</code>

只有超级用户能改变一个文件的owner。

<code>ls file …</code>

<code>lsr file …</code>

输出,并且输出被格式化为显示owner,group和权限mode。

超级用户就是namenode进程本身。宽松的条件下,如果你启动了namenode,你就是超级用户。超级用户拥有最大权限,对于超级用户的权限检查永久不会失败。没有人是永久的超级用户,谁启动namenode进程决定谁是超级用户。hdfs超级用户不一定是namenode主机的超级用户,所有的集群也不必拥有同一个超级用户。同样的,如果一个在个人电脑上尝试性运行hdfs的人,也不需要任何配置就是该次运行的超级用户。

此外,对应某个group的管理员由一个配置参数来指定。如果设置了,那么该组的成员也将是超级用户。

默认情况,web服务器的识别是一个配置参数。namenode对于实际用户的标识并不知情,但是web服务器就像由管理员选定的用户一样来运行。除非被选择的id是超级用户,否则部分命名空间将不可访问。

对于传统的posix权限模型,hdfs也支持posix的acls。acls对于实现不同于正常用户和组的组织层级权限需求非常有效。一条acl提供了一种方法来为特定的用户或组设置不同的权限,不仅仅是文件的owner和文件的group。

默认情况,对于acls的支持是disabled,并且namenode不允许创建acls。要开启acls,需要在namenode的配置里设置dfs.namenode.acls.enabled为true。

一条acl包含了一组acl的项。每个acl项指定了一个用户或组,然后授予或拒绝读、写和执行权限。举例如下:

acl项包含了一个类型,一个可选的名字和一个权限字符串。为了显示的需要,':'用来作为分隔符。在这个例子里,文件的owner拥有读写权限,文件的group拥有读和执行权限,其他人拥有读权限。到此,这等价于设置文件的权限bit为654。

除此之外,还有两个扩展的acl项,一个对应特定的用户bruce,一个对应特定的组sales,两者都被授予了完全访问权限。mask是一种特殊的acl项,用来过滤授予特定用户或组,也包含非特定的组的权限。在此例中,mask只有读权限,对应的几个acl项也被相应的过滤,只有读权限。

每条acl必须有一个mask。如果用户不提供mask,那么系统会自动插入一条mask,该mask是通过union所有项的权限作为过滤项计算出来的。

对一个拥有acl的文件运行<code>chmod</code>会导致改变mask的权限。由于mask是一个过滤器,这有效的限制了所有扩展的acl项,而不是仅仅改变group项和可能丢失的其他扩展acl项。

该模型也区分“访问acl”和“默认acl”,访问acl定义了在权限检查时的规则,而默认acl定义了新的子文件或者子目录创建时自动生效的acl项。举例如下:

只有目录才会有默认acl。当新的文件或子目录创建时,系统自动复制父目录的默认acl到其文件或子目录。子目录同时会把该默认acl作为自己的默认acl。在此原则下,默认acl会传递到系统任意深度的目录树结构中。

对于子文件或目录准确的访问acl值是由mode参数过滤决定的。假设默认的umask是022,那么对于新的文件来说就是644,对于新目录就是755。mode参数过滤对于未指定的用户(文件owner),mask和其他用户的复制来的权限。使用这一特定的示例acl,创建一个新的子目录,其权限mode是755,那么mode过滤器对于最后的结果不起作用。然而,如果我们假设创建一个文件mode是644,那mode过滤器会过滤新文件的acl,导致对于未指定用户(文件owner)拥有读写权限,对于mask拥有读权限,对于其他用户拥有读权限。这个mask同样作用于已制定名字的bruce和sales,其权限也将是只读。

注意,acl的复制发生在新目录或文件创建时。后续的对于父目录的默认acl的变更不会影响已经存在的子文件或目录。

默认acl必须拥有最小的acl项集,包括未指定用户(文件owner),未指定group(文件group)和其他用户。如果用户没有提供上述这些项的设置,那么系统自动复制访问acl中的对应项,或者如果没有访问acl,就复制权限位。默认acl必须有mask。如上所述,如果mask未声明,系统自动创建一个mask,此mask通过union所有项的权限过滤计算得出。

当一个文件拥有acl时,对于权限的校验算法修改为:

if用户名与文件owner匹配,那么检测owner权限;

else if用户名匹配指定的用户项,那么检测这些匹配的用户权限,这些权限先要经过mask过滤;

else if文件的group与group列表的任意成员匹配,并且经过mask过滤的权限授权访问,那么使用这些权限;

else if指定的group项匹配group列表中的任意成员,并且经过mask过滤的权限授权访问,那么使用这些权限;

else if文件group或者任意指定的group项与group列表中的任意成员匹配,但是没有被授权访问,那么访问拒绝;

otherwise校验文件其他用户的权限。

实现权限控制的最佳实践是利用传统的权限位,同时定义很小的一组acls来对可能的例外规则增强权限校验。带有acl的文件权限校验相比只用权限位的文件会导致namenode额外的内存消耗。

<code>public void modifyaclentries(path path, list&lt;aclentry&gt; aclspec) throws ioexception;</code>

<code>public void removeaclentries(path path, list&lt;aclentry&gt; aclspec) throws ioexception;</code>

<code>public void public void removedefaultacl(path path) throws ioexception;</code>

<code>public void removeacl(path path) throws ioexception;</code>

<code>public void setacl(path path, list&lt;aclentry&gt; aclspec) throws ioexception;</code>

<code>public aclstatus getaclstatus(path path) throws ioexception;</code>

<code>hdfs dfs -getfacl [-r] &lt;path&gt;</code>

显示文件和目录的acls。如果一个目录包含默认acl,那么getfacl会显示该默认acl。

<code>hdfs dfs -setfacl [-r] [-b|-k -m|-x &lt;acl_spec&gt; &lt;path&gt;]|[--set &lt;acl_spec&gt; &lt;path&gt;]</code>

设置文件和目录的acls。

<code>hdfs dfs -ls &lt;args&gt;</code>

如果文件或目录包含acl,ls的输出会附加一个'+'到权限字符串。

继续阅读