写点什么

常用的 Linux 可插拔认证模块(PAM)应用举例(一)

2012 年 4 月 30 日

上一篇文章《Linux 可插拔认证模块(PAM)的配置文件、工作原理与流程》我们介绍了常用的Linux 可插拔认证模块(PAM)的配置文件、工作原理和流程,下面我们将通过一些实际配置和例子来说明pam 的各种常用模块的作用以及使用方法。

pam_access.so 模块

pam_access.so 模块主要的功能和作用是根据主机名(包括普通主机名或者 FQDN)、IP 地址和用户实现全面的访问控制。pam_access.so 模块的具体工作行为根据配置文件 /etc/security/access.conf 来决定。该配置文件的主体包含了三个字段——权限、用户和访问发起方。格式上是一个用“:”隔开的表。
第一个字段:权限(permission),使用“+”表示授予权限,用“-”表示禁止权限。
第二个字段:用户(user),定义了用户、组以及用“@”表示的在不同主机上的同名用户和同一主机上不同名用户。
第三个字段:访问发起方(origins),定义了发起访问的主机名称、域名称、终端名称。

而且该文件提供了很多范例供修改时参考,并且都给出了具体的说明,例如:

复制代码
#禁止非 root 用户通过 tty1 访问相关服务
#-:ALL EXCEPT root:tty1
#禁止除了 wheel、shutdown 以及 sync 之外的所有用户访问相关服务
#-:ALL EXCEPT wheel shutdown sync:LOCAL
#禁止 wheel 用户通过.win.tue.nl 之外的其它它终端访问相关服务
#-:wheel:ALL EXCEPT LOCAL .win.tue.nl
# 禁止下面的用户从任何主机登录。其它用户可以从任意地方访问相关服务
#-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL
# root 用户允许通过 cron 来使用 tty1 到 tty6 终端访问相关服务
#+ : root : cron crond :0 tty1 tty2 tty3 tty4 tty5 tty6
# 用户 root 允许从下面的地址访问相关服务
#+ : root : 192.168.200.1 192.168.200.4 192.168.200.9
#+ : root : 127.0.0.1
# 用户 root 可以从 192.168.201. 网段访问相关服务
#+ : root : 192.168.201.
# 用户 root 可以从.foo.bar.org 中任何主机访问相关服务
#+ : root : .foo.bar.org
# 用户 root 不允许从任何主机访问相关服务
#- : root : ALL
# 用户 @nis_group 和 foo 可以从任何主机访问相关服务
#+ : @nis_group foo : ALL
# 用户 john 只能从 127.0.0.0/24 来对本机相关服务进行访问
#+ : john : 127.0.0.0/24
# 用户 john 可以通过 ipv4 和 ipv6 的地址对本机相关服务进行访问
#+ : john : ::ffff:127.0.0.0/127
# 用户 john 可以通过 ipv6 的地址访问本机相关服务
#+ : john : 2001:4ca0:0:101::1
# 用户 john 可以通过 ipv6 的主机 IP 地址来访问本机
#+ : john : 2001:4ca0:0:101:0:0:0:1
# 用户 john 可以通过 ipv6 的 IP 地址和掩码来访问相关服务
#+ : john : 2001:4ca0:0:101::/64
# 开放所有用户对本机所有相关服务的访问
#- : ALL : ALL

那么具体如何应用到实际环境中呢?我们来设计这样一个例子:

如果要在网络内架设一个 FTP 服务器,而且在该 FTP 服务器上需要强制地指定某个用户只能通过某个 IP 地址登录。这个时候 pam_access.so 模块就派上用场了。

假设我的 FTP 服务器是使用 vsftp 来构建的,那么具体方法是:

首先修改 FTP 服务器的 /etc/pam.d/vsftpd 文件,在调用 account 接口处插入:

复制代码
account required pam_access.so

到第一行,那么整个文件的内容如下:

复制代码
[root@localhost ~]# cat /etc/pam.d/vsftpd
#%PAM-1.0
session optional pam_keyinit.so force revoke
auth required pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed
auth required pam_shells.so
auth include system-auth
account required pam_access.so
account include system-auth
session include system-auth
session required pam_loginuid.so

上述配置表示当针对 FTP 访问执行用户类接口的时候会增加 pam_access.so 的认证。保存退出之后修改 /etc/security/access.conf 配置文件,添加下面的两行:

复制代码
- : admin1 : ALL EXCEPT 192.168.10.251
- : admin2 : ALL EXCEPT 192.168.10.252

前提是我在系统上事先已经建立了 admin1 和 admin2 两个用户。所以上面的配置表示:admin1 用户不能从 192.168.10.251 之外的任何客户端访问 FTP 服务器;而 admin2 用户不能从 192.168.10.252 之外的任何客户端访问 FTP 服务器。

最后再修改 /etc/vsftpd/vsftpd.conf 文件,禁用匿名登录:

复制代码
Anonymous_enable = NO

这样当重启 vsftpd 服务之后,用户 admin1 将只能从 192.168.10.251 访问 ftp 服务,而 admin2 将只能从 192.168.10.252 访问 ftp 服务。

