第十一章:NFSv4访问控制列表

将FreeBSD用作企业NFS或CIFS文件存储的存储后端需要复制组织的结构和文件权限中的信息控制。

传统的Unix user/group/everyone模式根本无法解决这个问题。你需要一个访问控制模型,让你以任何需要的方式构建权限(permissions)和特权(privileges)。

这就是访问控制列表(Access Control Lists——ACLs)发挥作用的地方。

ACL允许你从特权列表中进行选择,并将其应用于任何系统用户或组,为谁可以访问什么创建规则。

理解ACL非常简单,但实施和管理ACL很快会变得复杂。

ACL管理成功的关键在于简单性。不要仅仅因为可以就分配ACL,只在需要时分配它们。

ACL始终在本地主机上进行解释。如果你通过NFS或Samba共享文件系统,客户端发来请求,服务器会解释任何ACL,并按照ACL的指示对客户端做出响应。

ACL 类型

最常遇到的三种风格的ACL为POSIX、NTFS、NFSv4。

POSIX——Portable Operating System Interface,可移植操作系统接口,是一套操作系统互操作性标准。一个提议的POSIX标准POSIX.1e描述了一组访问控制列表行为。拟议的ACL标准经历了几次修订,许多操作系统都实施了其中一次修订。每个实施这些ACL草案的人都认为,当最终草案成为标准时,他们会更新其实施。然而,最终POSIX.1e并未被采纳为标准。因此,这些操作系统的”POSIX“ACL实现略有不同。FreeBSD支持POSIX.1e ACL,但并不建议使用。任何可以用POSIX ACL实现的东西都可以用NFSv4 ACL实现。

MS创建了NTFS访问控制列表。它比POSIX ACL具有更多的访问控制功能和类型,并广泛部署在企业环境中。NTFS ACL中的条目按顺序处理,以第一个匹配为基础。

NFSv4 ACL是作为NFS版本4协商的一部分创建的。它们与NTFS ACL非常相似,是专门允许类Unix主机更好地为Windows客户端服务而创建的。它们非常接近,NFSv4 ACL可以在Samba服务器上使用,并通过Windows安全工具进行管理,每个权限都是单独检查的。每个特权的第一个匹配规则获胜。一个请求可以从几个不同的访问规则中积累所需的权限(参阅本章后面的“ACE排序和拒绝”)。与NTFS一样,NFSv4 ACL默认拒绝访问。如果你需要FreeBSD上的ACL,请使用NFSv4 ACL。

FreeBSD的NFSv4 ACL实现(implementations)故意模仿OpenSolaris衍生操作系统中的实现。管理界面不同,但各种权限和ACL处理跟踪OpenSolaris相当接近。

其他操作系统以不同的方式实现这些ACL。例如Linux在POSIX ACL之上实现了NFSv4 ACL支持,为大多数用户创建了足够兼容的东西。

ACLs 和文件系统

所有ACL实现(implementations)都需要文件系统的支持。UFS支持POSIX和NFSv4 ACL。UFS和ZFS都支持NFSv4 ACL,但它们的行为略有不同。本章后面的”ACL继承“中会有详细介绍。以下是基础知识。

ZFS更适合NFSv4 ACL,默认情况下ZFS启用它们。ZFS提供了两种将目录ACL传递给文件和子目录的模式。想要继承ACL的更流行的方式(Windows客户端所期望的方式)的用户需要在创建任何需要ACL的文件或目录之前,在任何数据集上将aclmode和aclregister属性设置为passthrough。在创建任何文件或应用任何ACL之前设置此值。

你可以在UFS文件系统上使用NFSv4 ACL。然而,UFS文件系统只支持一种ACL继承方法,虽然这是一种不太流行的方法,但它可能对你有用。你必须使用nfsv4acls挂载选项。

ACL 格式

NFSv4访问控制列表包含访问控制条目(ACE——Access Control Entries)。每个ACE都描述了分配给单个实体(如用户、组或系统上的每个人)的权限。理解ACL最简单的方法是看一个简单的ACL。使用getfacl查看文件或目录上的ACL。

这里我有一个具有非常常见权限的文件:

我没有此文件分配任何特殊的ACL。所有者可以读取、写入和执行文件,拥有组的成员可以读取和写入文件,其他所有人可以读取它。让我们看看这些权限是如何显示为ACL的:

