第十章:Sudoers的分配和复杂的政策

对于一台机器来说,Sudo是一个很大的麻烦。但是,如果您运行数百或数千个系统,sudo会使用户权限易于管理。不容易或简单,但可以管理。在整个网络中制定一致策略的最佳方法是编写一个sudoers文件并将其复制到网络上的所有计算机。虽然这很容易做到,但这里有一些关于编写和部署安全策略的提示。

打破Sudo

在本书的前面,我们已经谈到了如何摆脱sudo的限制,但让我们把它们放在一起考虑。以下是关于如何制定sudoers政策的“热门话题”。

不要从别名中排除命令。用户可以轻松绕过命令列表,如 ALL,!/bin/sh 。使用ALL命令列表可以赋予人们特权访问权限,无论系统所有者如何限制它。

默认情况下,在命令列表中使用NOEXEC标志。具体列举必须运行其他命令的命令。当用户打电话抱怨他们无法运行某些命令时,你会经历艰难的几天,但你会很快找到合法必须运行其他命令的命令。当您在网络上自动分发单个sudoers文件时,这些更改将快速传播到所有主机。

为用户、命令、主机和运行方式设置使用别名。在规则中使用别名而不是命令名。这简化了更改,并有助于确保您的所有用户对其组中的其他用户具有相同的访问权限。

通过适当的配置,可以消除大多数逃避限制的方法。“正确配置”通常意味着“准确说明允许的访问权限”。不要只是让人们无限制地访问所有命令;相反,要弄清楚谁应该做什么,以及他们需要什么样的机会来做真正的工作。是的,这意味着要花时间和精力与有自己观点和愿望的活生生的人进行面对面的交谈,而不是做有趣的计算工作。

犹豫是否通过sudo为shell脚本授予root权限。虽然sudo会净化用户的shell环境,但shell脚本可以将那些可怕的东西放回原处。在很多情况下,通过sudo以root身份运行shell脚本相当于给用户root。即使您使用加密摘要验证来确保脚本在未经编辑的情况下运行,shell脚本也经常拉入其他shell脚本。用户和入侵者可以使用环境变量破坏任意数量的shell脚本。不要以为你的用户是不同的,不会弄乱你精心编写的shell脚本。他们不是,他们会的。

在某些主机上,严格的sudo配置是不现实的。台式机运行许多运行其他程序的程序。具有机器物理访问权限并需要运行图形桌面的用户可以轻松地在机器上获得root级别的访问权限。您的最佳做法是假设台式机不值得信赖,并保护您的服务器免受恶意工作站和外部入侵者的攻击。

如果你不愿意制定一个真正的sudoers政策,那么就不要浪费时间拼凑出一个半生不熟的sudoer政策,这种政策基本上或多或少都能满足你的需求。相反,让用户无限制地访问并处理后果。在经历了足够多的不必要的停机时间、系统损坏以及失去的夜晚和周末之后,你会愿意制定一个真正的sudoers政策。记录用户活动(见第12章)可以帮助准确评估出错时发生了什么,这可能是您组织的一个很好的替代方案。

主机名和Sudoers

在每台机器上单独管理sudoers时,策略的主机名部分往往会从系统管理员的视图中消失。它仍然在文件中,但你的意识不再看到它。它只是 "that 'ALL =' thing" ,必须出现在每个规则的中间。到目前为止,我还没有给予太多关注,因为我们只考虑了单一系统策略。当你想在整个网络中使用一个sudoers文件时,hostname 字段突然变得更加重要。

Sudo通过运行 hostname 获取本地计算机的名称。sudoers策略中的主机名必须与本地计算机认为的主机名完全匹配。这可能会在异构网络中造成困难。我的Linux服务器通常有一个由单个单词组成的主机名,比如www8或sip2。我的BSD机器有一个包含域的主机名,例如www.michaelwlucas.com。在开始编写集中式sudoers策略之前,请调查您的命名方案,因为它实际上部署在真实的服务器上。它们是否一致?如果你使用的是集中式服务器配置,你可能没问题。如果您仍在运行手工管理的服务器,或者手动安装服务器,则会出现不一致。在制定政策之前,先解决这些不一致之处。或者使用DNS或IP地址。

