在过去,我们想要对一个使用者进行认证 (authentication),得要要求用户输入账号密码, 然后透过自行撰写的程序来判断该账号密码是否正确。也因为如此,我们常常得使用不同的机制来判断账号密 码, 所以搞的一部主机上面拥有多个各别的认证系统,也造成账号密码可能不同步的验证问题! 为了解决这个问题,便有了 PAM (Pluggable Authentication Modules, 嵌入式认证模块)
的机制!
PAM 可以说是一套应用程序编程接口 (Application Programming Interface, API),他提供了一连串的 验证机制,只要使用者将验证阶段的需求告知 PAM 后, PAM 就能够回报使用者验证的结果 (成功或失败)。由于 PAM 仅是一套验证的机制,又可以提供给其他程序所呼叫引用,因此不论你使用什么程序,都可以使用 PAM 来进行验证,如此一来,就能够让账号密码或者是其他方式的验证具有一致的结果!也让程序设计师方便处理验证的问题!
PAM 是一个独立的 API 存在,只要任何程序有需求时,可以向 PAM 发出验证要求的通知, PAM 经过一连串的验证后,将验证的结果回报给该程序,然后该程序就能够利用验证 的结果来进行可登入或显示其他无法使用的讯息。 这也就是说,你可以在写程序的时候将 PAM 模块的功能加入,就能够利用 PAM 的验证功能。 因此目前很多程序都会利用 PAM
PAM 藉由一个与程序相同文件名的配置文件来进行一连串的认证分析需求。我们以 passwd 这个指令的呼叫 PAM 来说明。 当你执行 passwd 后,这支程序呼叫 PAM 的流程是:
PAM配置文件有下面两种写法:
[root@node1 ~]# cat /etc/pam.d/sshd
#%PAM-1.0
auth required pam_sepermit.so
auth substack password-auth
auth include postlogin
# Used with polkit to reauthorize users in remote sessions
-auth optional pam_reauthorize.so prepare
account required pam_nologin.so
account include password-auth
password include password-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open env_params
session required pam_namespace.so
session optional pam_keyinit.so force revoke
session include password-auth
session include postlogin
# Used with polkit to reauthorize users in remote sessions
-session optional pam_reauthorize.so prepare
可以将pam配置文件分为四列:
第一列代表 模块类型
第二列代表 控制标记
第三列代表 模块路径
第四列代表 模块参数
下面我们来详细了解这些参数:
Linux-PAM有四种模块类型,分别代表四种不同的任务,它们是:
认证管理(auth)
账号管理(account)
会话管理(session)
密码管理(password)
一个类型可能有多行,它们按顺序依次由PAM模块调用。
管理方式 | 说明 |
---|---|
auth | 是 authentication (认证) 的缩写,所以这种类别主要用来检验使用者的身份验证,这种类别通常是需要密码来检验的, 所以后续接的模块是用来检验用户的身份 |
account | account (账号) 则大部分是在进行 authorization (授权),这种类别则主要在检验使用者是否具有正确的权限,举例来说,当你使用一个过期的密码来登入时,当然就无法正确的登入了。 |
session | session 是会议期间的意思,所以 session 管理的就是使用者在这次登入 (或使用这个指令) 期间,PAM 所 给予的环境设定。 这个类别通常用在记录用户登入与注销时的信息!例如,如果你常常使用 su 或者是 sudo 指令的话, 那么应该可以在 /var/log/secure 里面发现很多关于 pam 的说明,而且记载的数据是『session open, session close』的信息 |
password | 使用用户信息来更新.如:修改用户密码. |
这四个验证的类型通常是有顺序的,不过也有例外就是了。 会有顺序的原因是:
这样说起来, 自然是需要有点顺序吧!
PAM使用控制标记来处理和判断各个模块的返回值.(在此只说明简单的认证标记)
控制标记 | 说明 |
---|---|
required | 表示即使某个模块对用户的验证失败,也要等所有的模块都执行完毕后,PAM 才返回错误信息。这样做是为了不让用户知道被哪个模块拒绝。如果对用户验证成功,所有的模块都会返回成功信息。 |
requisite | 与required相似,但是如果这个模块返回失败,则立刻向应用程序返回失败,表示此类型失败.不再进行同类型后面的操作. |
sufficient | 表示如果一个用户通过这个模块的验证,PAM结构就立刻返回验证成功信息(即使前面有模块fail了,也会把 fail结果忽略掉),把控制权交回应用程序。后面的层叠模块即使使用requisite或者required 控制标志,也不再执行。如果验证失败,sufficient 的作用和 optional 相同 |
optional | 表示即使本行指定的模块验证失败,也允许用户接受应用程序提供的服务,一般返回PAM_IGNORE(忽略). |
include | 表示在验证过程中调用其他的PAM配置文件。在RHEL系统中有相当多的应用通过完整调用/etc/pam.d/system-auth来实现认证而不需要重新逐一去写配置项。这也就意味着在很多时候只要用户能够登录系统,针对绝大多数的应用程序也能同时通过认证。 |
substack | 与 include 类似,区别是,include 调用文件执行时有 die 或者 bad 则立即返回调用处,而 substack 则等待文件执行完。 |
还有一种比较复杂的格式为value = action的语法来设置控制标志,标志之间会以空格分开。格式如下:
value1 = action1 value2 = action2 ……
例如:
root@ubuntu:~# grep -v '^#' /etc/pam.d/login
...
auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so
..
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
session required pam_env.so readenv=1
session required pam_env.so readenv=1 envfile=/etc/default/locale
...
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
其中value可以是下列Linux PAM库的返回值:
success、open_err、symbol_err、service_err、 system_err、buf_err、perm_denied、auth_err、cred_insufficient、authinfo_unavail、user_unknown、maxtries、new_authtok_reqd、acct_expired、 session_err、cred_unavail、cred_expired、cred_err、no_module_data、conv_err、 authtok_err、authtok_recover_err、authtok_lock_busy、authtok_disable_aging、 try_again、ignore、abort、authtok_expired、module_unknown、bad_item和default。
最后一个(default)能够用来设置上面的返回值无法表达的行为。
actionN
可以是一个非负整数或者是下面的记号之一:ignore、ok、done、bad、die和reset
。如果是非负整数J,就表示需要忽略后面J个同样类型的模块。通过这种方式,系统管理者可以更加灵活地设置层叠模块,模块的层叠路径由单个模块的反应决定。
关于这几个记号的详细解释:
模块路径.即要调用模块的位置. 如果是64位系统,一般保存在/lib64/security,如: pam_unix.so
同一个模块,可以出现在不同的类型中.它在不同的类型中所执行的操作都不相同.这是由于每个模块针对不同的模块类型,编制了不同的执行函数.
常用的PAM模块介绍
[root@node1 ~]# cat /etc/securetty
console
vc/1
...
vc/11
tty1
...
tty11
ttyS0
ttysclp0
sclp_line0
3270/tty1
hvc0
...
hvsi0
hvsi1
hvsi2
xvc0
[root@node1 ~]# cat /etc/pam.d/login
#%PAM-1.0
auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so
auth substack system-auth
auth include postlogin
account required pam_nologin.so
account include system-auth
password include system-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
session optional pam_console.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open
session required pam_namespace.so
session optional pam_keyinit.so force revoke
session include system-auth
session include postlogin
-session optional pam_ck_connector.so
[root@node1 ~]# 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 pam_env.so
auth required pam_faildelay.so delay=2000000
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 1000 quiet_success
auth required pam_deny.so
account required pam_unix.so
account sufficient pam_localuser.so
account sufficient pam_succeed_if.so uid < 1000 quiet
account required pam_permit.so
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
-session optional pam_systemd.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
auth required pam_tally2.so deny=6 onerr=fail no_magic_root unlock_time=120
login 的 PAM 验证机制流程是这样的:
验证阶段 (auth):
(a)会先经过 pam_securetty.so 判断,如果使用者是 root 时,则会参考 /etc/securetty 的设定;
(b)经过 pam_env.so 设定额外的环境变量;
(c)透过 pam_unix.so 检验密码,若通过则回报 login 程序;若不通过则
(d)继续往下以 pam_succeed_if.so 判断 UID 是否大于 1000 ,若小于 1000 则回报失败,否则再往下
(e)以 pam_deny.so 拒绝联机
授权阶段 (account):
(a)先以 pam_nologin.so 判断 /etc/nologin 是否存在,若存在则不许一般使用者登入;
(b)接下来以 pam_unix.so 及 pam_localuser.so 进行账号管理
(c)接下来以 pam_succeed_if.so 判断 UID 是否小于 1000 ,若小于 1000 则不记录登录信息。
(d)最后以 pam_permit.so 允许该账号登入。
密码阶段 (password):
(a)先以 pam_pwquality.so 设定密码仅能尝试错误 3 次;
(b)接下来以 pam_unix.so 透过 sha512, shadow 等功能进行密码检验,若通过则回报 login 程序,若不通过则
(c)以 pam_deny.so 拒绝登入。
会议阶段 (session):
(a)先以 pam_selinux.so 暂时关闭 SELinux;
(b)使用 pam_limits.so 设定好用户能够操作的系统资源;
(c)登入成功后开始记录相关信息在登录文件中;
(d)以 pam_loginuid.so 规范不同的 UID 权限;
(e)开启 pam_selinux.so 的功能。
总之,就是依据验证类别 (type) 来看,然后先由 login 的设定值去查阅,如果出现『 include system-auth 』 就转到 system-auth 文件中的相同类别,去取得额外的验证流程就是了。然后再到下 一个验证类别,最终将所有的验证跑完! 就结束这次的 PAM 验证啦!
#范例一:vbird1 这个用户只能建立 100MB 的文件,且大于 90MB 会警告
[root@study ~]# vim /etc/security/limits.conf
vbird1 soft fsize 90000
vbird1 hard fsize 100000
#账号 限制依据 限制项目 限制值
# 第一字段为账号,或者是群组!若为群组则前面需要加上 @ ,例如 @projecta
# 第二字段为限制的依据,是严格(hard),还是仅为警告(soft);
# 第三字段为相关限制,此例中限制文件容量,
# 第四字段为限制的值,在此例中单位为 KB。
# 若以 vbird1 登入后,进行如下的操作则会有相关的限制出现!
[vbird1@study ~]$ ulimit -a
....(前面省略)....
file size (blocks, -f) 90000
....(后面省略)....
[vbird1@study ~]$ dd if=/dev/zero of=test bs=1M count=110
File size limit exceeded
[vbird1@study ~]$ ll --block-size=K test
-rw-rw-r--. 1 vbird1 vbird1 90000K Jul 22 01:33 test
# 果然有限制到了
#范例二:限制 pro1 这个群组,每次仅能有一个用户登入系统 (maxlogins)
[root@study ~]# vim /etc/security/limits.conf
@pro1 hard maxlogins 1
# 如果要使用群组功能的话,这个功能似乎对初始群组才有效喔!而如果你尝试多个 pro1 的登入时,
# 第二个以后就无法登入了。而且在 /var/log/secure 文件中还会出现如下的信息:
# pam_limits(login:session): Too many logins (max 1) for pro1
这个文件挺有趣的,而且是设定完成就生效了,你不用重新启动任何服务的!
但是 PAM 有个特殊的地方,由于他是在程序呼叫时才予以设定的,因此你修改完成的数据, 对于已登入系统中的用户是没有效果的,要等他再次登入时才会生效
在/etc/pam.d/sshd文件中添加一条:
[kevin@node1 ~]$ head -2 /etc/pam.d/sshd
#%PAM-1.0
auth required pam_listfile.so item=user sense=allow file=/etc/sshdusers onerr=succeed
添加两个用户kevin和grace
[root@centos6-test06 ~]# useradd kevin
[root@centos6-test06 ~]# passwd kevin
[root@centos6-test06 ~]# useradd grace
[root@centos6-test06 ~]# passwd grace
编辑file指定的文件,添加上一个用户kevin(这一步是关键)
[root@centos6-test06 ~]# echo "kevin" >/etc/sshdusers //文件/etc/sshdusers是在上面添加到/etc/pam.d/sshd中定义的
然后验证,发现使用kevin账号能正常ssh登录,使用grace账号就不能正常ssh登录了!
kevin@localhost's password:
Last failed login: Fri Apr 14 18:35:27 CST 2023 from localhost on ssh:notty
There were 2 failed login attempts since the last successful login.
[kevin@node1 ~]$ exit
logout
Connection to localhost closed.
[root@node1 ~]# ssh -p22 grace@localhost
grace@localhost's password:
Permission denied, please try again.
注:此处如果root也使用ssh远程连接,也会受到pam_listfile.so限制的。
注意:
如果发生错误,Linux-PAM 可能会改变系统的安全性。这取决于你自己的选择,你可以选择不安全(开放系统)和绝对安全(拒绝任何访问)。通常,Linux-PAM 在发生错误时,倾向于后者。任何的配置错误都可能导致系统整个或者部分无法访问。配置 Linux-PAM 时,可能遇到最大的问题可能就是 Linux-PAM 的配置文件/etc/pam.d/*被删除了。如果发生这种事情,你的系统就会被锁住。有办法可以进行恢复,最好的方法就是用一个备份的镜像来恢复系统,或者登录进单用户模式然后进行正确的配置。
su的缺点
可以在/etc/pam.d/su文件里设置禁止用户使用su命令
[root@node1 ~]# head -6 /etc/pam.d/su
#%PAM-1.0
auth sufficient pam_rootok.so
# Uncomment the following line to implicitly trust users in the "wheel" group.
#auth sufficient pam_wheel.so trust use_uid
# Uncomment the following line to require a user to be in the "wheel" group.
#auth required pam_wheel.so use_uid
默认的状态下是允许所有用户间使用su命令进行切换的!(或者两行都注释也是运行所有用户都能使用su命令)
如果开启第二行auth,表示只有root用户和wheel组内的用户才可以使用su命令。
如果注释第一行,开启第二行,表示只有wheel组内的用户才能使用su命令,root用户也被禁用su命令。
在/etc/pam.d/login中添加如下一行
auth required pam_securetty.so
在/etc/pam.d/securetty中将tty1,tty2,tty5注释
之后使用root用户再次登录,就会出现
这么做其实并不是只限制root用户,也可以将其它用户用此方法来限定,当系统安装完成后,使用此方法来增强一下安全性。