所以当针对这种需求而且不想使用防火墙以及应用程序自带的认证机制的时候,通过 pam_access.so 可以实现所需的效果。

pam_listfile.so 模块

pam_listfile.so 模块的功能和 pam_access.so 模块类似,目标也是实现基于用户 / 组,主机名 /IP,终端的访问控制。不过它实现的方式和 pam_access.so 会稍微有些不同,因为它没有专门的默认配置文件。访问控制是靠 pam 配置文件中的控制选项和一个自定义的配置文件来实现的。而且除了针对上述访问源的控制之外,还能够控制到 ruser,rhost,所属用户组和登录 shell。所以有些用户认为它的功能似乎比 pam_access.so 更加灵活和强大一些。

对于 pam_listfile.so 的配置方法,我们可以参考 vsftpd 文件中对 pam 的调用方式。

熟悉 vsftpd 的人都知道,在 vsftpd 默认配置中,root 用户是不允许通过 ftp 方式直接访问 FTP 服务器的。这个功能实际上是由 /etc/vsftpd/vsftpd.conf,/etc/vsftpd/ftpusers 和 /etc/pam.d/vsftpd 共同控制的。 因为在 /etc/pam.d/vsftpd 中有这样的一行配置:

复制代码
auth required pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed

表示当用户试图登录 FTP 服务器的时候,会调用 pam_listfile.so 模块来验证用户是否可以登录,这里 item=user 表示访问控制是基于 user 即用户实现的。那么哪些用户可以登录呢?就是除了 file 选项所定义的 /etc/vsftpd/ftpusers 文件之外的用户,这是由另外一个选项 sense=deny 所决定的。

而在 /etc/vsftpd/vsftpd.conf 中明确指定了对用户的认证需要通过 /etc/pam.d/vsftpd 中的配置调用 pam 模块:

复制代码
pam_service_name=vsftpd

而恰好 root 用户又在 /etc/vsftpd/ftpusers 文件中,所以这成为了制约 root 登录 FTP 服务器的一个必要条件(但不是唯一条件)。

所以针对这种情况,我们要开放和允许 root 用户登录 FTP 的权限,至少有三种改法:

  1. 修改 /etc/pam.d/vsftpd 文件,将 sense=deny 改成 sense=allow。这样会正好将情况反转过来,FTP 服务器只允许 /etc/vsftpd/ftpusers 文件内的用户登录;
  2. 修改 /etc/pam.d/vsftpd 文件,注释掉调用 pam_listfile.so 那行。这样 FTP 服务器在认证用户的时候将不再考虑 pam_listfile.so 模块的任何限制;
  3. 将 root 从 /etc/vsftpd/ftpuser 文件中注释掉;

不过需要注意的是,root 用户比较特殊,因为它在 vsftpd 配置中的限制不仅仅来自于 pam,vsftpd 本身的配置中也对其做了限制。当我们看 /etc/vsftpd/user_list 文件的时候,还将会看到这样的配置说明:

复制代码
# vsftpd userlist
# If userlist_deny=NO, only allow users in this file
# If userlist_deny=YES (default), never allow users in this file, and
# do not even prompt for a password.
# Note that the default vsftpd pam config also checks /etc/vsftpd/ftpusers
# for users that are denied.

表示当 vsftpd.conf 中 userlist_deny=NO 的时候,系统将只允许 user_list 中的用户登录 FTP 服务器;如果 userlist_deny=YES,情况将截然相反——此时 user_list 变成了黑名单,里面的用户将一概不允许登录 FTP 服务器。所以要彻底开放 root 登录 FTP 的权限,我们还要在 /etc/vsftpd/vsftpd.conf 中增加 userlist_deny=YES 或者注释掉 user_list 中的 root。

不过不管怎么说,vsftpd 中禁用 root 用户的直接登录是在绝大多数 FTP 服务器上默认的安全措施,所以开放 root 权限时应该慎重。

另外除了通过 pam_listfile.so 实现基于用户的访问控制之外,还可以实现基于其它条件的访问控制。我们具体看看 pam_listfile.so 模块的选项就会比较清楚:

使用 pam_listfile.so 模块配置的格式分为五个部分:分别是 item、sense、file、onerr 以及 apply。 其中:

item=[tty|user|rhost|ruser|group|shell]:定义了对哪些列出的目标或者条件采用规则,显然,这里可以指定多种不同的条件。

onerr=succeed|fail:定义了当出现错误(比如无法打开配置文件)时的缺省返回值。

sense=allow|deny:定义了当在配置文件中找到符合条件的项目时的控制方式。如果没有找到符合条件的项目,则一般验证都会通过。

file=filename:用于指定配置文件的全路径名称。

apply=user|@group:定义规则适用的用户类型(用户或者组)。

而至于 file 文件的写法就简单了,每行一个用户或者组名称即可。

所以,当我们需要对其它服务进行类似的访问控制的时候,就可以照葫芦画瓢。例如现在需要在 SSH 服务器上对 ssh 客户端实现基于用户的访问控制,目的是不允许 admin 从通过 ssh 登录。

