HDFS 权限指南

概述

Hadoop 分布式文件系统 (HDFS) 为文件和目录实现了一个权限模型,该模型与 POSIX 模型共享了许多内容。每个文件和目录都与一个所有者和一个组相关联。文件或目录对作为所有者的用户、作为组成员的其他用户以及所有其他用户具有单独的权限。对于文件,需要 r 权限才能读取文件,需要 w 权限才能写入或附加到文件。对于目录,需要 r 权限才能列出目录的内容,需要 w 权限才能创建或删除文件或目录,需要 x 权限才能访问目录的子目录。

与 POSIX 模型不同,文件没有 setuid 或 setgid 位,因为没有可执行文件概念。对于目录,没有 setuid 或 setgid 位目录,这是为了简化。可以对目录设置粘滞位,防止除超级用户、目录所有者或文件所有者之外的任何人删除或移动目录中的文件。为文件设置粘滞位没有任何效果。总的来说,文件或目录的权限就是其模式。通常,此说明中将使用 Unix 惯例来表示和显示模式,包括使用八进制数字。创建文件或目录时,其所有者是客户端进程的用户身份,其组是父目录的组(BSD 规则)。

HDFS 还提供对 POSIX ACL(访问控制列表)的可选支持,以使用针对特定命名用户或命名组的更精细规则来增强文件权限。本文档后面将更详细地讨论 ACL。

每个访问 HDFS 的客户端进程都有一个由用户名和组列表组成的两部分身份。每当 HDFS 必须对客户端进程访问的文件或目录 foo 执行权限检查时,

  • 如果用户名与 foo 的所有者匹配,则测试所有者权限;
  • 否则,如果 foo 的组与组列表的任何成员匹配,则测试组权限;
  • 否则,测试 foo 的其他权限。

如果权限检查失败,则客户端操作失败。

用户身份

从 Hadoop 0.22 开始,Hadoop 支持两种不同的操作模式来确定用户身份,由 hadoop.security.authentication 属性指定

  • simple

    在此操作模式中,客户端进程的身份由主机操作系统确定。在类 Unix 系统上,用户名等效于 `whoami`。

  • kerberos

    在 Kerberized 操作中,客户端进程的身份由其 Kerberos 凭据确定。例如,在 Kerberized 环境中,用户可以使用 kinit 实用程序获取 Kerberos 票据授予票据 (TGT),并使用 klist 确定其当前主体。在将 Kerberos 主体映射到 HDFS 用户名时,将删除除主要组件之外的所有组件。例如,主体 todd/[email protected] 将在 HDFS 上充当简单用户名 todd。

无论操作模式如何,用户身份机制都与 HDFS 本身无关。HDFS 中没有创建用户身份、建立组或处理用户凭据的规定。

组映射

一旦按照上述方式确定了用户名,组列表将由组映射服务确定,该服务由 hadoop.security.group.mapping 属性配置。有关详细信息,请参见 Hadoop 组映射

权限检查

每个 HDFS 操作都要求用户具有特定权限(READ、WRITE 和 EXECUTE 的某种组合),这些权限通过文件所有权、组成员资格或其他权限授予。操作可以在路径的多个组件(而不仅仅是最终组件)执行权限检查。此外,某些操作取决于对路径所有者的检查。

所有操作都需要遍历访问。遍历访问要求对路径的所有现有组件(最终路径组件除外)具有 EXECUTE 权限。例如,对于访问 /foo/bar/baz 的任何操作,调用者必须对 //foo/foo/bar 具有 EXECUTE 权限。

下表描述了 HDFS 对路径的每个组件执行的权限检查。

  • 所有权:是否检查调用者是否是路径的所有者。通常,更改所有权或权限元数据的操作要求调用者是所有者。
  • 父级:请求路径的父目录。例如,对于路径 /foo/bar/baz,父级是 /foo/bar
  • 祖先:请求路径的最后一个现有组件。例如,对于路径 /foo/bar/baz,如果 /foo/bar 存在,则祖先路径为 /foo/bar。如果 /foo 存在但 /foo/bar 不存在,则祖先路径为 /foo
  • 最终:请求路径的最终组件。例如,对于路径 /foo/bar/baz,最终路径组件是 /foo/bar/baz
  • 子树:对于目录路径,目录本身及其所有子目录(递归)。例如,对于路径 /foo/bar/baz(具有名为 buzboo 的 2 个子目录),子树为 /foo/bar/baz/foo/bar/baz/buz/foo/bar/baz/boo