ACL是基于标准的文件权限构建的。一旦我将实际的ACL应用于文件,文件系统权限和ACL就会发生分歧。此ACL包含三个ACE条目,每个条目都有四个冒号分开的字段。

传统Unix权限中的”Everyone“意味着”除所有者和组所有者之外的所有人“。而NFSv4 ACL中,它意味着”包括所有者在内的所有人“。

接下来看一个具有ACL的文件:

注意,标准的Unix风格权限与我们查看的第一个文件相同。但是,权限已加号(+)结尾。这标示扩展权限或ACL。以下命令中的-q标志消除顶部注释掉的信息:

这些权限与file1的权限非常相似,但ACL有一个额外的访问控制条目。第一行无注释有五个字段。后面的条目显示为第一个字段的是这里的两个字段。

除了映射到传统Unix权限的owner@、group@和everyone@标签外,标签还可以指定用户或组。这些标签后面有一个限定符,可以精确地缩小此ACE适用于谁。此ACL中的第一个条目适用于用户jkh。

读取此ACL时,用户jkh对该文件的权限与文件所有者完全相同。因此,我们对该文件有四组权限,这是正常的Unix权限所不允许的。

ACE的用户标识部分——要么是从Unix风格的权限派生的标签,要么是标签和限定符的组合——被称为principal(主体)。每个ACE都包含一个主体、权限、继承和类型。

ACL 权限

每个权限可以由单个字符(如x或c)或名称(如execute或read_acl)表示。不过,记住这些字母的意思很烦人。如果你在getfacl中使用-v标志,你会得到每个特权的名称,而不是字母代码:

即使是最神秘的名字也比单个字母更容易记住。

你可以在命令中使用长名称,而不是单独的字母。用斜线分割长名称,如上所示。单字符权限不需要任何分隔符。

以下是按一般功能分组的权限:读取、写入、删除等。我们还将讨论特权组。

关于读的权限

读权限控制用户访问文件中信息的能力。

关于写入的权限

写权限控制用户更改文件的能力。

关于删除的权限

通过delete和delete_child权限,你可以使用ACLs移除删除文件的权限。

因此一个目录可以声明其中的所有文件都是不可移动的,除了专门标记为可移动的文件。任何可以更改文件ACL的用户都可以添加删除权限,因此如果你想让文件不可移动,你也需要阻止这种访问。

如果没有特别需求,不要更改以上权限。

其他权限

以下两个权限真的不适合其他任何地方。

权限设置

其中一些权限可以集体授予或撤销。如果用户应该能够写入文件,你会希望他拥有所需的所有权限。读取没有错误的文件需要能够查看文件元数据。这就是权限集发挥作用的地方。权限集没有单字符表示。

full_set包括所有权限。委托人(principal)被允许或拒绝访问所有内容。

modify_set授予委托人除write_acl和write_owner之外的所有权限。他无法更改文件所有权或文件上的ACL,但可以通过其他方式进行修改。

read_set使委托人能够读取文件、其元数据、ACL和任何扩展属性。

write_set允许委托人写入文件、其元数据和扩展属性,但不允许写入其ACL。

所有者的隐含ACE

文件所有者始终获得权限read_acl、write_acl、read_attributes和write_attributes,即使它们没有出现在acl中的任何ACE中。用户无法将自己锁定在自己的文件之外——这需要系统管理员。

设置ACLs

使用setfacl命令编辑ACL。

ACE顺序在解释ACL时至关重要,如本章后面的“ACL排序和拒绝”所述。

许多更改要求你选择一个位置来插入新的ACE,或者选择要删除的ACE编号。

ACL中的ACE从0开始编号。

添加ACEs

使用-a选项添加ACE(Access Control Entry)到ACL中。此标志有两个参数:要插入此ACE的规则号和ACE本身。

你可以指定多个ACE,用逗号分隔。

将按照你给出的顺序插入多个ACE,然后给出要更改的文件。

以下是示例文件的原始ACL:

用户jkh的ACE与文件所有者的ACE相同。

我们需要赋予用户phk与jkh相同的权限,所以我们复制了他的ACE并更改了主体,删除了破折号。添加ACE时,可以将user缩写为u。我想在第一行插入此ACE:

现在此文件的ACL如下:

用户phk和jkh拥有完全相同的权限,这可能惹恼他俩。

按用户管理ACL是一种糟糕的做法。