针对这种需求只需要更改 /etc/pam.d/sshd 文件,并在该文件中添加一行(添加到第一行):

复制代码
auth required pam_listfile.so item=user sense=deny file=/etc/pam.d/denyusers onerr=succeed

然后建立文件 /etc/pam.d/denyusers,并在文件中写入用户信息——> 就写 admin 即可。

表示用户以 ssh 登录必须要通过 pam_listfile.so 模块进行认证,认证的对象类型是用户,采用的动作是禁止,禁止的目标是 /etc/pam.d/denyuser 文件中所定义的用户。

这样在该条目添加到该文件之后,使用 admin 从其它主机远程 ssh 访问服务器会出现密码错误的提示。但是使用 root 或者其它用户则访问能够成功。

再次强调,要注意 pam 模块使用的顺序,刚才的规则一定要添加到 auth 的第一行之前,否则不会生效。

pam_listfile.so 的另外一个案例是应用于诸如 xinetd 超级进程上的的非独立服务的应用。例如笔者曾接触过一个用户在对系统进行安全加固的时候需要禁止 gssftp 的匿名用户登录功能。这里需要说明的是 gssftp 是 RHEL 系统上基于 kerberos 协议进行认证的一款轻量级的 FTP 服务器。默认情况下,当修改了 /etc/xinetd/gssftp 配置文件并通过重启 xinetd 启动服务就可以开启该服务。当开启 gssftp 服务之后,该 ftp 服务器也是允许匿名用户登录的。而且除了 /etc/xinetd.d/gssftp 之外没有其它的配置文件可以对该服务进行过多的定制。这个时候实际上也可以借用 pam_listfile.so 模块来对其进行一些简单的控制。

参考如下的例子,如果要禁用匿名登录,则可以修改 /etc/xinetd.d/gssftp 文件如下:

复制代码
[root@localhost ~]# cat /etc/xinetd.d/gssftp
# default: off
# description: The kerberized FTP server accepts FTP connections \
# that can be authenticated with Kerberos 5.
service ftp
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/kerberos/sbin/ftpd
# server_args = -l -a
log_on_failure += USERID
disable = no
}

然后在 /etc/pam.d/gssftp 文件中增加对 pam_listfile.so 模块的调用:

复制代码
auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed

并根据 PAM 配置文件内容建立相应配置文件 /etc/ftpusers,内容为:

复制代码
anonymous
ftp

这样修改之后的效果是,当没有改 server_args 时,那么启用 /etc/ftpusers 之后,所有用户都无法登录服务器;而如果保存了 server_args 之后,只有 pam_listfile.so 指定在 /etc/ftpusers 文件中的用户不能登录 FTP 服务器,而且在输入用户名之后会被直接拒绝掉。但 root 或者其它自建立的用户可以访问 FTP 服务器。

pam_limits.so 模块:

pam_limits.so 模块的主要功能是限制用户会话过程中对各种系统资源的使用情况。缺省情况下该模块的配置文件是 /etc/security/limits.conf。而该配置文件的基本格式实际上是由 4 个字段组成的表,其中具体限制的内容包括:

复制代码
Domain type item value
  用户名 / 组名 软 / 硬限制 具体值
   core——core 文件大小 (KB)
   data——最大数据大小 (KB)
   fsize——最大文件大小 (KB)
   memlock——最大可用内存空间 (KB)
   nofile——最大可以打开的文件数量
   rss——最大可驻留空间 (KB)
   stack——最大堆栈空间 (KB)
   cpu——最大 CPU 使用时间(MIN)
   nproc——最大运行进程数
   as——地址空间限制
   maxlogins——用户可以登录到系统最多次数
   locks——最大锁定文件数目

需要注意的是,如果没有任何限制可以使用”-”号,并且针对用户限制的优先级一般要比针对组限制的优先级更高。

使用 pam_limits.so 模块的最常见的场景是在运行 Oracle 数据库的 RHEL 服务器中,因为一般 Oracle 数据库在安装之前,按照其官方文档的说明需要先对某些用户(Oracle)使用系统资源的情况进行限制。

所以我们总是能够在 Oracle 数据库服务器的 /etc/security/limits.conf 文件中看到类似这样的配置:

复制代码
oracle soft nproc 2047
oracle hard nproc 16384
oracle soft nofile 1024
oracle hard nofile 65536

结合上面的配置文件说明,可知 Oracle 数据库需要对 Oracle 用户使用资源的情况进行一些限制,包括: oracle 用户最大能开启的进程数不超过 16384,最大能打开的文件数不超过 65536。

至于 soft 和 hard 的区别,不同于磁盘配额中的软限制和硬限制。普通用户可以调整自己的 soft limit 但最高不能超过 hard limit,而且除了 root 以外的普通用户也不能够随意更改 hard limit。该调整完成之后一般可以使用 ulimit 命令查看。

顺便提一下,针对 nofile,这个只是基于用户层面的限制和调整方法。基于系统层面的限制和调整方法是修改 /etc/sysctl.conf 文件,直接改 fs.file-max 参数,调整之后 sysctl –p 生效。