操作 所有权 父级 祖先 最终 子树
追加 不适用 不适用 WRITE 不适用
连接 否 [2] WRITE(源) 不适用 READ(源),WRITE(目标) 不适用
创建 不适用 WRITE WRITE [1] 不适用
createSnapshot 不适用 不适用 不适用 不适用
删除 否 [2] WRITE 不适用 不适用 READ、WRITE、EXECUTE
deleteSnapshot 不适用 不适用 不适用 不适用
getAclStatus 不适用 不适用 不适用 不适用
getBlockLocations 不适用 不适用 READ 不适用
getContentSummary 不适用 不适用 不适用 READ、EXECUTE
getFileInfo 不适用 不适用 不适用 不适用
getFileLinkInfo 不适用 不适用 不适用 不适用
getLinkTarget 不适用 不适用 不适用 不适用
getListing 不适用 不适用 READ、EXECUTE 不适用
getSnapshotDiffReport 不适用 不适用 READ READ
getStoragePolicy 不适用 不适用 READ 不适用
getXAttrs 不适用 不适用 READ 不适用
listXAttrs EXECUTE 不适用 不适用 不适用
mkdirs 不适用 WRITE 不适用 不适用
modifyAclEntries 不适用 不适用 不适用 不适用
removeAcl 不适用 不适用 不适用 不适用
removeAclEntries 不适用 不适用 不适用 不适用
removeDefaultAcl 不适用 不适用 不适用 不适用
removeXAttr 否 [2] 不适用 不适用 WRITE 不适用
重命名 否 [2] WRITE(源) WRITE(目标) 不适用 不适用
renameSnapshot 不适用 不适用 不适用 不适用
setAcl 不适用 不适用 不适用 不适用
setOwner 是 [3] 不适用 不适用 不适用 不适用
setPermission 不适用 不适用 不适用 不适用
setReplication 不适用 不适用 WRITE 不适用
setStoragePolicy 不适用 不适用 WRITE 不适用
setTimes 不适用 不适用 WRITE 不适用
setXAttr 否 [2] 不适用 不适用 WRITE 不适用
截断 不适用 不适用 WRITE 不适用

[1] 仅当调用使用覆盖选项且路径中存在文件时,才需要在 create 期间对最终路径组件进行 WRITE 访问。

[2] 任何对父目录检查 WRITE 权限的操作,如果设置了 粘滞位,也会检查所有权。

[3] 调用 setOwner 来更改拥有文件的用户需要 HDFS 超级用户 访问权限。更改组不需要 HDFS 超级用户访问权限,但调用者必须是该文件的所有者并且是指定组的成员。

了解实现

每个文件或目录操作都会将完整路径名传递给 NameNode,并且权限检查会针对每个操作沿路径应用。客户端框架会隐式地将用户身份与与 NameNode 的连接关联起来,从而减少了对现有客户端 API 进行更改的需要。一直以来,当对文件的一个操作成功时,该操作在重复时可能会失败,因为文件或路径上的某个目录不再存在。例如,当客户端首次开始读取文件时,它会向 NameNode 发出第一个请求来发现该文件的第一个块的位置。找到其他块的第二个请求可能会失败。另一方面,删除文件不会撤销已经知道该文件块的客户端的访问权限。添加权限后,客户端对文件的访问权限可能会在请求之间被撤销。同样,更改权限不会撤销已经知道该文件块的客户端的访问权限。

文件系统 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;还将返回与路径关联的用户、组和模式。

新文件或目录的模式受作为配置参数设置的 umask 限制。当使用现有的 create(path, …) 方法(没有 permission 参数)时,新文件的模式为 0666 & ^umask。当使用新的 create(path, permission, …) 方法(带有 permission 参数 P)时,新文件的模式为 P & ^umask & 0666。当使用现有的 mkdirs(path) 方法(没有 permission 参数)创建新目录时,新目录的模式为 0777 & ^umask。当使用新的 mkdirs(path, permission) 方法(带有 permission 参数 P)时,新目录的模式为 P & ^umask & 0777