理想情况下,我们应该有一个由phk和jkh担任角色的系统组。否则你的ACL会很快变得非常大。每当有人更改工作角色时,你都需要更改用户有权访问的文件的ACL。

使用组管理所有内容,即使组中只有一个人,也更具有可持续性,也不太可能让系统管理员发疯。添加ACE时,你可以将group缩写为g。

下面例子,我为phk和jkh创建了一个系统组,并为他们的访问添加了一个组ACE。我们使用的是特权集full_set,而不是使用正权限列表:

现在他们的特权是通过组ACE表达的,我们需要删除他们各自的ACE。

删除ACEs

使用-x标志从ACL中删除ACE。你要么需要ACE本身,要么需要ACE的规则号。记住,ACE从0开始编号。以下是一个ACL示例:

要通过ACE删除规则,请将ACE作为参数提供给-x。你可以删除任何破折号,也可以将user缩写为u。

以下示例,通过指定用户jkh的ACE来确定其访问权限:

此ACE已不在ACL中。

按数字删除ACE要容易得多,而不是在命令行中键入整个ACE。在上面的列表中,允许phk访问的规则是规则2。然而,我删除了之前的一条规则,将所有规则上移了一个空格。ACE 2现在是赋予文件所有者访问权限的那个。删除它会惹恼文件所有者。在删除规则之前,请务必仔细检查ACL。

用户phk不再拥有个人ACE。

要完全清除ACL,将其烧为灰烬,知道只剩下传统的Unix权限,请使用setfcal -b:

现在你可以重新开始,这次试着做对。

你还可以使用-m标志来编辑ACL。-m标志会自动将新的ACE放在列表的开头,并修改现有的ACE以匹配命令行上的ACE。不建议使用-m,因为-a和-x要精确得多。

这并不是说-m的效果是随机的,而是对于任何不熟悉其内部结构的人来说,-m的影响都是随机的。

ACL 文件

你可以从文件应用ACL,也可以将文件上的ACL复制到另一个文件。将ACL存储在文件中就像运行getfacl并将输出重定向到文件一样简单。添加-q标志,消除注释信息可能是有意义的。

然后,你可以使用setfacl的-M选项将此ACL应用于另一个文件。使用包含ACL的文件作为-M的参数。这会将任何新条目添加到目标文件ACL的开头。在这里,我将文件acl1中的ACL应用于file4。

这将允许你在目录中的每个文件上设置一个通用ACL,但使用继承来管理目录要容易得多,正如本章稍后讨论的那样。

当文件已经具有某些相同主体的ACL时,将ACE添加到CAL会变得棘手。最简单的方法是在应用新ACL之前删除旧ACL,在-M之前使用-b标志:

如果-b出现在-M之后,setfacl会应用新的ACL并立即将其清除。-M实际是没有效果的。

但是,如果你不想摧毁旧的ACL,而是将这两者合并。可以参考以下示例。

下面是文件上的ACL:

vermin组可以完全访问此文件。下面是我想添加的ACL:

这个ACL给新的主体、wheel和operator组访问权限。但它对vermin组有互相冲突的规则。该文件的原始ACL赋予vermin组完全访问权,而在新的ACL中,vermin组仅有读取和执行权限。让我们应用ACL,看看会发生什么:

授予wheel和operator访问权限的新ACE出现在ACL的前面部分,即使它们出现在ACL文件的中间。vermin的ACE已经被文件的ACE覆盖。

NFSv4确实希望所有者、组所有者和每个人的权限出现在ACL的末尾。你可以将这些规则放在你的ACL-in-a-file中的任何位置,但当你将ACL应用于此文件时,它们会自动移动到末尾。

记住,NFSv4 ACL是在按权限优先匹配的基础上工作的。老式的Unix风格权限是末尾隐式"nope"之前的最后一站。

此外,逐个文件管理ACL也是最后的手段。你希望文件继承其ACL。

ACL 继承

ACL管理通常在每个目录的基础上执行。你在目录上设置一个ACL,并告诉它该目录中的所有文件和目录都继承了ACL。当你需要更改ACL时,在目录中更改它,它会自动渗透到文件系统中。

严格来说,继承是NFS 4.1中标准化的功能,而ACL实现的其余部分来自NFS 4规范。然而,继承与NFS 4兼容,这是一个重要特性。

继承样式

NFSv4 ACL有两种不同的主继承模型,还有一些使用不太广泛的模型。我们先分别讨论流行的,然后深入了解鲜为人知的。