另外一个例子,pam_limits.so 模块也可以使用在对一般应用程序使用的资源限制方面。举例来说,如果需要在 SSH 服务器上对来自不同用户的 ssh 访问进行限制,就可以调用该模块来实现相关功能。例如,当需要限制用户 admin 登录到 SSH 服务器时的最大连接数(防止同一个用户开启过多的登录进程),就可以在 /etc/pam.d/sshd 文件中增加一行对 pam_limits.so 模块的调用:

复制代码
session required pam_limit.so

然后在 /etc/security/limits.conf 文件中增加一行对 admin 用户产生的连接数进行限定:

复制代码
admin hard maxlogins 2

完成之后重启服务器端的 sshd 服务。

之后我们可以看到,从客户端以 admin 身份登录 SSH 服务器时,在客户端上可以打开两个控制台登录。但当客户端开启第三个登录窗口的时候会被服务器拒绝,但其它用户不会受到限制。

pam_rootok.so 模块

一般情况下,pam_rootok.so 模块的主要作用是使 uid 为 0 的用户,即 root 用户能够直接通过认证而不用输入密码。

pam_rootok.so 模块的一个典型应用是插入到一些应用程序的认证配置文件中,当 root 用户执行这些命令的时候可以不用输入口令而直接通过认证。

比如说“su”命令,为什么当以 root 用户执行“su”切换到普通用户身份的时候是不需要输入任何口令而可以直接切换过去?

当我们查看一下 /etc/pam.d/su 文件的内容就不会奇怪了。因为该文件的第一行就是:

复制代码
auth sufficient pam_rootok.so

而如果将该行配置注释掉的情况下,就会发现即便以 root 用户切换普通用户的时候仍然要求输入口令。

另外一种方法,只需要将上述的“sufficient”改成“required”即可。因为这样,pam_rootok.so 模块的验证通过就成为了必要条件之一。

pam_rootok.so 模块的另外一个应用是在 chfn 命令中。Chfn 命令用于改变 /etc/passwd 中的用户的说明字段。当以 root 身份执行 chfn 命令修改用户信息的时候是不用输入密码的。但是以普通用户身份执行 chfn 则需要输入密码之后才能改变自己的用户说明。这实际上也是因为在 /etc/pam.d/chfn 配置文件中的第一行调用了 pam_rootok.so 的结果。

不过这里即便将该配置中的第一行注释掉,root 用户通过 chfn 修改自己信息的时候仍然不需要使用密码。所以恐怕效果不是很明显。究其原因主要是很多 PAM 模块对 root 用户是不会产生限制的。

pam_userdb.so 模块

pam_userdb.so 模块的主要作用是通过一个轻量级的 Berkeley 数据库来保存用户和口令信息。这样用户认证将通过该数据库进行,而不是传统的 /etc/passwd 和 /etc/shadow 或者其它的一些基于 LDAP 或者 NIS 等类型的网络认证。所以存在于 Berkeley 数据库中的用户也称为虚拟用户。

pam_userdb.so 模块的一个典型用途就是结合 vsftpd 配置基于虚拟用户访问的 FTP 服务器。

相对于本地用户以及匿名用户来说,虚拟用户只是相对于 FTP 服务器而言才有用的用户,这些用户被严格地限定在 pam_userdb 数据库当中。所以虚拟用户只能访问 FTP 服务器所提供的资源,因而可以大大提高系统安全性。另外相对于匿名用户而言,虚拟用户必须通过用户名和密码才能够访问 FTP 的资源。这样也提高了对 FTP 用户下载的可管理性。

基于虚拟用户实现的 vsftpd 的原理基本上是这样一个过程:

先定义一些专门针对 FTP 的虚拟用户,然后将用户信息加入到系统自带的数据库中(但不是 passwd)从而生成一个访问 FTP 的虚拟用户列表,这里使用的数据库是 db4 也就是 Berkeley DB。然后可以通过使用 pam_userdb.so 模块来调用该数据库存储用户信息以及实现 FTP 用户认证。当然同时也可以在系统中通过对配置文件的定义和划分来实现对不同虚拟用户不同类型的访问控制。

下面我将详细介绍一下基于虚拟用户的 FTP 服务器配置方法,其中也包含了对 pam_userdb.so 模块的使用。

我的实验环境很简单:

FTP 服务器的 IP 地址是:10.66.0.136,使用的操作系统是 RHEL 5.4,FTP 服务是 vsftpd。

操作步骤:

1. 备份配置文件:

复制代码
[root@dhcp-0-136 ~]# cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak

2. 建立虚拟用户数据库文件 login.txt:

复制代码
[root@dhcp-0-136 vsftpd]# pwd
/etc/vsftpd
[root@dhcp-0-136 vsftpd]# cat login.txt
ftpuser1
123
ftpuser2
123
ftpuser3
123

该文件单数行为用户名称,双数行为用户访问 FTP 服务器时的口令。

完成之后根据该文件内容创建登录用户数据库文件:

复制代码
[root@dhcp-0-136 vsftpd]# db_load -T -t hash -f /etc/vsftpd/login.txt /etc/vsftpd/vsftpd_login.db

之后需要确认系统上已经安装 Berkeley DB,即 db4 软件包。

完成之后,在 /etc/vsftpd/vsftpd.conf 目录下会产生 vsftpd_login.db 文件。

设置该文件的访问权限为仅 root 可读写:

复制代码
[root@dhcp-0-136 vsftpd]# chmod 600 vsftpd_login.db

3.使用 PAM 来实现对登录 FTP 用户的限制:

现在需要创建一个使用数据库的 pam 配置文件,在这里该文件命名为 vsftpd.pam,在 /etc/pam.d 目录下。

复制代码
[root@dhcp-0-136 ~]# touch /etc/pam.d/vsftpd.pam
[root@dhcp-0-136 ~]# vi /etc/pam.d/vsftpd.pam

在该文件中写入下面的两行:

复制代码
[root@dhcp-0-136 ~]# cat /etc/pam.d/vsftpd.pam
auth required /lib/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login
account required /lib/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login

假如说以后添加了新的用户,那么修改 login.txt,然后再次执行命令:

复制代码
db_load -T -t hash -f /etc/vsftpd/login.txt /etc/vsftpd/vsftpd_login.db

生成新的数据库即可。

4.建立一个本地用户,其主目录作为虚拟用户对 FTP 服务器的访问时的服务目录:

复制代码
[root@dhcp-0-136 ~]# useradd –d /home/ftpsite virtual

通过上面的步骤建立一个名为 virtual 的本地用户,并且该用户的主目录是 /home/virtual

5. 现在通过对 /etc/vsftpd/vsftpd.conf 主配置文件的定义来使虚拟用户可以访问 FTP 服务:

复制代码
[root@dhcp-0-136 ~]# vi /etc/vsftpd/vsftpd.conf

既然该服务器要求使用虚拟用户访问,那么需要先关闭匿名用户访问功能:

复制代码
anonymous_enable=NO
local_enable=YES

另外出于安全考虑,此处添加的所有用户都只赋予对 FTP 服务器只读权限:

复制代码
write_enable=NO
anon_upload_enable=NO
anon_mkdir_write_enable=NO
anon_other_write_enable=NO

确保用户在登录的时候使用刚才定义的 PAM 配置文件来进行身份验证,因此更改此行为:

复制代码
pam_service_name=/etc/pam.d/vsftpd.pam

6.激活虚拟用户功能,并将所有虚拟用户对 FTP 服务器的访问映射为刚才所添加的 virtual 用户对 FTP 服务器的访问。

复制代码
guest_enable=YES
guest_username=virtual

这个 guest_enable 非常重要,该选项用来激活虚拟用户! 而 guest_username 是将所有虚拟用户映射成先前所建立的真实用户"virtual"。这也决定了虚拟用户在整个文件系统中的位置,也就是"virtual"的主目录 /home/ftpsite。

如果系统中还有本地用户需要访问 FTP 服务器,可以再次对本地用户访问 FTP 服务时的根目录进行指定:

复制代码
local_root=/home/ftpsite

当然在这里将 local_root 改为其它用户也没有任何问题。 再增加一行:

复制代码
listen=YES

表示 FTP 服务器将会以 standalone 的模式启动。即不通过 xinetd 以及 /etc/init.d 下的脚本启动,而可以直接执行 vsftpd 的启动。最后启动 FTP 服务:

复制代码
[root@dhcp-0-136 ~]# service vsftpd restart

在服务启动之后可在客户端上进行测试: 我们可以使用刚才在自定义数据库中建立的用户 ftpuser1、ftpuser2、ftpuser3 分别登录 FTP 服务器进行访问测试,如果都能够在 ftp 的命令行中正常登录则证明整个实验成功! 但是如果访问失败,比如说如果出现 “failed to open directory",则有可能是因为目录 /home/ftpsite 不是全局只读属性(如果想更改,可以通过设置 anon_world_readable_only=NO 来实现,这个默认是 YES,它是用来设置匿名用户只能下载全局可读的文件)。

7.在 FTP 服务器的虚拟用户建立之后,可以考虑针对不同的虚拟用户设置不同的访问权限:

整个功能只有在使用虚拟帐号的情况下才能借助虚拟帐号的 per-user 功能来实现。
为了测试需要,现在添加另外三个新的用户,分别是 ftpuser4,ftpuser5 和 ftpuser6:

复制代码
[root@dhcp-0-136 vsftpd]# cat login.txt
ftpuser1
123
ftpuser2
123
ftpuser3
123
ftpuser4
123
ftpuser5
123
ftpuser6
123

然后将该文件所有信息再次导入数据库中:

复制代码
[root@dhcp-0-136 vsftpd]# db_load -T -t hash -f /etc/vsftpd/login.txt /etc/vsftpd/vsftpd_login.db

假如在该场景中需要设置 ftpuser4 和 ftpuser5 用户具有对主目录的读取权限,而 ftpuser6 具有对主目录的写入权限。
那么可以针对这三个用户分别建立配置文件来进行限制。

