制定一个sudoers政策很简单。你只要写下谁可以在哪台机器上运行什么。还有什么比这更容易的呢?现在,对500名用户重复上述操作。确保具有共同功能的用户具有相同的安全规则。那些Oracle数据库管理员呢?您必须包含每个管理员需要作为单独用户运行的每个命令。
如果你必须用sudoers写下所有这些,你只需在休息室的墙上喷涂根密码即可。
更复杂的是,类Unix系统从大量来源获取信息。其中一些甚至不是模糊的Unixy。如果服务器连接到Active Directory或NIS域,则可能需要在安全策略中使用该信息。也许您想要一个规则,即“域管理员组中的所有用户都可以挂载CIFS共享”。您需要知道如何将此信息引入您的sudoers策略。
Sudoers提供别名来压缩和简化安全策略。别名是可以在sudoers规则中使用的预定义项目列表。您可以在任何使用用户名、主机或命令的地方使用别名。更改别名是一种简单、有效且保证一致的方法,可以在复杂的sudoers文件中进行更改。
但在我们讨论这些之前,让我们先看看通配符。
通配符是一种可以匹配不同类型字符的特殊符号。Sudoer允许您使用通配符来匹配主机、文件系统路径和命令行参数。Sudoer通配符看起来很像shell或Perl正则表达式,但事实并非如此。通配符是基于操作系统的 glob
和 fnmatch
函数构建的。如果操作系统的 glob
和 fnmatch
函数支持字符类,则可以在通配符中使用类。如果你不知道什么是字符类,不用担心。
假设你的网络有几个域名服务服务器,所有服务器的主机名都是dns1、dns2、dns3等。你可能不会给非DNS服务器一个以这些字符开头的名称。您的DNS管理员需要完全访问这些服务器,因此您可以在主机定义中使用通配符:
xxxxxxxxxx
fred dns? = ALL
问号(?)匹配任何单个字符。此sudoers规则适用于任何主机dns0到dns9。它还通适用于 dnsA 到 dnsZ。也许你只有DNS服务器1到4,没有预见到任何扩展,也不想在出现的任何新DNS服务器上自动授予常规DNS管理员特权访问权限:
xxxxxxxxxx
fred dns[1-4] = ALL
通过在括号中指定字符范围,可以限制匹配。
您可以在括号中使用一系列字母:
xxxxxxxxxx
pete www[a-z] = ALL
Pete可以在服务器wwwa到wwwz上运行任何命令。没有多少人这样使用字母,但这是一种选择。您还可以使用大写字母,范围A-z匹配所有大写和小写字母。
xxxxxxxxxx
pete www[A-z] = ALL
如果要匹配一个类型的多个字符,请附加星号:
xxxxxxxxxx
fred dns[0-9]* = ALL
如果你最终有了服务器dns9183,Fred可以管理它。我敢肯定,到那时他会很累的,所以希望你能使用用户别名来给他一些帮助。
更一般地说,星号字符可以匹配任意数量的字符,也可以根本不匹配。它匹配一切,除了一些故意的例外。如果Thea需要Pete来管理服务器的核心功能,她可以给他一个这样的规则:
xxxxxxxxxx
pete ALL = /sbin/*, /usr/sbin/*, /usr/local/sbin/*
Pete可以在任何常见的sbin目录中运行任何命令。Visudo可能位于其中一个目录中,因此Pete可以更改自己的权限。Thea需要学习访问控制的细节,或者可能将 visudo
移动到私有目录。
用于命令时,星号与用于分隔目录的斜线字符不匹配。如果你想让用户访问子目录中的所有程序,你必须明确指定该子目录:
xxxxxxxxxx
pete ALL = /usr/bin/*, /usr/bin/X11/*
但是,当用于命令行参数时,星号确实与斜线匹配。毕竟,命令可能会在参数中包含斜线。它们可能包括空格、任何文本字符串,或者谁知道是什么。
这意味着系统管理员需要小心使用通配符作为命令行参数。很难击败教科书中危险通配符规则的例子:
xxxxxxxxxx
pete ALL = /bin/cat /var/log/messages*
Pete可以看到 /var/log/messages
的内容,以及 /var/log/message
等轮换日志。这似乎是无害的。但是通配符可以匹配任意数量的字符,所以Pete可以运行这样的命令:
xxxxxxxxxx
$ sudo cat /var/log/messages /etc/shadow
这肯定不是系统所有者的意思。
解决这个问题很容易。问号与单个字符匹配:
xxxxxxxxxx
pete ALL = /bin/cat /var/log/messages, \
/bin/cat /var/log/messages??
或者Thea可以使用一系列数字:
xxxxxxxxxx
pete ALL = /bin/cat /var/log/messages, \
/bin/cat /var/log/messages.[0-9]
当然,更小的数字范围是有效的。
有时,您必须匹配选定的字符,而不是范围。您可能需要匹配字符A、c或q中的任何一个。无法将这些表示为范围,但您可以匹配方括号中的特定字符:
xxxxxxxxxx
pete ALL = /opt/bin/program –[Acq]
此模式匹配括号中指定的单个字符,允许您安全地允许用户访问特定的命令行参数。
字符 *
、?
、[
和 ]
在sudoers中有特定的含义。如果你需要匹配其中一个字符,请在它前面加一个反斜杠。下面例子我们允许参数 [
和 ]
:
xxxxxxxxxx
carl ALL = /opt/bin/program –[\[\]]
现在,你可以允许你想要的任何论点组合。
也许你特别想禁止使用任何论据。两个双引号之间没有空格,告诉sudoers只匹配空字符串:
xxxxxxxxxx
dirk ALL = /opt/program ""
Dirk只有在不给出任何参数的情况下才能运行指定的程序。
通配符与别名结合使用特别有用。
别名是类似项目的命名列表。您可以使用别名来指代运行该命令的用户、运行sudo的主机、运行命令的用户或正在运行的命令。举个简单的例子,让我们制作一个别名,其中包括使用传统转储备份和恢复类Unix系统的命令:
xxxxxxxxxx
Cmnd_Alias BACKUP = /sbin/dump, /sbin/restore, /usr/bin/mt
可以运行这些命令的用户可以创建和部署备份。谁有这份吃力不讨好的工作?
xxxxxxxxxx
mike ALL = BACKUP
我真幸运。
对于一个用户来说,别名似乎不是什么优势。但是,如果您有多个备份操作员,则可以为他们的用户名创建别名。下面,我为管理备份的人员创建了TAPEMONKEYS别名:
xxxxxxxxxx
User_Alias TAPEMONKEYS = mike, pete, hank
当你组合这些别名时,你可以编写这样的sudoers规则:
xxxxxxxxxx
TAPEMONKEYS ALL = BACKUP
两个别名声明和一个规则取代了一个更长的规则。你可以在没有别名的情况下编写完全相同的规则:
xxxxxxxxxx
mike,pete,hank ALL = /sbin/dump, /sbin/restore, /usr/bin/mt
这条语句更长,更难阅读。当你添加命令或用户时,它会变得更长。成功的磁带猴子将承担更多的职责,延长指挥名单。
使用别名会使人员和任务的更改立即渗透到所有用户中。不会有几十个剪切和粘贴的变化使你的大脑麻木的风险。
别名只能包含大写字母、数字和下划线。名称必须以大写字母开头。CUSTOMERS是一个有效的别名,但 _CUSTOMERS 和 2CUSTOMERS 不是。在使用别名之前,您必须先定义别名,因此人们通常会将所有别名放在sudoers的顶部。
现在让我们看看sudoers中发现的四种数据类型,如何扩展它们,以及如何在别名中使用它们。
还记得在第一章中我告诉过你,每个sudoers规则都以用户名开头吗?是啊,嗯……这并不完全正确。严格来说,每条规则都从一个用户 list 开始。用户名是此列表中最常见的条目类型,但还有更多。还有更多。
sudoers识别的用户名不一定是 /etc/passwd 中的用户名。我的组织通过LDAP管理用户帐户,sudoers识别LDAP用户名与本地用户名完全相同。但我的特定LDAP配置限制了用户名,使其看起来像本地用户名。您可能需要从Microsoft Active Directory、 /etc/group 、用户别名或仅由三个新几内亚部落和您的尖端组织使用的钝目录系统中提取信息。
Sudoers识别七种类型的用户列表。
Sudoers接受来自操作系统的组。在组名前面加一个百分号(%)。我可以创建 /etc/groups 条目 dba ,将我的数据库管理员添加到其中,并在sudoers中引用它。
xxxxxxxxxx
%dba db1 = (oracle) /opt/oracle/bin/*
dba组中的每个人都可以在服务器 db1 上以 oracle 的身份运行 /opt/oracle/bin 目录中的所有命令。
一些操作系统为可以成为root(Ubuntu上的 admin )或可以使用root密码(基于BSD的系统上的 wheel )的用户提供了一个系统组。默认的sudoers策略有一个例子,可以让这些用户无限制地访问系统。
xxxxxxxxxx
%wheel ALL = (ALL) ALL
这个组中的人无法通过此规则获得任何额外的访问权限—— wheel 的成员已经可以使用su成为root。但这可以让人们适应在日常(day-to-day)工作中使用sudo。
记住,使用 id
命令查看您的帐户是哪些组的成员。
您可以在sudoers中使用用户ID号,方法是在它们前面加一个哈希标记(#):
xxxxxxxxxx
#10000 ALL = /sbin/reboot
任何UID为10000的帐户都可以通过sudo重新启动任何计算机。我不知道你为什么想让这个用户到处重新启动一切,但我见过比这更奇怪的配置。
如果您有多个具有相同用户ID的用户帐户,则此规则适用于所有这些用户帐户。
如果不想使用组名,请使用以 %#
开头的组ID号。在传统的BSD系统中,wheel是组0:
xxxxxxxxxx
%#0 ALL = ALL
如果你的用户名服务不稳定,你可能想走这条路。我建议你改为修复名称服务,但你可能无法控制它。
与用户ID一样,如果您有多个具有相同GID的组,则此规则同样适用于这两个组。
如果您通过NIS管理系统,下一步应该是停止使用NIS。但是,在达到这一点之前,您可以在sudoers规则中引用网络组,方法是以加号(+)开头:
xxxxxxxxxx
+webmasters ALL = /opt/apache22/bin/*, /opt/apache22/sbin/*
您的网站管理员团队可以运行两个指定Apache目录中的任何程序。
如果你的sudo版本有必要的插件或额外的代码来支持检查组是否符合类Unix系统规范之外的信息源,你可以在sudoers中引用这些插件或代码。在它们前面加上 %:
:
xxxxxxxxxx
%:Admins ALL = ALL
许多非Unix目录服务在组名中使用空格或非ASCII字符。这些字符必须以某种方式逃脱。转义特殊字符很麻烦,因此请将整个组名(包括前导 %:
)括在双引号中:
xxxxxxxxxx
"%:Domain Admins" ALL = ALL
当对非Unix组有疑问时,请使用双引号。
当你运行id查看你的帐户属于哪些组时,非Unix组会出现在标准Unix组之后的输出中。
因此,您已将系统附加到非Unix目录,并希望使用这些外部组的数量而不是名称?没问题。在组号前加上 %:#
。是的,这是一个百分号、冒号和哈希标记。
xxxxxxxxxx
%:#87119301 ALL = ALL
然而,如果你发现自己需要这样做,我建议你退一步,重新考虑如何使用你的目录服务。
你的用户名列表可以包含一个用户别名,所以我们最好讨论一下。用户别名是系统用户的列表。所有用户别名定义都以字符串User_Alias开头:
xxxxxxxxxx
User_Alias SYSADMINS = thea
User_Alias MINIONS = mike, pete, hank, dirk
在这里,用户别名SYSADMINS包含一个用户 thea 。如果该组织获得了另一个完整的系统管理员,将他们的用户名添加到别名中将赋予新人与Thea相同的权限。
用户别名MINIONS包含四个用户。当Thea在sudoers规则中使用这个别名时,它会对所有四个小黄人产生相同的影响。当然,其他规则可能会改变个人仆从的访问权限。
您可以在用户别名中使用任何类型的用户名。
xxxxxxxxxx
User_Alias WHINERS = "%:Domain Users", %operator, MINIONS
记住,别名只能包含大写字母、数字和下划线。别名必须以大写字母开头。
sudoers中的hosts条目接受纯主机名以外的值。但让我们先谈谈那些纯粹的主机名。
Sudo通过运行hostname来确定本地主机的名称。它不依赖于DNS、 /etc/hosts 、LDAP或任何其他名称目录。除非主机名返回,否则传统的主机名 localhost 在规则中不起作用。(您可以使用fqdn选项更改此行为,我们将在第10章中对此进行研究。)这意味着sudoers中的主机名必须与本地计算机上设置的主机名匹配。更改主机名和sudo断点。如果hostname返回一个完全限定的域名(例如,www.michaelwlucas.com而不是www),那么sudoers只需要机器名,而不需要完整的域名。
除了使用本地主机名外,sudoers还可以接受各种IP地址和网络组。
Sudo可以区分主机名和IP地址,因此您不需要在IP地址前放置任何特殊标记:
xxxxxxxxxx
mike 192.0.2.1 = ALL
Sudo检查机器的所有真实网络接口的IP地址。它还检查连接到真实接口的接口,如VLAN接口和网桥。它忽略了诸如环回之类的逻辑接口。
您还可以在sudoers中使用网络,以虚线四边形(192.0.2.0/255.255.255.255.128)或无类域间路由(CIDR)格式(192.0.2.0/24)指定网络掩码。如果机器上的任何接口都在该网络中,则适用sudoers规则。
xxxxxxxxxx
pete 192.0.2.0/24 = ALL
mike 198.51.100.0/255.255.255.0 = /etc/rc.d/named *
对于在不同网络上具有多个接口的机器,请记住sudo使用最后一个匹配规则。如果两个网络的规则冲突,则最后一条规则获胜。
YP/NIS站点可以通过在名称前加+来引用sudoers中的网络组:
xxxxxxxxxx
carl +db = ALL
然而,对于我们大多数人来说,引用主机组的方式将是使用主机别名。
主机别名是主机的命名列表。用字符串Host_Alias
表示主机别名。主机别名可以包括sudo识别的主机名的任何变体:
xxxxxxxxxx
Host_Alias WWW = www[1-3]
您可以在另一个主机别名中包含一个:
xxxxxxxxxx
Host_Alias DMZ = 192.0.2.0/24, 198.51.100.0/255.255.255.0, WWW
与用户别名一样,主机别名只能包含大写字母、数字和下划线,并且必须以大写字母开头。然后,您可以在sudoers规则中使用此别名。
xxxxxxxxxx
mike DMZ = all
现在,我对DMZ组中的主机拥有完全权限。
通过将目标用户名放在命令前的括号中,可以授予用户以其他用户身份运行命令的权限。我们之前已经看到了如何做到这一点:
xxxxxxxxxx
chris beefy = (oracle) ALL
Chris可以作为用户 oracle 在主机 beefy 上运行任何命令。这些被称为RunAs特权(RunAs privileges)。
与用户名一样,RunAs用户也是列表。假设你有多个数据库平台——Oracle、MySQL和Postgres。您的数据库团队需要访问权限才能以数据库用户的身份在任何主机上运行命令。在用户列表中有效的任何类型的用户名在RunAs语句中都是有效的。
xxxxxxxxxx
carl ALL = (oracle, postgres, mysql) ALL
数据库管理员Carl可以在任何服务器上运行任何命令,只要他将其作为数据库用户程序之一运行即可。
如果你有可以运行命令的非Unix风格的用户,你可以编写包含它们的sudoers规则。
xxxxxxxxxx
pete ALL = ("%:Domain Users", %operator, lpd) ALL
您还可以让用户作为组的成员而不是特定用户运行命令。标准的Unix惯例是使用用户名、冒号和组名指定文件所有权。要编写允许以组成员身份运行命令的规则,请跳过用户名。您可能有只对组 staff 可见的日志文件。
xxxxxxxxxx
%helpdesk ALL = (:staff) cat /var/log/secure
帮助台工作人员可以像在组 staff 中一样运行此命令。
你现在可能已经掌握了这个窍门,但为了完整起见,让我们来谈谈RunAs别名。RunAs别名允许您对运行命令所需的用户进行分组。RunAs别名的名称只能包含大写字母、数字和下划线,并且必须以大写字母开头。
xxxxxxxxxx
Runas_Alias DB_USERS = oracle, postgres, mysql
您可以在任何想要使用用户名列表的地方使用字符串DB_USERS。
xxxxxxxxxx
carl DB = (DB_USERS) ALL
我们现在有一个可读的规则,允许Carl在DB别名中的任何服务器上以数据库用户的身份运行任何内容。如果Carl在数据库管理方面得到任何帮助,系统所有者可以用DB_ADMINS别名替换Carl的名字。
在某些方面,命令列表是最简单的列表。命令可以是带通配符的路径(/sbin/*)或完整的命令名(/sbin/dump)。您可以将这些命令放入列表中,正如我们已经看到的那样。
xxxxxxxxxx
mike ALL = /sbin/dump, /sbin/restore, /usr/bin/mt
没有办法拉入非Unix命令。文件系统上的内容是您必须使用的内容。
命令别名是指定名称的命令列表,标记为Cmnd_Alias。命令别名的规则与其他别名完全相同。命令别名可以包括其他命令别名:
xxxxxxxxxx
Cmnd_Alias HELPDESK = /usr/bin/passwd, BACKUP
您可以在任何使用命令的地方使用命令别名。
您可以在命令列表或命令别名之前使用标记。标记是更改命令运行方式的标志。我将在本书更合适的章节中详细介绍这十个标签的作用,但当你看到一个标签时,你应该能认出它。一个标签出现在命令列表之前,用冒号与命令隔开。
xxxxxxxxxx
mike ALL = NOEXEC: ALL
标签名称都是大写字母,没有任何数字或符号。标记会影响标记后列表中的所有命令。我们将在第6章中使用NOEXEC标签,所以现在不用担心它的含义。
有些规则比它们需要的更慷慨。让我们重新考虑一下卡尔的数据库访问权限。
xxxxxxxxxx
carl ALL = (oracle, postgres, mysql) ALL
Carl可以在组织中的所有计算机上以三个数据库用户的身份运行命令。然而,他并不需要在所有机器上都有这种访问权限。大多数机器上只安装了一个数据库服务器或客户端。很少有系统同时运行MySQL和Postgres。
在许多环境中,这种额外的访问可能并不重要。
如果Carl试图在运行PostgreSQL的系统上以 oracle 的身份运行命令,该命令将失败。
xxxxxxxxxx
$ sudo -u oracle sqlplus
sudo: unknown user: oracle
sudo: unable to initialize policy plugin
如果用户存在,这要归功于LDAP的奇迹,但没有软件,则命令将失败。如果软件存在但未配置,则命令将失败。如果软件已配置,但命令失败,则数据库可能未运行。如果Carl试图在PostgreSQL服务器上配置Oracle,高级系统管理员Thea需要和他谈谈。可能涉及轮胎铁。
当你编写复杂的策略时,你需要决定你愿意做多少工作来消除这种过度的访问。Carl在Oracle服务器上配置PostgreSQL的能力是否存在风险?如果是,就消除它。
记住!我在第二章中提到的角色?我们可以使用否定字符从列表中排除项目。
xxxxxxxxxx
User_Alias NOTSCUM = %wheel, !mike
NOTSCUM ALL = ALL
除了一个例外,组wheel的成员可以完全访问系统。Thea说,当我告诉她我对她舒适的椅子做了什么时,我可能会拿回我的访问权限。
否定对于主机、用户和运行方式别名非常强大。它不仅对命令别名无用,而且有害。命令列表包括特定命令的完整路径或带通配符的目录。
你会认为否定对命令列表是有效的。但用户可以复制文件。他们可以创建指向文件的链接。他们可以找到一种通过各种路径访问文件的方法。要了解为什么这是一个问题,这里有一个用于成为root的命令的别名。
xxxxxxxxxx
Cmnd_Alias BECOME_ROOT = /bin/sh, /bin/bash, /bin/tcsh, /usr/bin/su
这里有一个排除这些命令的sudoers规则。
xxxxxxxxxx
%wheel ALL = ALL, !BECOME_ROOT
这似乎有效。如果我试图运行禁止的命令,sudo告诉我不被允许,并记录错误。不过,作为一个聪明得令人恼火的用户,我尝试了以下方法:
xxxxxxxxxx
$ cp /bin/sh /tmp/mycommand
$ sudo /tmp/mycommand
# id
uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)
哎呀。系统管理员排除了 /bin/sh ,但没有排除作为 /tmp/mycommand 安装的 /bin/sh 副本。当然不是我自己编译并安装在主目录中的 zsh
副本。
您不能使用排除项从列表中删除命令。无法安全地排除命令。sudo的作者对此进行了广泛的记录,恳求人们不要这样做,但世界各地的系统管理员仍然坚持这样做。没有什么比在sudoer命令列表中使用排除项更能大喊“我不读说明书!”的了。排除用户。不包括机器。甚至排除运行方式别名。但不要排除命令。
使用 sudo –l
检查权限的用户将看到扩展的别名,而不是别名或其定义。
xxxxxxxxxx
$ sudo -l
Password:
User mike may run the following commands on this host:
(root) ALL, !/bin/sh, /bin/bash, /bin/tcsh, /usr/bin/su
我没有看到BECOME_ROOT别名,所以我不知道Thea是如何写这项政策的。我确实知道如何在这台机器上扎根,而Thea却一无所知。因为没有正确配置sudo的系统管理员肯定也不会查看日志(见第12章)。
别名是合理化和简化您的sudoer策略的一种简单方法。现在让我们看看如何通过选项和默认值更改sudo行为的核心。