资讯详情

资讯详情

Linux账号安全实战:PAM认证、su与sudo权限管理全解析

先说一个真实的教训。早年在某家公司做运维某天一大早同事就懵了服务器登录不上root 密码被改好几个账号的 shell 被换成了/sbin/nologin。查了半天根本不是外部攻击而是内部有人用 root 权限干了一通顺手操作。整个过程不超过十分钟但暴露出的问题很典型账号权限没有分级、su 和 sudo 的用没人管、PAM 的默认配置形同虚设。从那以后我对 Linux 系统安全里这些基础动作——账号安全、su 命令、PAM 认证、sudo 命令——的重视程度完全不一样了。这篇文章就从实战角度把这些内容完整梳理一遍。不是教科书式的罗列而是把我这些年踩过的坑、验证过的配置、翻车之后的复盘都放进来。适合刚接触 Linux 的运维新人系统入门也适合有一定基础的老手对照自己的环境做一次安全自查。1. 账号安全系统大门的第一道锁1.1 账号生命周期从创建到回收都有讲究很多人觉得账号管理就是useradd加userdel实际上生产环境里绝大多数安全问题都出在账号的中间状态上——人走了账号没删、角色变了权限没降、临时开的账号没人记得关。先看两个关键文件。/etc/passwd存的是用户基本信息每一行对应一个账号典型的格式是testuser:x:1000:1000:Test User:/home/testuser:/bin/bash从左到右依次是用户名、密码占位符真正的密码在 shadow 里、UID、GID、描述信息、家目录、登录 shell。第七个字段往往被忽略但它决定了这个账号能不能登录系统。如果某个账号不该有交互式登录权限直接把 shell 改掉usermod -s /sbin/nologin testuser这一步比删除账号更稳妥。因为很多服务进程是以特定用户身份跑的你删了账号可能导致某些数据文件的属主变成一串数字后患无穷。我以前就干过这种事删了一个跑应用的用户结果整个目录的文件所有格全乱了恢复起来特别费劲。正确做法是如果只是暂时不用锁定账号如果确定不再使用先禁用再观察一段时间确认没有服务依赖再删除。锁定账号有几种方式# 传统方式锁定密码 passwd -l testuser # 新一点的用法锁定用户 usermod -L testuser # 也可以用 chage 把密码期限设置为 1970 年强制过期 chage -d 0 testuser对应的解锁命令是passwd -u和usermod -U。但要注意passwd -l只是锁住了密码认证如果这个账号配置了 SSH 密钥登录依然能登进来。所以真正要彻底禁用还得把 public key 从~/.ssh/authorized_keys里清掉或者干脆改 shell。再一个容易踩的坑是 UID 复用。删除账号后系统不会自动回收 UID如果你新建账号时不小心用了重复的 UID新账号就会直接拥有旧账号的文件。生产环境最好建一个 UID 分配记录表或者从一开始就把普通用户 UID 规划在一个固定区间比如 5000-6000避免和系统内置账号冲突。1.2 密码策略让弱密码进不了系统账号锁得再好密码太弱照样白搭。Linux 的密码策略主要靠两块/etc/login.defs里定义全局密码老化参数PAM 的pam_pwquality模块负责密码复杂度检查。先看/etc/login.defs里最常见的几项PASS_MAX_DAYS 90 PASS_MIN_DAYS 7 PASS_WARN_AGE 14我一般把最长使用期设成 90 天最短使用期设成 7 天。最短使用期很多人不理解它的作用是防止用户改密码时立刻改回原来的——刚改完新密码想马上切回去是不行的。警告期 14 天意思是密码还有两周过期的时候就提醒用户。光有老化还不够复杂度检查必须交给 PAM。在/etc/pam.d/system-auth或者/etc/pam.d/passwd里加上password requisite pam_pwquality.so try_first_pass local_users_only retry3 minlen12 dcredit-1 ucredit-1 lcredit-1 ocredit-1这几个参数的含义minlen12是最小长度 12 位dcredit-1表示至少包含一个数字ucredit-1至少一个大写字母lcredit-1至少一个小写字母ocredit-1至少一个特殊符号。注意 dcredit 前面的负号负值表示至少有多少个正值表示最多能有几个这个方向搞反了配置就完全不是你想要的效果。在实际操作中还有个细节容易被忽略密码策略只对后续新设置的密码生效已经存在的弱密码不会自动失效。所以我在初始化账号时会强制用户首次登录必须改密码chage -d 0 testuser这样用户第一次登录就会被要求设置新密码。配合前面说的密码过期内检查相当于给每个账号都上了道保险。顺带提醒一句root 账号的密码策略我建议单独设得更严格比如最长使用期 30 天。root 是系统的命脉密码更新频率高一点出事后的影响面就小一点。1.3 登录入口限制不该进的地方别让进账号安全不只是谁有账号还包括从哪里能登录。默认情况下Linux 允许 root 从任何终端登录 SSH这实际上把暴力破解的靶子直接暴露了出来。我在生产环境中几乎一律关闭 root 远程 SSHPermitRootLogin no在/etc/ssh/sshd_config里改完这一行然后systemctl restart sshd。日常维护用普通用户登录需要 root 权限再通过 su 或 sudo 切换。对于物理终端和串口登录/etc/securetty可以控制 root 能在哪些 tty 上登录。默认文件里列了一堆虚拟终端如果你的服务器有专人管理只保留tty1就够了其他的注释掉能减少很多风险面。2. su 命令身份切换的最后一公里2.1 su 和 su - 的区别真的不是多一个横杠很多人用 su 切换用户的时候从来没有留意过那个横杠其实区别非常大。su testuser是临时切换身份但保留当前 shell 的大部分环境变量比如 PATH、HOME 都不会变化su - testuser是完整登录相当于用 testuser 的身份重新登录了一次系统会加载它的登录脚本环境变量全部重置。换句话说如果你在有 root 权限的 shell 里敲su testuser然后在这个 shell 里执行命令很可能还是以 root 的家目录和 PATH 在跑。我见过一个真实事故某同事用su testuser切过去之后执行了一个脚本脚本里用了相对路径写日志结果文件全写到了 root 的家目录下后来排查半天才发现是环境变量惹的祸。所以我现在给自己定了个规矩切换用户一律用su - 用户名不带横杠的 su 尽量不用。切换为 root 也一样su - root和su -的效果相同都是完整加载 root 环境。2.2 用 wheel 组收敛 su 权限Linux 默认任何普通用户都能用 su 切换成 root——只要他知道 root 密码。这在一个团队协作的环境里非常危险root 密码一旦泄露所有能登录系统的人都等于拿到了 root。正确的做法是只允许 wheel 组内的用户使用 su。实现方式在/etc/pam.d/su里加两行auth sufficient pam_rootok.so auth required pam_wheel.so use_uidpam_rootok.so的作用是放行 root 自己root 执行 su 不再验证pam_wheel.so use_uid则是要求发起 su 的用户必须属于 wheel 组否则拒绝。配置完之后把需要授权的人加入 wheel 组usermod -aG wheel testuser注意用-aG这是追加组的意思。如果漏了-ausermod -G会直接把用户从其他所有辅助组里踢出来我身边就有人因为这个操作把用户弄残过。这种做法的好处很直接不需要给所有人 root 密码只有 wheel 组成员能切换其他人就算知道了 root 密码也切不进去。而且哪天有人离职只需要把账号移出 wheel 组权限立刻收回不用换所有密码。团队越大这个收益越明显。2.3 su 的日志追踪很多人以为 su 是一个静默操作其实系统的审计日志会记录一切。CentOS 系看/var/log/secureDebian 系看/var/log/auth.log。su 成功的记录大致长这样Feb 10 14:22:31 server su: pam_unix(su:session): session opened for user root by testuser(uid1000)如果看到大量连续失败的 su 尝试比如Feb 10 14:25:01 server su: pam_unix(su:auth): authentication failure; lognametestuser uid1000 euid1000 ttypts/0 rusertestuser rhost userroot那基本可以断定有人在猜密码。我通常会对 su 的失败日志做简单的统计一旦某个用户短时间失败次数超过五到十次立刻调查。这个动作在应急响应里往往是发现问题的最早信号。3. PAM 认证Linux 登录背后的交通警察3.1 PAM 的运作逻辑模块栈决定一切PAM可插拔认证模块是 Linux 下几乎所有认证行为的底层框架。SSH 登录、su 切换、sudo 提权、图形界面登录全都经过 PAM。它的核心思想是把认证拆成一个个独立模块通过配置文件按顺序组合形成一条认证流水线。配置文件在/etc/pam.d/目录下文件名对应服务名。比如/etc/pam.d/sshd控制 SSH 登录/etc/pam.d/su控制 su 切换/etc/pam.d/system-auth是大多数服务引用的公共配置。一个登录请求进入系统后PAM 会按顺序执行该服务对应的模块栈。每一行模块配置由三部分组成模块类型、控制标志、模块参数。控制标志是最需要理解的部分常见的有四种控制标志含义失败时的行为required必须要通过本模块失败则整个认证失败但会继续执行后续模块requisite必须要通过本模块失败则立即终止不再执行后续模块sufficient通过即放行本模块通过且前面没有失败直接返回成功optional可选本模块结果不影响最终结果除非没有其他模块判定这个区别在排查问题时很关键。required即使失败了也会走完整个模块栈方便后续模块记录日志requisite是一票否决一旦不满足马上拦下。两者选择哪个要看你的实际需求。顺序也极其重要。PAM 的模块是串联的前面的模块返回 sufficient 并成功后面的模块可能都不会执行。比如在/etc/pam.d/su里pam_rootok.so必须放在最前面这样 root 切换身份直接放行不用走密码验证。3.2 生产环境里最常用的三个 PAM 实践限制 SSH 登录用户名单有些环境希望只允许指定用户从 SSH 登录剩下的账号即使有密码也进不来。做法是用pam_listfileauth required pam_listfile.so onerrsucceed itemuser sensedeny file/etc/ssh/denyuserssensedeny表示名单里的人拒绝itemuser表示按用户名匹配。把要禁止的人写进/etc/ssh/denyusers每行一个。反过来你想做白名单senseallow配合onerrfail即可这样不在白名单里的全部拒绝。登录失败锁定暴力破解是 SSH 的老大难PAM 层面可以做失败次数锁定。传统老一点的发行版用pam_tally2新发行版用pam_faillock。以 CentOS 7 系的配置为例在/etc/pam.d/system-auth里追加auth required pam_faillock.so preauth silent audit deny5 unlock_time600 auth sufficient pam_unix.so nullok try_first_pass auth [defaultdie] pam_faillock.so authfail audit deny5 unlock_time600deny5是连续失败五次锁定unlock_time600是锁定 600 秒。配置完别急着收工先测试几遍确认不会把正常用户锁在外面。这里特别提醒PAM 配置错了导致所有用户登录不上的案例非常多改之前务必保留一个已打开的 root 终端万一出问题还能救回来。禁用空密码默认配置里pam_unix.so nullok允许空密码账号登录这在公网服务器上等于自杀。系统里存在空密码账号时任何人都能直接登录。安全的做法是去掉nullok或者用pam_warn提醒。我在初始化系统脚本里都会检查一遍/etc/shadow里第二字段遇到空值立刻锁定awk -F: ($2 ) {print $1} /etc/shadow3.3 我踩过的 PAM 坑第一次配 PAM 时我犯过一个非常经典的错误在/etc/pam.d/sshd里放错了模块顺序把pam_nologin.so放在了验证模块之前。这个模块的本意是检测/etc/nologin文件是否存在如果存在就禁止非 root 用户登录。结果文件确实存在但顺序错误导致 root 也被拦在外面SSH 全断最后只能去机房用物理终端处理。还有一次是搞混了required和requisite的语义。我给某服务配置了密码质量检查用了required失败后模块栈继续走日志里出现大量重复记录排查日志的同事差点被搞疯。后来改成requisite失败即终止干净利落。这些经验总结下来就一句话每次改 PAM 配置前先备份原文件改完在一个不受影响的会话里测试并且永远留一条不经过 PAM 的退路比如物理终端或已登录的 root 会话。4. sudo 命令把 root 能力拆开下发4.1 sudoers 语法权限最小化的关键工具sudo 的设计初衷就是按需授权给需要的人刚好够用的权限而不是把 root 密码交出去。所有 sudo 的规则都在/etc/sudoers文件里必须用visudo编辑因为它会做语法检查避免你写错后把所有 sudo 用户坑了。sudoers 的基本语法是用户 主机(可切换身份) 命令列表拆开看一个例子webadmin ALL(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx这行的意思是webadmin 可以在任何主机上以任何用户身份执行systemctl restart nginx和systemctl reload nginx这两条命令。其他命令一概不行。这就是最小权限的落地你需要什么权限就只给你什么权限。命令列表里的细节很多人会忽略。如果你写成/usr/bin/systemctl restart nginx*通配符会允许nginx.service、nginx-foo之类的任意服务名。所以我写规则的原则是命令路径尽量完整参数尽量写死能不用通配符就不用。有特殊需求时用sudo -l先验证规则实际匹配了什么。4.2 别小看这几个 Defaults 选项sudoers 文件开头的Defaults段是安全配置的重点一般建议至少包含这些Defaults env_reset Defaults requiretty Defaults timestamp_timeout5 Defaults logfile/var/log/sudo.logenv_reset是重置环境变量防止用户通过恶意 LD_PRELOAD 之类的变量劫持 sudo 执行环境。requiretty要求用户必须在终端上执行 sudo防止通过脚本来回传递权限。timestamp_timeout5是 sudo 密码记住的时间默认 15 分钟缩短到 5 分钟能减少别人在你离开终端时顺手用 sudo 的机会。logfile是 sudo 独立日志专用于审计。每次执行都会被记录下来包括时间、主机、用户、执行了什么命令。内容大概长这样Feb 10 15:00:01 : testuser : TTYpts/1 ; PWD/home/testuser ; USERroot ; COMMAND/bin/bash -c cat /etc/shadow有了这份日志任何预谋操作都有迹可循。合规审计的时候这份日志比什么都管用。4.3 sudo 的常见误用和反悔技巧sudo 最容易被误用的场景是给了无密码权限。NOPASSWD确实方便比如在自动化脚本里需要执行特权命令写成deploy ALL(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/git pull */deploy*但这里有个微妙的陷阱git pull本身就是可以执行任意命令的通过 hook你把git pull放入 NOPASSWD等于把整个 shell 都交出去了。所有能间接执行任意命令的工具——编辑器、shell、脚本解释器、包管理器——都危险。/usr/bin/vim可以在 vim 里执行 shell 命令/usr/bin/less也可以。授权时要多想一层给这条命令用户能不能绕过限制直接拿到 shell能的话就别给。想撤回某个用户的 sudo 权限最稳妥的方式是删除sudoers.d里对应的文件或者用gpasswd -d testuser wheel如果该用户是通过 wheel 组获得 sudo 权限的话移除组即可。有些发行版默认配置里%wheel ALL(ALL) ALL是一行这个规则的覆盖面太广生产环境建议改成更细的规则或者干脆不要用组授权改成逐用户授权每一条都可审计、可回收。验证配置有没有生效用两个命令# 查看当前用户可执行的 sudo 命令 sudo -l # 测试特定命令 sudo -l /usr/bin/systemctl restart nginx我每次改完 sudoers 都会用这两个命令检查一遍确认规则和预期一致再交给同事使用。5. 常见问题与排查实录5.1 账号被锁定了怎么办情况就是用户登录提示 account locked 或者密码正确但进不去。先别慌按这个顺序排查用 root 检查/etc/shadow里该用户的第二字段看看是否被加了!或!!前缀。如果是说明被锁了执行passwd -u 用户名解锁。检查是否存在失效时间chage -l 用户名能看到最后的锁定期限。如果 Account expires 已过用chage -E -1 用户名取消。如果配置了pam_faillock检查/var/log/faillock或直接用faillock --user 用户名查看失败次数达上限就faillock --user 用户名 --reset。我在实际排查中发现很多人遇到这种问题第一反应是改密码其实改了也没用锁定的标记还在。先把锁定源找到再处理效率高得多。5.2 PAM 配置改坏了登录不进去怎么办这是几乎所有运维都遇到过的惊魂时刻。处理顺序最重要如果当前还有一个已登录的 root 会话比如某个没关的终端千万别关。visudo -c和pam-console检查一下然后赶紧把改错的备份恢复回去。如果是通过 SSH 远程操作的立刻换到物理终端或带外管理口不要继续在 SSH 里折腾因为下一次会话失败可能就真的断线了。恢复到备份后不要急着改回去先在测试机或容器里验证一遍模块栈的逻辑确认控制标志、顺序都没问题再上生产。我个人的习惯是所有 PAM 配置文件改动前先cp /etc/pam.d/system-auth /etc/pam.d/system-auth.bak.日期改完立刻ssh localhost做一次本地登录测试。任何一个细节不对本地测试都会先暴露问题。5.3 su 和 sudo 权限反复出问题的排查思路如果用户反馈sudo 用不了或su 切不了常见原因大概就三类用户不在 wheel 组导致pam_wheel拒绝 su或者 sudoers 里没有对应规则。用id 用户名查看所属组。/etc/sudoers语法错误导致 sudo 拒绝加载配置。执行visudo -c检查语法。命令路径对不上。sudoers 里写的路径是/usr/bin/systemctl但实际系统里 systemctl 在别的位置用which systemctl确认后再写。提到根因是权限规则不匹配我建议检查的时候重点看/var/log/secureCentOS或/var/log/auth.logUbuntu每一次失败都有记录直接指明是哪一步拒绝的。带着日志去排查比自己猜要快一倍。6. 写在最后的一些心得就我个人这几年的管理经验来说账号安全、su、PAM、sudo 这四个东西你单独看每一个都不复杂难的是把它们串成一套完整的安全体系。账号安全管的是谁有钥匙PAM 管的是钥匙验不验su 和 sudo 管的是开门之后能进哪个房间、能碰什么东西。任何一个环节缺位整个系统的安全性都会大打折扣。最后再分享一个我坚持了很多年的习惯每次搭建一台新服务器都把这四件事完整过一遍——检查账号清单、禁用不必要的登录通道、确认 PAM 配置、制定 sudoers 最小权限规则——然后把这套工作写成初始化脚本。服务器越多这套脚本的价值越大。希望这篇文章能给正在做系统安全的你一些可落地的参考。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →