在Ubuntu服务器运维中,root权限的滥用是安全事件的头号源头。多人协作的环境里,如果所有人都知道root密码,或者都能无限制地使用sudo,一次误操作就可能让系统崩溃,更不用说恶意内部威胁。真正有效的权限管控,不是禁用这些工具,而是精确地限定谁能用、从哪里用、能做什么。下面直接进入具体的限制方案。
理解su和sudo的权限模型差异很多人把su和sudo混为一谈,但它们的权限边界完全不同。su命令需要知道目标用户的密码,当你执行su -时,系统要求输入root密码,验证通过后获得一个全新的root shell。这意味着任何知道root密码的人都能成为超级管理员,而且操作日志很难追溯到具体个人。sudo则基于调用者自己的密码验证,通过/etc/sudoers文件定义谁可以以谁的身份执行什么命令。sudo的日志会精确记录哪个用户在什么时间执行了什么命令,这是审计的关键。因此,安全加固的第一步是禁用root账户的直接登录和su到root的通道,把所有特权操作强制纳入sudo的管控范围。
锁定root账户的直接登录能力Ubuntu默认就锁定了root账户的密码,但很多运维人员为了方便会主动给root设置密码并启用登录。要检查当前状态,执行passwd -S root,输出中的“L”表示已锁定,“P”表示有密码可用。如果root有密码,立即锁定它:
sudo passwd -l root
这不会影响系统运行,因为所有需要root权限的操作都应该通过sudo完成。更进一步,编辑/etc/shadow文件确保root的密码字段以感叹号开头,这是密码锁定的底层标记。同时检查/etc/passwd中root的shell字段,虽然保留/bin/bash是安全的,但在极端情况下可以改为/usr/sbin/nologin来彻底阻止任何人切换到root shell,不过这会影响sudo -i的使用,需要根据实际环境权衡。
限制su命令的使用范围即使root密码被锁定,su命令仍然存在被滥用的可能。在Ubuntu中,su命令属于util-linux包,无法直接卸载,但可以通过PAM模块和用户组来严格限制谁可以执行它。最有效的方法是利用pam_wheel模块,要求只有wheel组的成员才能使用su。编辑/etc/pam.d/su文件,找到下面这行并取消注释:
auth required pam_wheel.so
这行配置的含义是,当用户执行su时,PAM会检查该用户是否属于wheel组。如果不属于,即使他知道目标用户的密码,su也会拒绝执行。接下来创建wheel组并把授权的管理员加入:
sudo groupadd wheel sudo usermod -aG wheel authorized_user
更严格的策略是添加group=参数指定一个自定义组名,比如只允许“suadmins”组的成员使用su。同时可以在同一文件中添加pam_wheel.so的trust选项,让wheel组成员使用su时不需要输入目标用户密码,但这会降低安全性,不建议在生产环境使用。
构建精细化的sudoers规则体系sudo的权限控制远比想象中强大,但大多数系统只有一个简单的%sudo ALL=(ALL:ALL) ALL规则,这等于给了sudo组成员完整的root权限。真正安全的做法是拆解权限,按需分配。编辑sudoers文件必须使用visudo命令,它会检查语法错误,防止把自己锁在系统外:
sudo visudo -f /etc/sudoers
不要直接修改主文件,更好的做法是在/etc/sudoers.d/目录下为每个用户或用户组创建独立的配置文件。比如为运维团队创建一个文件/etc/sudoers.d/ops-team:
# 允许ops-team组成员以root身份重启nginx服务,无需密码 %ops-team ALL=(root) NOPASSWD: /bin/systemctl restart nginx # 允许查看系统日志,但不能修改 %ops-team ALL=(root) NOPASSWD: /usr/bin/journalctl # 禁止运行shell和su %ops-team ALL=(root) !/bin/bash, !/bin/su
规则中的感叹号表示显式禁止。这个配置让运维人员只能执行指定的服务管理命令,无法获得交互式shell,也无法通过sudo su或sudo bash提权到root。权限分配遵循最小权限原则,每个角色只获得完成工作所需的最小命令集。
利用sudo的command别名简化管理当需要管理的命令越来越多时,直接在规则中罗列命令会变得难以维护。sudoers支持定义命令别名,将相关命令分组。在/etc/sudoers或/etc/sudoers.d/目录下的文件中定义:
Cmnd_Alias WEB_ADMIN_CMDS = /bin/systemctl start nginx, /bin/systemctl stop nginx, /bin/systemctl reload nginx, /usr/bin/certbot Cmnd_Alias LOG_READ_CMDS = /usr/bin/journalctl, /usr/bin/tail, /usr/bin/less Cmnd_Alias DB_BACKUP_CMDS = /usr/bin/mysqldump, /usr/bin/pg_dump, /bin/tar
然后在用户规则中引用这些别名:
webadmin ALL=(root) NOPASSWD: WEB_ADMIN_CMDS logreader ALL=(root) NOPASSWD: LOG_READ_CMDS dbbackup ALL=(root) NOPASSWD: DB_BACKUP_CMDS
这样做的好处是,当需要调整某个角色的权限时,只需修改别名定义,所有引用该别名的规则自动生效。注意命令别名中的命令必须使用绝对路径,因为sudo在执行时会忽略用户的环境变量PATH,直接查找绝对路径对应的可执行文件,防止用户通过修改PATH来执行恶意程序。
通过PAM模块限制sudo的登录环境sudo本身可以控制命令层面的权限,但要限制谁能在什么时间、从哪个终端使用sudo,需要借助PAM。编辑/etc/pam.d/sudo文件,可以添加时间、终端和用户级别的访问控制。例如限制sudo只能在工作时间使用:
account required pam_time.so
然后在/etc/security/time.conf中定义时间规则:
sudo;*;username;Al0900-1800
这表示用户username只能在周一到周五的9点到18点之间使用sudo。再比如限制只能从本地控制台使用sudo,禁止远程SSH会话中的sudo操作:
account required pam_access.so
配合/etc/security/access.conf配置:
-:username:ALL EXCEPT LOCAL
这些PAM层面的限制与sudoers规则叠加,形成多层防护。即使攻击者获取了用户密码,如果不在允许的时间段或从未授权的终端登录,sudo也无法执行。
启用sudo会话日志记录和审计权限限制得再严格,如果没有完整的审计日志,安全事件发生后也无法追溯。Ubuntu默认的sudo日志记录在/var/log/auth.log中,但信息量有限。要获得详细的命令执行记录,需要启用sudo的I/O日志功能。在/etc/sudoers或/etc/sudoers.d/目录下添加:
Defaults log_input, log_output
Defaults iolog_dir=/var/log/sudo-io/%{user}
Defaults logfile=/var/log/sudo.log
log_input和log_output会记录sudo会话中所有的输入和输出,iolog_dir指定了日志存储目录,使用%{user}变量为每个用户创建独立的日志目录。这些日志可以通过sudoreplay命令回放,就像查看操作录像一样:
sudo sudoreplay -l # 列出所有会话 sudo sudoreplay 会话ID # 回放指定会话
此外,配置sudo在用户执行特权命令时发送邮件通知管理员:
Defaults mailto="admin@example.com" Defaults mail_always
这样任何sudo操作都会触发邮件告警,实现对特权操作的实时监控。
处理sudoers语法中的陷阱和边界情况sudoers规则的编写有很多容易忽略的细节。比如命令路径中的通配符使用要极度谨慎,ALL这个关键字如果出现在命令字段,意味着可以执行任何命令,等同于完全root权限。很多人以为指定了/usr/bin/*就是安全的,但攻击者可以通过创建软链接或者利用某些命令的shell逃逸功能突破限制。例如允许执行/usr/bin/vim,用户就可以在vim中通过:shell命令获得一个完整的shell。针对这类编辑器、查看器命令,应该使用sudo的NOEXEC选项或者直接禁止,改用更安全的替代工具。另一个常见问题是NOPASSWD标签的位置,它必须紧跟在主机列表之后、命令列表之前,否则会导致语法错误。还有sudoers文件的权限必须是0440,所有者和组必须是root,否则sudo会拒绝使用该文件并发出警告。
结合系统服务单元文件限制服务操作权限对于需要频繁重启特定服务的场景,与其给用户sudo执行systemctl的权限,不如利用systemd的Polkit策略或者创建专用的服务单元文件。在/etc/systemd/system/目录下创建一个仅包含ExecReload的drop-in文件,然后通过sudoers只授权systemctl reload特定服务,而不是start/stop/restart全部权限。例如:
%webops ALL=(root) NOPASSWD: /bin/systemctl reload nginx-config.service
这样即使sudoers规则被绕过,攻击者也只能重载服务配置,无法停止服务造成业务中断。这种纵深防御的思路,是把权限分散到不同层面,每一层只暴露最小的操作面。
定期审计和自动化合规检查安全配置不是一劳永逸的。随着团队人员变动和业务调整,sudoers规则会不断累积,可能产生冲突或过度授权。建立定期审计机制,使用脚本自动检查所有sudoers文件:
#!/bin/bash # 检查所有sudoers文件中的高风险规则 grep -r "ALL" /etc/sudoers /etc/sudoers.d/ | grep -v "^#" | grep -v "ALL=(ALL:ALL) ALL" # 列出所有拥有sudo权限的用户 getent group sudo | cut -d: -f4 # 检查wheel组成员 getent group wheel | cut -d: -f4
把这个脚本加入cron任务,每周运行一次,输出发送给安全管理员。同时使用sudo -l命令可以查看当前用户或指定用户拥有的sudo权限,这是快速排查权限问题的方法。对于不再需要的用户,立即从授权组中移除,并清理对应的sudoers配置文件。
通过锁定root直接登录、用PAM wheel限制su、拆分sudoers规则到最小命令集、启用I/O日志审计、结合PAM时间终端控制,这五层措施构成了Ubuntu系统特权访问管理的完整防线。关键在于持续执行最小权限原则,每个用户只拥有完成当前任务所必需的特权,并且在不需要时立即收回。
