第七章:任意文件和随机程序

Unix的组成员模型有其局限性。例如,如果您的环境使用跨平台NFS,则单个用户只能是16个组的成员。你可能想把管理一个组的任务委托给一个特定的用户,但大多数类Unix系统都没有这种能力。(Solaris的功能系统非常接近。)有时,您希望PAM只读取允许访问服务的用户列表。

同样,您可能希望登录进程将程序作为PAM进程的一部分运行。这可能是NIS配置的一部分,也可能与防火墙或自定义应用程序的组件有关。

您可以将PAM模块用于这两个方面。我们将从Linux-PAM的pam_list文件开始。

检查文件: pam_listfile

传统的Unix系统包括每个应用程序的允许或禁止用户列表。经典的例子是 /etc/ftpusers ,它列出了禁止使用FTP的用户。如果你试图以 rootoperator 的身份使用FTP,FTP守护进程会检查此文件并将你踢出。

Linux-PAM的 pam_listfile 命令PAM读取包含列表的文本文件,并根据该列表允许或拒绝访问。您可以根据用户名或组以及终端设备、远程主机或远程用户或shell允许或拒绝访问。

pam_listfile的典型用法如下:

这是身份验证策略的一部分。我们设定这个例子是必要的,但它可能是必要的或足够的。(将pam_listfile配置设置为可选会有点奇怪,但我不会说这永远都不合适。)

这个 item 就是你在列表中要找的。在这种情况下,我们正在检查用户名。您可以检查shells、terminals等。

sense 是pam_listfile在文件中有项目时的相应方式。在这里,如果用户名在文件中,我们告诉pam_listfile拒绝请求。

Pam_listfile需要知道要检查的 file

最后,我们使用 onerr 设置说明pam_listfile在发生意外错误时的行为。这不是像“用户不在文件中”这样的错误,而是“文件不可读”或“内核告诉我关闭”。在这里,如果pam_listfile有错误,我们告诉它返回成功。

此示例实现了传统的 /etc/ftpusers 功能。文件中列出的用户名可能无法通过此服务的身份验证。

让我们分别看看这一部分。

Pam_listfile Items(项目)

虽然用户名是允许或阻止访问的最明显方式,但pam_listfile允许其他选项。

您可能希望根据用户是否在控制台上来允许或拒绝访问。虽然有一个pam_console模块,但它为控制台用户配置环境。在大多数组织中,您登录控制台的唯一时间是在灾难中。

假设您希望系统管理员能够在控制台上运行 su(1) ,但在远程登录时,他们必须使用 sudo(1) 。将这样的条目添加到 /etc/pam.d/su

通过这样的语句,pam_listfile将用户的终端(tty)与 /etc/ttylist 文件进行比较。如果找到匹配项,它将返回由感测设置提供的访问权限——在本例中为allow。

/etc/ttylist文件需要包含这样的列表:

基于虚拟终端的阻塞更加困难。Linux根据需要创建虚拟终端。您不能在pam_listfile文件中使用通配符或正则表达式,因此您需要列出比实际更多的虚拟终端。

允许或阻止基于远程主机的访问可以通过 rhost 项完成。由于pam_listfile不支持任何类型的通配符或网络掩码,因此它几乎总是执行此类检查的错误位置。如果您绝对必须在PAM中使用远程主机匹配,请使用包含单个IP地址的文件,并为持续的低级烦恼做好准备。

您可以根据用户的shell允许或拒绝身份验证,如 /etc/passwd 中所示。在这里,我拒绝对使用 /etc/shelllist 中列出的shell的所有用户进行身份验证:

然而,没有什么能阻止用户更改其shell、进行身份验证,然后恢复其首选shell。您不会经常看到基于shell的阻止或许可列表。

组成员身份是pam_listfile可以处理的另一个标准。如下所述,它并不像你想象的那样工作,但这里有一个例子:

如果用户是 /etc/grouplist 中列出的组的成员,则她可以访问。如果不是,她就不能访问。

使用带有组成员资格的pam_listfile是说明pam如何破坏你一整天的好方法。你可以使用pam_wheel语句只允许 wheel 组的成员使用服务,然后添加一个如上所述的pam_list.file语句,禁止 /etc/grouplist 中列出的组中的任何人使用同一服务。现在将 wheel 添加到 /etc/grouplist 。现在,您有了一个完全有效的PAM配置,可以阻止所有人进行身份验证。