DNS 和 Sudo

域名系统将主机名映射到IP地址。服务器可能认为它的名字是 www8 ,但DNS将其记录为 www8.michaelwlucas.com 。DNS是集中管理的(主要是;稍后会详细介绍)。让sudo引用DNS来获取机器名可以消除任何本地主机名不一致的问题。它还增加了对DNS进行机器管理的依赖性。如果您的DNS服务器发生故障,sudo将无法运行。如果由于DNS关闭而无法运行sudo,并且由于sudo关闭而无法重新启动DNS,恭喜!你未能深入思考自己的失败模式。预计你当地的 Thea 很快就会来取你的尸体。

主机可能被配置为解析来自各种信息源(如YP或LDAP)的IP地址和主机名。如果服务器更喜欢这些信息源之一而不是DNS,那么您需要验证您的sudoers规则是否与该信息源中的主机名匹配。最常见的替代信息源是hosts文件/etc/hosts。检查您的服务器是否更喜欢主机表而不是DNS,如果是,请在该文件中确认服务器的名称。

一台机器在DNS和主机中都可以有多个主机名,但sudo只使用主主机名。Sudo忽略所有别名或其他记录。如果使用hosts文件,则只使用条目中的第一个主机名。如果您使用的是DNS,则忽略任何CNAME记录。Sudo只使用正向和反向DNS中显示的主机名。

要启用DNS,请在sudoers中使用fqdn选项。

Sudo仍然会检查本地主机名,如果sudoers规则恰好与本地名称匹配,则规则匹配。如果名称不匹配,sudo将使用DNS并将每个规则与服务器的完全限定域名进行比较。您需要完整的主机名,而不是使用短主机名www8。

sudoers文件中的行会更长,但没关系。此外,您的sudo命令将需要更长的时间,因为sudo会向DNS查询本地主机名。

然而,破坏基于主机名的保护的明显方法是让系统管理员更改本地主机的名称。如果您的sudoers策略允许其他没有特权的用户更改计算机名称,那么他可以更改应用于该计算机的策略。

IP 地址

我发现在我的sudoers策略中使用IP地址比使用主机名更可靠,至少在我的环境中是这样。在大型网络中,机器存在于不同的网段并具有不同的网络访问规则,系统管理员通常无法访问网络设备。流氓系统管理员可能会将web服务器的名称更改为数据库层上的主机名称,但如果不失去对计算机的访问权限,他就无法更改该服务器的IP地址。

使用主机别名定义这些网络子网。

为这些主机别名分配访问规则,有问题的用户绕过访问控制的唯一方法是将机器移动到另一个子网。最终,您如何设计sudoers策略以避免这些主机名更改取决于您的员工和用户、您的环境以及您的风险承受能力。

在Sudoers中包含文件

sudoers策略可以通过引用包含其他文件。这使您可以为所有系统制定通用的sudoers策略,并按机器角色或功能添加其他文件。您可以使用 #include 语句添加特定文件、按主机名添加文件或目录中的文件。

该文件将插入到sudoers策略中您使用include语句的位置。如果在sudoers顶部包含文件,则全局规则将覆盖所包含策略中的任何内容。如果你的include语句出现在sudoers的最后,那么包含的文件会覆盖全局策略。为什么这很重要?考虑一个包含以下行的文件:

wheel 组传统上是那些被允许使用 root 密码的用户,也称为“高级系统管理员”。根据您的操作系统,这可能是 admin 组或其他什么。包含的文件禁止wheel中的所有用户通过sudo运行任何命令。如果此规则在sudoers策略中最后出现,则会删除高级系统管理员对服务器的访问权限。这可能不是你想要的。

包含特定文件