对应用程序 Shell 的更改

新操作

  • chmod [-R] mode file ...

    只有文件的所有者或超级用户才能更改文件的模式。

  • chgrp [-R] group file ...

    调用 chgrp 的用户必须属于指定组并且是该文件的所有者,或者必须是超级用户。

  • chown [-R] [owner][:[group]] file ...

    文件的所有者只能由超级用户更改。

  • ls file ...

  • lsr file ...

    重新格式化输出以显示所有者、组和模式。

超级用户

超级用户是具有与 NameNode 进程本身相同身份的用户。宽泛地说,如果您启动了 NameNode,那么您就是超级用户。超级用户可以执行任何操作,因为超级用户的权限检查永远不会失败。没有关于谁是超级用户的持久概念;当启动 NameNode 时,进程身份将确定谁是当前的超级用户。HDFS 超级用户不必是 NameNode 主机的超级用户,所有集群也不必具有相同的超级用户。此外,在个人工作站上运行 HDFS 的实验人员会方便地成为该安装的超级用户,而无需任何配置。

此外,管理员可以使用配置参数识别一个特殊组。如果设置,此组的成员也是超级用户。

Web 服务器

默认情况下,Web 服务器的身份是一个配置参数。也就是说,NameNode 不会了解真实用户的身份,但 Web 服务器的行为就像它具有管理员选择的用户的身份(用户和组)一样。除非所选身份与超级用户匹配,否则 Web 服务器可能无法访问名称空间的部分内容。

ACL(访问控制列表)

除了传统的 POSIX 权限模型外,HDFS 还支持 POSIX ACL(访问控制列表)。ACL 可用于实现与用户和组的自然组织层次结构不同的权限要求。ACL 提供了一种为特定的已命名用户或已命名组(不仅是文件的拥有者和文件的组)设置不同权限的方法。

默认情况下,支持 ACL,并且 NameNode 允许创建 ACL。要禁用对 ACL 的支持,请在 NameNode 配置中将 dfs.namenode.acls.enabled 设置为 false。

ACL 由一组 ACL 条目组成。每个 ACL 条目都指定一个特定用户或组,并为该特定用户或组授予或拒绝读取、写入和执行权限。例如

   user::rw-
   user:bruce:rwx                  #effective:r--
   group::r-x                      #effective:r--
   group:sales:rwx                 #effective:r--
   mask::r--
   other::r--

ACL 条目由类型、可选名称和权限字符串组成。出于显示目的,“:”用作每个字段之间的分隔符。在此示例 ACL 中,文件所有者具有读写访问权限,文件组具有读执行访问权限,其他人具有读取访问权限。到目前为止,这等效于将文件的权限位设置为 654。

此外,还有 2 个针对已命名用户 bruce 和已命名组 sales 的扩展 ACL 条目,两者都授予完全访问权限。掩码是一个特殊的 ACL 条目,它会筛选授予所有已命名用户条目和已命名组条目的权限,以及未命名组条目。在此示例中,掩码仅具有读取权限,我们可以看到,几个 ACL 条目的有效权限已相应地进行了筛选。

每个 ACL 都必须具有一个掩码。如果用户在设置 ACL 时未提供掩码,则会通过计算所有将被掩码过滤的项的权限的并集自动插入掩码。

对具有 ACL 的文件运行 chmod 实际上会更改掩码的权限。由于掩码充当过滤器,因此这会有效地限制所有扩展 ACL 项的权限,而不是仅更改组项,并且可能会遗漏其他扩展 ACL 项。