默认继承模型将用户的umask与继承的ACL相结合,以设置新创建的文件和目录的权限(permissions)和特权(privileges)。

所有者在任何继承的ACL上都会丢失write_owner和write_acl权限,这意味着他们无法使用chmod更改文件权限。这保留了传统Unix模型的大部分内容,用户可以调整其umask以设置权限。MS客户端没有umask的概念,像Samba这样的CIFS服务器无法合理猜测它应该为创建的所有不同文件和目录使用什么umask。使用这种模式完全混淆了用户。

除了默认模型外,ZFS还支持passthrough(直通)模式。这忽略了用户的umask,只是将目录的ACL向下传播到文件和子目录。如果你为Windows客户端提供服务,你几乎肯定会想要直通模式。

要启用passthrough模式,请将ZFS属性aclmode和aclherite设置为passthrough

在创建任何目录和文件之前,请更改这些设置,特别是对于MS客户端。

继承选项

你可以调整ZFS数据集的aclinherit属性,以指定继承的功能。虽然直通模式最受欢迎,但其他选项可能适合特殊情况。

默认的受限权限会删除新文件和目录上继承的write_owner和write_acl权限。只有文件所有者可以更改自己文件的ACL。继承的ACL不能分配超出用户umask允许的权限。

要禁用新创建的文件和目录上的所有ACL继承,请将aclinherit设置为discard。

如果我们将aclinherit设置为noallow,则新文件和目录只继承deny ACEs,外部进程必须分配任何allow ACEs。

将aclinherit设置为passthrough会忽略umask,根据ACL创建具有Unix风格权限的文件。

最后,passthrough-x的aclinherit值限制了对新文件的执行权限。如果可继承的ACE包括执行权限,并且umask允许创建可执行文件,则新文件将继承可执行权限。

模式选项

aclmode属性规定了chmod如何更改文件的权限。

groupmask的alcmode设置使用户的umask成为文件可以拥有的最大权限级别。有关完整讨论,请参阅本章稍后的“umask和ACL继承。

将aclmode设置为discard,可以让chmod删除所有ACL信息。

aclmode的passthrough将ACL直接发送到新文件。

继承标志

你可以通过使用inheritance flags(继承标志)来精确地指定目录如何传播其ACL。你还记得每个ACE中最后一个字段旁边总是破折号吗?这是我们填写的地方。

这些标志只能放置在目录上,不能放置在单个文件上。与特权标志一样,你不能混合使用名称和字母。

文件和目录在添加到文件系统时继承其ACL。在文件服务器上,这可能发生在用户上传文档的时候。对本地用户来说,这是在文件创建时。

目录和继承

让我们将ACL应用于目录并添加一些文件。

我们将从常见的情况开始,一个具有处于直通(passthrough)模式的ACL的ZFS数据集。请注意,虽然我使用特权集的全名,但我使用字母作为继承选项。我不能将特权的全名与字母特权混合,也不能将继承选项的全名与字符混合,但我可以对特权和继承使用不同的方法。

我的系统组vermin可以完全访问此目录。f(file_inherit)和d(dir_inherit)继承标志告诉系统将此相同的ACL应用于任何文件和子目录。

用户phk上传了一个名为phones.docx的文件,其中包含如何设置新手机的说明。他对档案没做什么特别的事情。获取其ACL:

此文件上的ACL与目录上的ACL的不同之处仅在于继承标志,大写字母I意味着这个ACL是继承的。

如果此组中的用户创建了一个子目录,它将获得相同的ACL,以及f和d继承标志。

此新子目录中的文件将获得相同的ACL。用户jkh将文档alarms.docx上传到serverroom目录,记录下次触发警报要做什么。它获得的ACL与上面的文件相同。

改变ACLs和继承

ACL继承在文件和目录创建时开始。

接下来看看这在实践中是如何影响系统的。在这里,我通过添加n继承标志来更改继承,这样ACL就不会传播到子目录中的文件。我还为员工组中的每个人提供只读访问权限。

现在检查支持目录中的现有文件,如phones.docx,您将看到ACL没有变化。staff组无法读取此文件。不过,创建一个像keycards.docx这样的新文件,它就有了新的ACL。

这似乎很简单,但子目录稍微不那么直观。我关闭了“传播到子目录中的文件”继承选项,所以让我们转到子目录serverroom,看看那里发生了什么。现有文件alarms.docx具有与创建时相同的ACL。