Pam_listfile Sense(含义) and File(文件)

file 语句告诉pam_listfile在哪里可以找到要查找的项目列表。每个项目都应该在自己的行上。与配置文件一样,使用哈希标记(#)开始注释。

sense 语句告诉pam_listfile如何处理该列表。将sense设置为 allow 将告诉pam_listfile只允许访问文件中列出的项。设置意义以 deny 阻止文件中的任何项目。

Pam_listfile Errors(错误)

你打破了一些东西,通常是无意的。onerr 语句告诉pam_listfile当它出现问题时该怎么办。将onerr设置为 success 会告诉pam_listfile在出现问题时允许访问。将onerr设置为 fail 会导致其关闭失败,拒绝访问。

不,“我的用户名不在文件中”不是PAM错误。据大多数人说,最常见的错误是缺少列表文件。也许您只想允许访问 /etc/access 中列出的帐户。如果该文件丢失,pam_listfile是否应该允许访问?还是应该拒绝?没有普遍正确的答案,但有一个可以让你选择你最喜欢的失败模式。

Pam_listfile和更改用户名

当用户更改用户名时,例如使用 su(1)sudo(1) ,您将在pam_listfile中看到奇怪的行为。Pam_listfile根据目标用户而不是请求用户检查访问权限。它根据文件检查PAM_USER项,而不是PAM_RUSER。

假设您在 /etc/access 中列出了用户 mwl ,并将 su(1) 配置为在允许访问之前使用pam_listfile检查 /etc/sulist 中的用户名。如果用户 mwl 运行 su root ,他将被拒绝访问。在这种情况下,mwl 是请求用户,而不是目标用户。这意味着您可以使用pam_listfile来限制某人可以向哪些帐户进行 su(1) ,但不能从哪些帐户进行。

然而,FTP等服务不会更改用户名。没有请求用户,只有请求身份验证的用户。Pam_listfile适用于这些服务。或者,您可以使用稍后讨论的pam_exec。

OpenPAM 对比 pam_listfile

OpenPAM不包含pam_listfile。它甚至不是大多数基于BSD的系统的可用软件包。

这似乎是一个明显缺失的部分,但当需要时,这些部分会被添加到典型的基于OpenPAM的系统中。在OpenPAM系统上需要pam_listfile的人都没有提交添加它的请求。在某种程度上,这是因为您可以通过运行脚本轻松复制pam_listfile。接下来我们再看看。

运行程序: pam_exec

如第3章和第6章所述,PAM可以使用PAM_exec运行任意命令。如果程序成功运行(返回0),pam_exec将返回success。如果程序返回错误或根本无法运行,pamexec将返回failure。

为什么要使用pam_exec?您可以按照第3章中的讨论提取PAM环境项。教科书中的例子是在用户更改密码后重建YP数据库。但是,您也可以使用pam_exec来实现一些功能,例如在允许访问之前根据用户列表检查用户名,就像pam_listfile一样。

需要考虑的一件事是,pam_exec会为每个身份验证请求启动一个进程。如果许多人向您的系统进行身份验证,这些过程可能会带来很大的负载。验证你的“任意命令”不是隐喻性的,相当于一群穿着苏格兰短裙的蓝色小个子男人,他们认为你的系统资源会让人大饱眼福。然而,对于大多数环境来说,简单的命令和脚本应该是可以的。

配置 pam_exec

可以将命令直接放入PAM配置文件中。除了最简单的命令,我发现这个命令很脆弱,很容易被破坏。你可能不喜欢,但既然我是写这本书的人,我们就按我的方式去做。相反,将您希望PAM执行的命令放在脚本中,并让pam_exec调用该脚本。

pam_exec的最简单调用只需要一个选项,即要运行的命令:

当用户尝试访问他们的帐户时,PAM会运行脚本 pamvarlog.sh 。我们在第3章中使用了此脚本来收集PAM环境变量。

OpenPAM和Linux-PAM都为pamexec提供了额外的选项,但我们将首先介绍基本功能。具体来说,我们将完全通过shell命令实现pam_listfile的基本功能。

在pam_exec中实现pam_listfile

我们将有一个允许访问服务的用户列表。如果文件中存在用户名,则授予访问权限。如果没有,则拒绝访问。

PAM将其通常的一批项目交给我们的脚本。如果您不确定特定服务器设置了哪些项,请在第3章中使用 pamvarlog.sh 脚本捕获它们。幸运的是,大多数身份验证过程都需要PAM_USER。

在检查目标文件中的用户名时,我们需要小心一点。我们不想匹配评论。我们只想匹配完整的用户名。使用一点 grep(1) 就可以了。这是一个概念验证脚本,它根据 /etc/validuser 检查用户,如果用户名出现在那里,则允许访问。

此脚本返回 grep(1) 语句返回的任何内容。如果 grep(1) 找到匹配项,则返回0。如果没有匹配,则返回1。记住,pam_exec将0视为成功,将1视为失败。

如果你想拒绝 /etc/bogususers 中列出的用户访问怎么办?这是一个巨大的、侵入性的变化:添加一个感叹号:

此脚本反转 grep(1) 语句的返回代码。

基于这些示例,创建符合您需求的脚本。至少,您必须对输入进行消毒。清除输入的方法取决于系统的默认根shell——bash的方法与传统sh不同。

Pam_exec 对比 Modules

将pam_exec用于所有事情似乎是解决许多问题的一种简单明了的方法。我们是系统管理员。我们喜欢shell脚本。“你想通过NoSQL数据库进行身份验证吗?我会写一个shell脚本!”

现实生活并非如此简单。

当函数存在PAM模块时,通常最好使用它,而不是编写自己的脚本。模糊但公开可用的PAM模块可能比您的shell脚本拥有更多的用户。最糟糕的bug已经被发现了,最棒的是,它们已经被别人发现了。如果你写自己的脚本,你就可以自己找到这些bug。

使用pam_exec应该被视为最后的选择。不过,这是一个非常有用的最后选择。

使用pam_exec时要记住的一件事是,Linux-PAM和OpenPAM实现pam_exec的方式不同。

OpenPAM pam_exec

OpenPAM的pam_exec只有一个选项,即调整返回代码。

默认配置中的OpenPAM pam_exec返回两个响应之一。如果命令返回0,pam_exec将返回pam_UCCESS。如果命令返回任何其他内容,pam_exec将返回PAM_PERM_DENIED。

pam_exec的 return_prog_exit_status 选项会改变它返回的内容。与简单的“是”或“否”不同,return_prog_exit_status让脚本返回实际的PAM返回代码,如第4章所述。您的脚本需要为调用它的服务模块函数返回一个有效的返回代码。在Linux-PAM中,您可以检查PAM_SM_FUNC项以查看调用pam_exec的函数。每个服务模块功能的手册页列出了可接受的返回代码。pam_exec知道哪些函数可以接受哪些返回代码。如果您的脚本试图返回不可接受的代码,pam_exec将替换为PAM_SERVER_ERR。

这是OpenPAM pam_exec支持的唯一选项。虽然Linux-PAM的pam_exec支持更多选项,但这些选项几乎都可以在脚本中实现。

Linux-PAM pam_exec

pamexec的Linux PAM版本具有调试、日志记录和处理权限和密码的选项。

debug 选项将调试信息发送到系统日志,与第1章中讨论的完全相同。

您的pam_exec命令可能会生成输出。该输出通常会被丢弃,但您可以使用 log=stdout 选项。通过定义日志文件,您可以将任何输出发送到该文件。stdout 选项将程序的输出发送到标准输出,让调用程序处理该输出。

即使命令通常不生成输出,pam_exec也会对程序抛出的任何错误进行响应。quiet 选项禁用这些消息。

type= 选项告诉pam_exec只有在pam策略类型匹配时才运行该命令。例如,您可以设置type=account,这样这个特定的pam_exec只在帐户策略上运行。

通过添加 expose_authtok 选项,您可以在标准输入下将用户的密码交给脚本。系统变量PAM_MAX_REPS_SIZE规定了最大密码长度,但通常约为512字节。

最后,通过添加 seteuid 标志,您可以使用正在进行身份验证的进程的有效UID运行程序,而不是使用正在接受身份验证的用户。

PAM 对比 SELinux

安全增强型Linux(Security Enhanced Linux —— SELinux)为Linux系统添加了细粒度的访问控制。它通常出现在CentOS类型的系统中。SELinux经常阻止附加PAM模块正常运行。pam_ssh模块,第10章的明星,就是其中之一,所以我们将把它作为一个例子。

修复与SELinux相关的问题需要验证SELinux是否导致了您的问题,然后调整系统安全策略以允许此模块运行。

Is It SELinux?

与SELinux相关的错误通常首先表现为“这真的应该奏效”的感觉。如果程序在登录期间无法运行,或者它几乎可以工作,但一些重要功能崩溃,请检查SELinux错误。

第10章讨论了使用pam_ssh进行工作站身份验证。pam_ssh模块使用用户的ssh密钥对本地计算机的控制台进行身份验证,启动ssh代理 /usr/bin/ssh 代理,并将ssh密钥添加到代理中。(如果你不知道这意味着什么,就拿一本《SSH Mastery》[Tilted Windmill Press,2012])。您还不知道pam_ssh,但这对于诊断其SELinux问题并不重要。

如果你在最新版本的CentOS上部署pam_ssh,你将能够进行身份验证,但该模块不会启动ssh代理。将调试标志添加到pam_ssh语句中,这样的消息就会出现在 /var/log/secure 中。

然而,一旦你有了命令提示符,你就可以很好地运行ssh-agent。

这令人费解。这是毫无意义的。检查是否存在SELinux问题。

虽然您可以在 /var/log/audit/audit.log 中搜索被拒绝的语句,但验证问题来自SELinux的最权威的方法是告诉SELinux记录策略违规,但不要阻止它们。这被称为许可模式。这最好在测试系统上完成。使用 setenforce(8) 切换执行模式:

现在尝试重新登录。禁用SELinux后,pam_ssh可以很好地启动其ssh代理。这是一个SELinux问题。将SELinux转回 enforcing 模式:

问题再次出现。是的,它是SELinux。

现在创建一个SELinux策略以允许此PAM模块运行。

创建SELinux策略

在尝试更改系统的SELinux策略之前,请安装SELinux管理工具。在CentOS上,setroubleshot包包含您需要的一切。我们将从audit2allow命令开始,该命令读取审计日志并创建策略。

搜索审计日志以确定SELinux是如何阻止应用程序的。在这里,我搜索关键字“denied”和我的PAM模块的名称PAM_ssh。

如果没有得到任何结果,请尝试删除PAM模块名称。SELinux可能正在阻塞与该模块相关的辅助进程。不过,当你得到结果时,把它们输入 audit2allow 。使用 -m 和模块名称根据错误消息打印SELinux策略模块建议:

棘手的部分来了。您了解SELinux访问控制列表吗?如果是这样,请阅读拟议的政策,并确保其看起来合理。如果没有,要么找一个有SELinux专业知识的人,让他们看看,要么决定盲目信任audit2allow。

要在系统上创建实际的SELinux策略,请使用带有-M标志和实际模块名称的audit2allow。这只会制定政策;它不会激活它。

你不是那种无视指示的人,是吗?按照好软件告诉你的去做。

PAM模块现在应该可以工作了,或者至少它应该继续处理下一个错误。什么是进步,不是吗?

一旦你有了一个可用的SELinux策略,你就可以将包含该策略的 .pp 文件复制到其他系统并安装它。你必须确保SELinux的策略版本匹配,并且该策略中引用的类型存在于其他系统中。最好的方法是在测试和生产环境中使用相同的操作系统版本。

SELinux 和 pam_mkhomedir

pam_mkhomedir模块在用户登录时创建丢失的主目录。它用于部署集中式身份验证的组织,如LDAP。这本书没有涵盖这个模块,因为它非常琐碎。

pam_mkhomedir的一个问题是它不支持SELinux。红帽将SELinux设想为其操作系统的核心部分。红帽公司没有更新pam_mkhomedir,而是用SELinux友好的pam_oddjob_mkhomedr替换了pam_mkhomadir。解决方案是存在的,所以这对大多数CentOS管理员来说都很好。

然而,我曾在多个多平台企业工作过,在这些企业中,公司安全策略要求使用pam_mkhomedir。也许安全团队已经有一段时间没有更新其政策了。也许这是跨平台标准化推动的一部分。我甚至看到过这样一个案例:“该死的,该死的系统管理员一直在用pam_exec做任何事情!”调整SELinux安全策略可以让你在CentOS上使用pam_mkhomedir。

最终,您将无法在CentOS上获得pam_mkhomedir。与其调整政策并继续你的一天,不如与你的安全团队谈谈。找出他们试图解决的问题。帮助他们更新标准或以其他方式解决问题。

但是,如果您真的想使用组织的安全策略,则需要附加PAM模块。