也许您有一个分发到所有系统的sudoers安全策略的基础模板,这样您的高级系统管理员就可以访问所有服务器。每台机器都有根据系统需求量身定制的安全策略。在这种情况下,您将把 /etc/sudoers 复制到网络上的所有计算机上,并告诉本地用户将自己的规则放在不同的文件中,例如 /etc/sudoers.local 。在您的全局sudoers中添加 #include 语句。

在该文件中设置您的本地添加。

每个主机包含文件

也许您想包含一个基于本地主机名的文件。您可以使用 %h 转义符在文件中使用本地主机名。

www8 机器上,sudo会查找一个名为 /etc/sudoers.www8 的文件。

包括目录

包括一个文件对你来说还不够吗?Sudo允许您使用 #includedir 语句将所有文件包含在一个目录中。

许多Linux发行版都使用这种语法。这个想法是,你可以有一个中央的标准sudoers策略,然后根据机器的功能将其他策略复制到机器上。主机是Web服务器吗?将标准文件001-sudors.www 复制到include目录。数据库服务器?复制数据库文件。两者都有?然后复制两者。

这是管理sudoers策略的一种完全有效的方法。然而,当您的网络变得如此复杂时,您最好研究基于LDAP的安全策略(第11章),而不是通过本地文件管理sudo。

Sudo按照词法顺序读取和处理这些文件。在词序中,数字总是排在大写字母之前,大写字母总是排在小写字母之前。小写字母位于重音字符之前。每次在目录中运行纯ls时,您都会看到这种排序。你会看到数字的排序方式是1、11、12、2,然后是21。鼠这个词出现在沙鼠之前。控制排序的最简单方法是让所有包含的文件都以数字开头,并包含前导零。这样,策略文件 001-sudors.www 将在 100-sudoers.database 之前处理。文件 2-sudoers.wordpress 在两者之后都会被处理,因此请包含前导零。

或者使用基于LDAP的策略向每台机器显示一个一致的策略。你最终会更快乐。

包含文件时出错

如果 /etc/sudoers 中包含的文件在语法上无效,sudo将不会运行——就像你在 /etc/sudoer 本身中有语法错误一样。Visudo只检查一个文件的完整性,而不是sudoers文件中包含的所有内容。使用 -f 标志将visudo指向其他文件。

Visudo将打开此文件的副本,编辑副本,检查文件的语法,然后替换原始文件或告诉您修复错误,就像它对 /etc/sudoers 所做的那样。

网络中的单一用户

如果你运行了数百台机器,你已经有了一种将文件分发到所有机器的方法。Puppet、Chef、Ansible甚至rdist等工具使这几乎变得容易。在中央计算机上配置sudo并将sudoers文件推送到网络中的所有主机,并不能阻止有人编辑本地计算机的sudoers档案。但它改善了对这些变化的检测和恢复。它也比使用包含目录更容易——您可以将各种服务器分组,并使用这些组作为规则。

如果您正在集中管理sudo,我强烈建议在将其安装为 /etc/sudoers 之前,让每台本地计算机验证它是否可以解析新的sudoers文件。如果您在运行sudo 1.8.7的计算机上安装了一个在sudo 1.8.9上运行的sudoers文件,则可能包含了旧版sudo无法解析的选项或规则。如果sudo无法解析 /etc/sudoers ,sudo将不会运行。在将新文件复制到位之前,使用 visudo –cf 验证它将为您节省很多麻烦。我强烈推荐阅读Jan Piet Mens的博客文章“不要在家里尝试:/etc/sudoers” (http://jpmens.net/2013/02/06/don-t-try-this-at-the-office-etc-sudoers/)以及相关帖子,很好地描述了糟糕的sudoers政策在大型网络上造成的痛苦。(这很有趣,因为它发生在别人身上。)Mens还有一个Ansible的剧本,可以安全地分发 /etc/sudoers ,这样你就可以从他的痛苦中学习。

虽然在一个位置配置sudo策略并将其推送到所有主机比在每个主机上单独配置有明显的优势,但最好让sudo从LDAP读取其策略。