Unix的组成员模型有其局限性。例如,如果您的环境使用跨平台NFS,则单个用户只能是16个组的成员。你可能想把管理一个组的任务委托给一个特定的用户,但大多数类Unix系统都没有这种能力。(Solaris的功能系统非常接近。)有时,您希望PAM只读取允许访问服务的用户列表。
同样,您可能希望登录进程将程序作为PAM进程的一部分运行。这可能是NIS配置的一部分,也可能与防火墙或自定义应用程序的组件有关。
您可以将PAM模块用于这两个方面。我们将从Linux-PAM的pam_list文件开始。
传统的Unix系统包括每个应用程序的允许或禁止用户列表。经典的例子是 /etc/ftpusers ,它列出了禁止使用FTP的用户。如果你试图以 root 或 operator 的身份使用FTP,FTP守护进程会检查此文件并将你踢出。
Linux-PAM的 pam_listfile 命令PAM读取包含列表的文本文件,并根据该列表允许或拒绝访问。您可以根据用户名或组以及终端设备、远程主机或远程用户或shell允许或拒绝访问。
pam_listfile的典型用法如下:
xxxxxxxxxx
auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
这是身份验证策略的一部分。我们设定这个例子是必要的,但它可能是必要的或足够的。(将pam_listfile配置设置为可选会有点奇怪,但我不会说这永远都不合适。)
这个 item 就是你在列表中要找的。在这种情况下,我们正在检查用户名。您可以检查shells、terminals等。
sense 是pam_listfile在文件中有项目时的相应方式。在这里,如果用户名在文件中,我们告诉pam_listfile拒绝请求。
Pam_listfile需要知道要检查的 file 。
最后,我们使用 onerr 设置说明pam_listfile在发生意外错误时的行为。这不是像“用户不在文件中”这样的错误,而是“文件不可读”或“内核告诉我关闭”。在这里,如果pam_listfile有错误,我们告诉它返回成功。
此示例实现了传统的 /etc/ftpusers 功能。文件中列出的用户名可能无法通过此服务的身份验证。
让我们分别看看这一部分。
虽然用户名是允许或阻止访问的最明显方式,但pam_listfile允许其他选项。
您可能希望根据用户是否在控制台上来允许或拒绝访问。虽然有一个pam_console模块,但它为控制台用户配置环境。在大多数组织中,您登录控制台的唯一时间是在灾难中。
假设您希望系统管理员能够在控制台上运行 su(1)
,但在远程登录时,他们必须使用 sudo(1)
。将这样的条目添加到 /etc/pam.d/su :
xxxxxxxxxx
auth required pam_listfile.so item=tty sense=allow file=/etc/ttylist onerr=succeed
通过这样的语句,pam_listfile将用户的终端(tty)与 /etc/ttylist 文件进行比较。如果找到匹配项,它将返回由感测设置提供的访问权限——在本例中为allow。
/etc/ttylist文件需要包含这样的列表:
xxxxxxxxxx
tty1
tty2
tty3
tty4
基于虚拟终端的阻塞更加困难。Linux根据需要创建虚拟终端。您不能在pam_listfile文件中使用通配符或正则表达式,因此您需要列出比实际更多的虚拟终端。
允许或阻止基于远程主机的访问可以通过 rhost 项完成。由于pam_listfile不支持任何类型的通配符或网络掩码,因此它几乎总是执行此类检查的错误位置。如果您绝对必须在PAM中使用远程主机匹配,请使用包含单个IP地址的文件,并为持续的低级烦恼做好准备。
您可以根据用户的shell允许或拒绝身份验证,如 /etc/passwd 中所示。在这里,我拒绝对使用 /etc/shelllist 中列出的shell的所有用户进行身份验证:
xxxxxxxxxx
auth required pam_listfile.so item=shell sense=deny file=/etc/shelllist onerr=succeed
然而,没有什么能阻止用户更改其shell、进行身份验证,然后恢复其首选shell。您不会经常看到基于shell的阻止或许可列表。
组成员身份是pam_listfile可以处理的另一个标准。如下所述,它并不像你想象的那样工作,但这里有一个例子:
xxxxxxxxxx
auth required pam_listfile.so item=group sense=allow file=/etc/grouplist onerr=succeed
如果用户是 /etc/grouplist 中列出的组的成员,则她可以访问。如果不是,她就不能访问。
使用带有组成员资格的pam_listfile是说明pam如何破坏你一整天的好方法。你可以使用pam_wheel语句只允许 wheel 组的成员使用服务,然后添加一个如上所述的pam_list.file语句,禁止 /etc/grouplist 中列出的组中的任何人使用同一服务。现在将 wheel 添加到 /etc/grouplist 。现在,您有了一个完全有效的PAM配置,可以阻止所有人进行身份验证。
file 语句告诉pam_listfile在哪里可以找到要查找的项目列表。每个项目都应该在自己的行上。与配置文件一样,使用哈希标记(#)开始注释。
sense 语句告诉pam_listfile如何处理该列表。将sense设置为 allow 将告诉pam_listfile只允许访问文件中列出的项。设置意义以 deny 阻止文件中的任何项目。
你打破了一些东西,通常是无意的。onerr 语句告诉pam_listfile当它出现问题时该怎么办。将onerr设置为 success 会告诉pam_listfile在出现问题时允许访问。将onerr设置为 fail 会导致其关闭失败,拒绝访问。
不,“我的用户名不在文件中”不是PAM错误。据大多数人说,最常见的错误是缺少列表文件。也许您只想允许访问 /etc/access 中列出的帐户。如果该文件丢失,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。它甚至不是大多数基于BSD的系统的可用软件包。
这似乎是一个明显缺失的部分,但当需要时,这些部分会被添加到典型的基于OpenPAM的系统中。在OpenPAM系统上需要pam_listfile的人都没有提交添加它的请求。在某种程度上,这是因为您可以通过运行脚本轻松复制pam_listfile。接下来我们再看看。
如第3章和第6章所述,PAM可以使用PAM_exec运行任意命令。如果程序成功运行(返回0),pam_exec将返回success。如果程序返回错误或根本无法运行,pamexec将返回failure。
为什么要使用pam_exec?您可以按照第3章中的讨论提取PAM环境项。教科书中的例子是在用户更改密码后重建YP数据库。但是,您也可以使用pam_exec来实现一些功能,例如在允许访问之前根据用户列表检查用户名,就像pam_listfile一样。
需要考虑的一件事是,pam_exec会为每个身份验证请求启动一个进程。如果许多人向您的系统进行身份验证,这些过程可能会带来很大的负载。验证你的“任意命令”不是隐喻性的,相当于一群穿着苏格兰短裙的蓝色小个子男人,他们认为你的系统资源会让人大饱眼福。然而,对于大多数环境来说,简单的命令和脚本应该是可以的。
可以将命令直接放入PAM配置文件中。除了最简单的命令,我发现这个命令很脆弱,很容易被破坏。你可能不喜欢,但既然我是写这本书的人,我们就按我的方式去做。相反,将您希望PAM执行的命令放在脚本中,并让pam_exec调用该脚本。
pam_exec的最简单调用只需要一个选项,即要运行的命令:
xxxxxxxxxx
account required pam_exec /usr/local/scripts/pamvarlog.sh
当用户尝试访问他们的帐户时,PAM会运行脚本 pamvarlog.sh 。我们在第3章中使用了此脚本来收集PAM环境变量。
OpenPAM和Linux-PAM都为pamexec提供了额外的选项,但我们将首先介绍基本功能。具体来说,我们将完全通过shell命令实现pam_listfile的基本功能。
我们将有一个允许访问服务的用户列表。如果文件中存在用户名,则授予访问权限。如果没有,则拒绝访问。
PAM将其通常的一批项目交给我们的脚本。如果您不确定特定服务器设置了哪些项,请在第3章中使用 pamvarlog.sh 脚本捕获它们。幸运的是,大多数身份验证过程都需要PAM_USER。
在检查目标文件中的用户名时,我们需要小心一点。我们不想匹配评论。我们只想匹配完整的用户名。使用一点 grep(1)
就可以了。这是一个概念验证脚本,它根据 /etc/validuser 检查用户,如果用户名出现在那里,则允许访问。
xxxxxxxxxx
/usr/bin/grep ^$PAM_USER$ /etc/validusers
return $?
此脚本返回 grep(1)
语句返回的任何内容。如果 grep(1)
找到匹配项,则返回0。如果没有匹配,则返回1。记住,pam_exec将0视为成功,将1视为失败。
如果你想拒绝 /etc/bogususers 中列出的用户访问怎么办?这是一个巨大的、侵入性的变化:添加一个感叹号:
xxxxxxxxxx
! /usr/bin/grep ^$PAM_USER$ /etc/bogususers
return $?
此脚本反转 grep(1)
语句的返回代码。
基于这些示例,创建符合您需求的脚本。至少,您必须对输入进行消毒。清除输入的方法取决于系统的默认根shell——bash的方法与传统sh不同。
将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返回两个响应之一。如果命令返回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支持更多选项,但这些选项几乎都可以在脚本中实现。
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运行程序,而不是使用正在接受身份验证的用户。
安全增强型Linux(Security Enhanced Linux —— SELinux)为Linux系统添加了细粒度的访问控制。它通常出现在CentOS类型的系统中。SELinux经常阻止附加PAM模块正常运行。pam_ssh模块,第10章的明星,就是其中之一,所以我们将把它作为一个例子。
修复与SELinux相关的问题需要验证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 中。
xxxxxxxxxx
Jul 6 16:53:33 centos pam_ssh[3039]: exec /usr/bin/ssh-agent
Jul 6 16:52:29 centos pam_ssh[3039]: /usr/bin/ssh-agent: Permission denied
Jul 6 16:52:29 centos pam_ssh[3039]: /usr/bin/ssh-agent exited with status 127
然而,一旦你有了命令提示符,你就可以很好地运行ssh-agent。
这令人费解。这是毫无意义的。检查是否存在SELinux问题。
虽然您可以在 /var/log/audit/audit.log 中搜索被拒绝的语句,但验证问题来自SELinux的最权威的方法是告诉SELinux记录策略违规,但不要阻止它们。这被称为许可模式。这最好在测试系统上完成。使用 setenforce(8)
切换执行模式:
xxxxxxxxxx
# setenforce 0
现在尝试重新登录。禁用SELinux后,pam_ssh可以很好地启动其ssh代理。这是一个SELinux问题。将SELinux转回 enforcing 模式:
xxxxxxxxxx
# setenforce 1
问题再次出现。是的,它是SELinux。
现在创建一个SELinux策略以允许此PAM模块运行。
在尝试更改系统的SELinux策略之前,请安装SELinux管理工具。在CentOS上,setroubleshot包包含您需要的一切。我们将从audit2allow命令开始,该命令读取审计日志并创建策略。
搜索审计日志以确定SELinux是如何阻止应用程序的。在这里,我搜索关键字“denied”和我的PAM模块的名称PAM_ssh。
xxxxxxxxxx
# grep denied audit.log | grep pam_ssh
如果没有得到任何结果,请尝试删除PAM模块名称。SELinux可能正在阻塞与该模块相关的辅助进程。不过,当你得到结果时,把它们输入 audit2allow
。使用 -m
和模块名称根据错误消息打印SELinux策略模块建议:
x# grep denied audit.log | grep pam_ssh | audit2allow –m pam_ssh
module pam_ssh 1.0;
require {
type unconfined_t;
type ssh_agent_exec_t;
class file entrypoint;
}
#============= unconfined_t ==============
allow unconfined_t ssh_agent_exec_t:file entrypoint;
棘手的部分来了。您了解SELinux访问控制列表吗?如果是这样,请阅读拟议的政策,并确保其看起来合理。如果没有,要么找一个有SELinux专业知识的人,让他们看看,要么决定盲目信任audit2allow。
要在系统上创建实际的SELinux策略,请使用带有-M标志和实际模块名称的audit2allow。这只会制定政策;它不会激活它。
xxxxxxxxxx
# grep denied audit.log | grep pam_ssh | audit2allow -M pam_ssh
******************** IMPORTANT ***********************
To make this policy package active, execute:
semodule -i pam_ssh.pp
你不是那种无视指示的人,是吗?按照好软件告诉你的去做。
xxxxxxxxxx
# semodule -i pam_ssh.pp
PAM模块现在应该可以工作了,或者至少它应该继续处理下一个错误。什么是进步,不是吗?
一旦你有了一个可用的SELinux策略,你就可以将包含该策略的 .pp 文件复制到其他系统并安装它。你必须确保SELinux的策略版本匹配,并且该策略中引用的类型存在于其他系统中。最好的方法是在测试和生产环境中使用相同的操作系统版本。
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模块。