在serverroom目录中创建一个新文件racks.docx。检查其ACL,您会发现它与旧文件alarms.docx具有相同的ACL。这些ACL不应该再传播了——发生了什么?

顶级目录发生了ACL更改,表示不会将ACL传播到子目录中的文件,但子目录serverroom已经存在。目录在创建时继承其ACL。对顶级目录的更改没有传播到子目录,因此子目录保留了自己的ACL。

如果创建新的子目录,它将从父目录继承新的ACL。新子目录中的文件将不会继承ACL。

这与您在Microsoft系统上的体验没有什么不同。更改目录中的ACL并将其传播到根目录树中的整个目录树会导致大量磁盘流失,因为系统会触及每个文件。递归更改整个ZFS目录树上的ACL可能需要两个find(1)命令:一个用于文件,另一个用于目录。您最好使用-M从文件中读取ACL,并在应用新ACL之前使用-b删除现有的ACL。

Umask 和 ACL 继承

如果你在UFS上使用ACL,或者你不想使用ZFS的passthrough模式,用户的umask会给出ACL中权限的上限。如果你在文件创建时不熟悉umask和权限,请阅读umask(2)和互联网上的教程。每个系统管理员在开始考虑实施ACL之前,都必须了解umask。

FreeBSD默认的umask时022,这意味着文件时所有者可以读取和写入的模式,而组所有者和其他人只能读取他们。如果你使用此umask并创建文件,则这些是文件可以拥有的最大权限。如果你继承的ACL说”这个其他组应该有对文件的读写权限“,而你的umask说”其他人都可以对我创建的文件进行只读访问“,那么umask获胜。

FreeBSD给用户一个默认的umask 022是有充分理由的。在某些情况下,增加umask可能是有意义的——在一些共享系统上,我将umask设置为027,这样,随机系统用户就无法查看我的文件内容。降低你的umask很少有意义。

如果你不使用ZFS的ACL传递功能,当你在目录树的ACL保护部分工作时,你需要umask减少到0,然后再完成后将其增加回022。当passthrough模式帮你处理时,不需要做这么麻烦的事情。

ACE 订购和拒绝

NFSv4 ACL在第一次匹配的基础上进行处理。然而,一场比赛不会触发整个ACE。相反,每个权限都会单独检查。考虑以下文件。

这真是一个愚蠢的文件。它可由所有者执行。它可由组所有者读取和执行。它是可写的、可读的,除了所有者之外,每个人都可以执行。在现实世界中,你几乎永远不会这样做。

这是我第一次在ACL中显示拒绝ACE,所以让我们快速讨论一下。

一般来说,拒绝ACE应该出现在ACL的开始。当否认声明与允许声明混杂出现时,微软客户尤其会发出嘘声。唯一通常出现在允许之后的否认ACE是最后的隐含ACE。不过,某些ACL不允许将所有拒绝语句放在文件的开头。在文件上运行chmod 00101并查看ACL。

当主体尝试访问文件时,它尝试使用的每个权限都会被单独检查。每个检查都会通过ACL“下拉”,直到它匹配到主体和特权。

假设文件所有者想要执行这个愚蠢的文件。第一个ACE上的负责人是所有者。这个ACE明确禁止阅读和写作,但没有提到执行。我们检查下一个ACE,直到ACE允许执行,ACE明确拒绝执行,或者我们点击终端隐含拒绝。

这个例子看起来微不足道,但同样的理论也适用于复杂的ACL。用户可以作为一个主体的成员访问一个特权,然后作为另一个主体成员访问第二个特权。每个单独的权限检查都会通过ACL,直到它被授予用户或被阻止。

ACLs 和 Samba

NFSv4 ACL最常部署在Samba 4服务器上(http://www.samba.org),作为CIFS客户端的存储后端。以下是FreeNAS支持人员John Hixson提供的一些常见问题,他回答了大多数ACL问题。

始终使用ZFS。一旦你有了ZFS,一定要在Samba的数据集上将aclmode和aclherite设置为passthrough。

不要使用全局可写文件(mode 777),也不要使用000的umask。如果你这样做,你会受苦的。

目录上的Windows搜索要求至少具有rxaRc权限。

ACL可能会比本章中的示例复杂得多。记住,简单性是成功部署ACL的关键。祝你好运。

在ACL之后,FUSE和autofs似乎微不足道。