首先创建并且定义一个容纳每个用户配置文件的目录:

复制代码
[root@dhcp-0-136 vsftpd]# mkdir /etc/vsftpd_user_conf

然后在主配置文件中进行定义,定义的是不同用户配置文件所存储的路径,就在刚才建立的目录下:

复制代码
user_config_dir=/etc/vsftpd_user_conf

进入该目录创建不同用户的配置文件:

复制代码
[root@dhcp-0-136 vsftpd]# cd /etc/vsftpd_user_conf

创建的 ftpuser4 配置文件的内容如下:

复制代码
[root@dhcp-0-136 vsftpd]# ls /etc/vsftpd_user_conf
ftpuser4
[root@dhcp-0-136 vsftpd]# cat /etc/vsftpd_user_conf/ftpuser4
anon_world_readalbe_only=NO

通过上面的定义,ftpuser4 用户可以读取的同时也能够下载文件。但是如果写成 YES 则意味着无法列出文件和目录。
然后赋予 ftpuser5 用户相同的权限,由于权限与 ftpuser4 一致,所以只需要拷贝配置文件即可:

复制代码
[root@dhcp-0-136 vsftpd]#cp /etc/vsftpd_user_conf/ftpuser4 /etc/vsftpd_user_conf/ftpuser5

最后是定义 ftpuser6 的权限,该用户可以向服务器目录写入和上传,那么配置文件内容为:

复制代码
[root@dhcp-0-136 vsftpd]# vi /etc/vsftpd_user_conf/ftpuser6
anon_world_readable_only=NO 不仅仅是只读,还可以下载
write_enable=YES 可以写入
anon_upload_enable=YES 可以上传
anon_mkdir_write_enable=YES 可以建立目录

如果 ftpuser5 已经提升为管理员,现在需要针对管理员定义一个更高的权限,可以将 ftpuser5 的配置文件增加一行:

复制代码
anon_other_write_enable=YES 增加管理员用户的删除 / 重命名的权限

完成之后保存退出,并重启服务:

分别测试 ftpuser4、ftpuser5 以及 ftpuser6 的权限会发现:

ftpuser4 只有登录浏览权限;
ftpuser5 除了登录浏览之外还可以删除文件以及重命名文件
ftpuser6 可以登录浏览并上传和下载,但是对于已有的文件没有写入权限。

实验到此基本成功!

在实际操作中,这种方法能够更好解决对服务器访问用户细化地设置权限的问题。而且 pam_userdb.so 模块也不仅仅只是用于在 FTP 服务中创建和使用虚拟用户,类似的服务,例如 postfix,web 服务等,都可以使用该模块实现类似功能。

pam_securetty.so 模块

pam_securetty.so 模块用于控制用户登录系统的时候所使用的终端。为说明该模块的用法,我们可以例举一个简单的例子:
在 RHEL 操作系统上一旦开启 telnet 服务之后,默认情况下 root 用户是无法通过 telnet 登录的。如果要开启该功能的话,只需要修改 /etc/pam.d/remote 文件,将该文件中的:

复制代码
auth required pam_securetty.so

这一行注释掉即可。

当通过这种方式 telnet 成功之后,再执行 who 命令查看用户登录情况的时候不难发现,刚才 root 用户 telnet 上来使用的终端是 pts/0,即网络终端。那么为什么 root 用户无法使用 pts/0 登录系统呢?实际上这是 pam_securetty.so 模块的配置文件 /etc/securetty 所定义的。默认情况下该文件中只定义了本地终端,所以另外一种改法是将 pts/0,pts/1 这类网络终端以及所需要添加的其它类型终端加入到该文件中去。这样修改之后 root 用户一样可以登录。

不过当我们使用非 root 用户通过 telnet 登录的时候会发现登录成功的时候通过 who 显示出来用的也是 pts/0 终端,那么为什么非 root 用户不用修改 securetty 文件而 root 用户就需要?其实答案很简单,只要访问 /usr/share/doc/pam-/txt/README.pam_securetty 文件 (该目录下也有其它 PAM 模块的帮助文档) 就可以获知,实际上 pam_securetty.so 和其它的 pam 模块不同,它只针对 root 用户起作用。

所以 pam_securetty 模块和 pam_limit.so 模块在某种程度上是差不多的,即都可以针对系统资源使用做出明确的限制。

pam_cracklib.so 模块

pam_cracklib.so 是一个常用并且非常重要的 PAM 模块。该模块主要的作用是对用户密码的强健性进行检测。即检查和限制用户自定义密码的长度、复杂度和历史等。如不满足上述强度的密码将拒绝用户使用。

pam_cracklib.so 比较重要和难于理解的是它的一些参数和计数方法,其常用参数包括:

debug:将调试信息写入日志;

type=xxx:当添加 / 修改密码时,系统给出的缺省提示符是“New UNIX password:”以及“Retype UNIX

password:”,而使用该参数可以自定义输入密码的提示符,比如指定 type=your own word;

retry=N:定义登录 / 修改密码失败时,可以重试的次数;