该模型还区分“访问 ACL”(定义在权限检查期间强制执行的规则)和“默认 ACL”(定义新子文件或子目录在创建期间自动接收的 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 中的确切权限值受模式参数的过滤影响。考虑到默认 umask 为 022,对于新目录通常为 755,对于新文件通常为 644。模式参数过滤了未命名用户(文件所有者)、掩码和其他用户的复制权限值。使用此特定示例 ACL,并使用模式为 755 创建新的子目录,此模式过滤对最终结果没有影响。但是,如果我们考虑创建模式为 644 的文件,则模式过滤会导致新文件的 ACL 对未命名用户(文件所有者)接收读写权限,对掩码接收读取权限,对其他用户接收读取权限。此掩码还意味着已命名用户 bruce 和已命名组 sales 的有效权限仅为读取。

请注意,复制发生在创建新文件或子目录时。对父项的默认 ACL 的后续更改不会更改现有子项。

默认 ACL 必须具有所有最低要求的 ACL 条目,包括未命名用户(文件所有者)、未命名组(文件组)和其他条目。如果用户在设置默认 ACL 时未提供其中一个条目,则会通过从访问 ACL 复制相应的权限或在没有访问 ACL 时复制权限位自动插入这些条目。默认 ACL 还必须具有掩码。如上所述,如果未指定掩码,则会通过计算掩码将过滤的所有条目上的权限的并集自动插入掩码。

请注意,对于给定的文件或目录,ACL 条目的数量不能无限。访问条目的最大数量为 32,默认条目的最大数量为 32,总共为 64。

在考虑具有 ACL 的文件时,权限检查算法将更改为

  • 如果用户名与文件所有者匹配,则测试所有者权限;

  • 否则,如果用户名与其中一个命名用户条目中的名称匹配,则测试这些权限,并通过掩码权限进行过滤;

  • 否则,如果文件的组与组列表中的任何成员匹配,并且由掩码过滤的这些权限授予访问权限,则使用这些权限;

  • 否则,如果命名组条目与组列表中的成员匹配,并且由掩码过滤的这些权限授予访问权限,则使用这些权限;

  • 否则,如果文件组或任何命名组条目与组列表中的成员匹配,但这些权限未授予任何访问权限,则拒绝访问;

  • 否则,测试文件的其他权限。

最佳做法是依靠传统的权限位来实现大多数权限要求,并定义较少数量的 ACL 以使用一些例外规则来扩充权限位。与仅具有权限位的文件相比,具有 ACL 的文件在 NameNode 中会产生额外的内存开销。

ACL 文件系统 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 命令

  • 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 的任何文件或目录的权限字符串。

    请参阅 文件系统 Shell 文档以全面了解这些命令。

配置参数

  • dfs.permissions.enabled = true

    如果为“是”,则按此处所述使用权限系统。如果为“否”,则关闭权限检查,但所有其他行为保持不变。从一个参数值切换到另一个参数值不会更改文件或目录的模式、所有者或组。无论权限是否启用,chmod、chgrp、chown 和 setfacl 始终检查权限。这些功能仅在权限上下文中才有用,因此不存在向后兼容性问题。此外,这允许管理员在启用常规权限检查之前可靠地设置所有者和权限。

  • dfs.web.ugi = webuser,webgroup

    Web 服务器要使用的用户名。将其设置为超级用户名称允许任何 Web 客户端查看所有内容。将其更改为其他未使用的身份允许 Web 客户端仅查看使用“其他”权限可见的内容。可以将其他组添加到用逗号分隔的列表中。

  • dfs.permissions.superusergroup = supergroup

    超级用户组的名称。

  • fs.permissions.umask-mode = 0022

    创建文件和目录时使用的 umask。对于配置文件,可以使用十进制值 18。

  • dfs.cluster.administrators = ACL-for-admins

    作为 ACL 指定的集群管理员。这控制了谁可以访问 HDFS 中的默认 servlet 等。

  • dfs.namenode.acls.enabled = true

    设置为 true 以启用对 HDFS ACL(访问控制列表)的支持。默认情况下,启用 ACL。禁用 ACL 时,NameNode 会拒绝所有设置 ACL 的尝试。

  • dfs.namenode.posix.acl.inheritance.enabled

    设置为 true 以启用 POSIX 样式 ACL 继承。默认情况下启用。启用后,当创建请求来自兼容客户端时,NameNode 会将父目录的默认 ACL 应用于创建模式,并忽略客户端 umask。如果找不到默认 ACL,它将应用客户端 umask。