Difok=N:定义新密码中必须有几个字符要与旧密码不同。但是如果新密码中有 1/2 以上的字符与旧密码不同时,该新密码将被接受;

minlen=N:定义用户密码的最小长度;

dcredit=N:定义用户密码中必须包含多少个数字;

ucredit=N:定义用户密码中必须包含多少个大写字母;

lcredit=N:定义用户密码中必须包含多少个小些字母;

ocredit=N:定义用户密码中必须包含多少个特殊字符(除数字、字母之外);

下面是关于 pam_cracklib.so 的一个应用实例——在 /etc/pam.d/system-auth 中使用 pam_cracklib.so 来限制用户修改自己密码时必须满足一定的强健性要求。

复制代码
[root@localhost ~]# cat /etc/pam.d/system-auth
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth required /lib/security/$ISA/pam_env.so
auth sufficient /lib/security/$ISA/pam_unix.so likeauth nullok
auth required /lib/security/$ISA/pam_deny.so
account required /lib/security/$ISA/pam_unix.so
account sufficient /lib/security/$ISA/pam_succeed_if.so uid < 100 quiet
account required /lib/security/$ISA/pam_permit.so
password required /lib/security/$ISA/pam_cracklib.so retry=3 minlen=9 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1
password sufficient /lib/security/$ISA/pam_unix.so nullok use_authtok md5 shadow
password required /lib/security/$ISA/pam_deny.so
session required /lib/security/$ISA/pam_limits.so
session required /lib/security/$ISA/pam_unix.so

从上面使用 pam_cracklib.so 的策略看,要求用户修改密码时必须要满足 9 位,并且密码中至少要包含一个大写字母、小写字母、数字和特殊符号。

但是实际上像 minlen 和所有 credit 所对应的数值可以是非 0 之外的正负整数,那么这些数值到底表示什么意思呢?很多人将其简单地理解为某一类字符的位数,其实远远没有那么简单。

下面我们对这些数值和关系做一个简短的说明:

首先要明确整个环境中密码的长度要满足下面的计算公式:

计算公式:最小密码长度(minlen)应该小于或者等于 dcredit+ucredit+lcredit+ocredit+ 其它分值 (同时满足 * credit 的条件),关于分值我们随后会讲解。 (参考 file:///usr/share/doc/pam-0.99.6.2/html/sag-pam_cracklib.html 理解)

(注)*credit=-1 表示至少有一个的意思,*credit=N(N 表示当满足条件的时候加 N 分,例如 dcredit=2 表示一个数字加 2 分,两个数字加 4 分),所以这里 minlen 其实更准确的表述应该是 mincredit。

所以在下面的例子中,当 pam_cracklib.so 的参数按如下方式指定:

复制代码
password requisite pam_cracklib.so try_first_pass retry=3 minlen=12 dcredit=2 ucredit=0 lcredit=0 ocredit=0

那么当用户执行命令修改密码的时候:

复制代码
[test@dhcp-0-185 ~]$ passwd
Changing password for user test.
Changing password for test
(current) UNIX password:
New UNIX password: 输入密码: 1\=poiuyt 不成功 1 个数字再加 2 分

此时密码有一个数字,2 分,其它的字符每个 1 分,总共 10 分,不满足 minlen 的位数需求,所以该密码不能通过。

复制代码
BAD PASSWORD: is too simple
New UNIX password: 输入密码: 12\=poiuyt 成功 2 个数字再加 2 分
Retype new UNIX password:

此时密码有两个数字,4 分,其它的字符每个 1 分,总共 12 分,满足 minlen 的位数需求,所以密码可以通过。

复制代码
New UNIX password: 输入密码: 1\=poiuytre 成功 1 个数字再加 2
Retype new UNIX password:

此时密码有 1 个数字,2 分,其它的字符每个 1 分,总共 12 分,满足 minlen 的位数需求,所以密码可以通过。

因此通过上述的配置基本可以得出这样的结论:

当某类 credit 为正数 N 的时候,表示密码中该类字符一个可以加 N 分;当某类 credit 为负数 N 的时候,表示密码中某类字符必须具备 N 个。时间关系我将不会一一演示。

所以当输入的密码所有的字符总分大于或者等于 minlen,并且满足所有 credit 的要求,该密码通过; 即:输入的密码长度(每个输入值都算数)+*credit(加分)>=minlen

所以 pam_cracklib.so 模块在系统安全管理策略和管理中的用途是非常重要和广泛的。

pam_pwhistroy.so 模块

pam_pwhistory.so 模块也是一个常用模块,一般辅助 pam_cracklib.so,pam_tally.so 以及 pam_unix.so 等模块来加强用户使用密码的安全度。不过 pam_pwhistory.so 模块起的是另一类的作用,即专门为用户建立一个密码历史档案,防止用户在一定时间内使用已经用过的密码。

例如,当需要限定用户在 90 天之内不能重复使用以前曾经使用过的 10 个密码,那么具体操作方法是去修改 /etc/pam.d/system-auth 文件,在 password 接口处增加:

复制代码
……
    password     required       pam_cracklib.so    retry=3
    password     required       pam_pwhistory.so   enforce_for_root remember=10
……

此时用户使用过的密码将会记录到 /etc/security/opasswd 文件中。但是 pam_pwhistory.so 并没有什么选项可以限定密码在多少天之内无法被重复使用,所以上述的 90 天是无法配置的。一个简单的解决方法就是当 90 天左右的时候,手动清空一次 opasswd 文件即可。

当然,如果要实现同样的功能除了 pam_pwhistory.so 模块之外还有其它的办法。比较常用的是 pam_unix.so 模块。具体方法是修改 /etc/pam.d/system-auth 文件,给 pam_unix.so 模块里加上 remember=10 这个选项,修改之后的配置文件为:

复制代码
password required pam_unix.so md5 remember=10 use_authtok

这样系统将同样记住 10 个已经使用的密码。

具体情况请参考: http://www.deer-run.com/~hal/sysadmin/pam_unix.html

不过此时 /etc/security/opasswd 文件因为记录了 N 个使用过的密码,所以安全性就十分关键了,所以要确保该文件只能被 root 用户读取和编辑:

复制代码
# touch /etc/security/opasswd
# chown root:root /etc/security/opasswd
# chmod 600 /etc/security/opasswd

关于作者

王基立, 现工作于红帽软件(北京)有限公司,具备多年的售前解决方案规划与售后技术支持经验,熟悉红帽所有平台类产品和解决方案。现常驻深圳任红帽软件华南区解决方案架构师一职,主要负责红帽解决方案在华为、中兴等大型电信企业用户环境中的设计、规划、应用以及相关售前工作。同时也为包括各高级分销商以及金融、政府、教育等各方面在内的管道和区域用户提供相关解决方案、技术咨询、技术培训、现场实施、技术支持等服务。


给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2012 年 4 月 30 日 00:007050

评论

发布
暂无评论
发现更多内容

阿里云边缘容器服务、申通 IoT 云边端架构入选 2021 云边协同发展阶段性领先成果

阿里巴巴云原生

云原生

异构内存及其在机器学习系统的应用与优化

白玉兰开源

人工智能 机器学习 解决方案 第四范式 傲腾

Spring永远的神!阿里大牛熬夜38天整理的Spring全家桶笔记太香了!

程序员小毕

Java spring 程序员 架构 面试

Python——输入输出:加减乘除四则运算的程序

在即

6月日更 六月日更

你愿意被管理么?

escray

极客时间 学习笔记 朱赟的技术管理课 六月日更

分布式认知工业互联网如何赋能工业企业数字化转型?

CECBC区块链专委会

云原生推动全云开发与实践

阿里巴巴云原生

云原生

做通才还是专才,你会怎么选?

架构精进之路

认知提升 6 月日更

【布道API】浅谈API设计风格

devpoint

Rest API 6 月日更

项目管理与项目集管理、项目组合管理的区别?

万事ONES

项目管理 项目 PMO ONES

公司:离职就是一场危机管理

石云升

创业 职场经验 6月日更

高性能 JavaScriptの七 -- 编程实践小技巧

空城机

JavaScript 前端 前端性能优化 6月日更

Mybatis 二级缓存简单示例

Java mybatis

spring-beans 注册 Beans(四)BeanDefinition

梦倚栏杆

这些书都学完,绝对是编程界的大佬

看山

Java 程序员书单 6 月日更

【21-1】21 连更第一篇

耳东

6月日更

软件研发团队如何做好项目进度管理?

万事ONES

项目管理 研发管理 需求 ONES

Java--JVM运行流程

是老郭啊

JVM JVM原理 Java 8

不管是三胎还是App!指望“拉新”太难了,还是要靠老用户!

友盟全域数据

APP开发

操作系统内核是什么?Linux内核又是什么?读完这篇文章,我终于知道了

奔着腾讯去

c++ 操作系统 内存管理 Linux内核 进程管理

Kubernetes手记(5)- 配置清单使用

雪雷

k8s 六月日更

JavaScript 中数组 sort() 方法的基本使用

编程三昧

JavaScript 前端 数组 排序 js

数字化转型背景下的测试转型

BY林子

敏捷测试 测试转型

区块链+金融:当前区块链应用场景中最具活力的领域

CECBC区块链专委会

学妹问,学网站开发还是打 ACM?

程序员鱼皮

Java 程序员 算法 前端 ACM

MySQL基础之六:连接查询

打工人!

myslq 6 月日更

浅谈Java中的TCP超时

Hoswey_洪树伟

Java、

5分钟速读之Rust权威指南(十九)

码生笔谈

rust 生命周期

【Vue2.x 源码学习】第八篇 - 数组的深层劫持

Brave

源码 vue2 6 月日更

《原则》(八)

Changing Lin

6 月日更

加快技术应用规模化 建设世界先进水平区块链产业生态

CECBC区块链专委会

低代码的认知误区与落地实践

低代码的认知误区与落地实践

常用的Linux可插拔认证模块(PAM)应用举例(一)-InfoQ