最小权限原则在CentOS服务器上不是装个sudo就完事,真正落地时你会发现绝大多数权限失控来自三个被长期忽视的角落:服务账户默认shell、文件系统挂载选项、以及systemd单元文件里的隐式提权。先说结论:如果你的服务器还允许root直接SSH登录,或者所有运维人员共用同一个root密码,那么后面谈的所有细节都建立在一个漏水的桶上。先把这两件事关掉,再继续往下看。
最小权限的第一层是账户隔离。CentOS 7/8/9默认创建的用户属于wheel组,但wheel组本身只是允许sudo,并不意味着权限最小。更合理的做法是按职责划分组,例如webadmin、dbadmin、deploy,每个组只授予特定命令的sudo权限。不要直接编辑/etc/sudoers,而是在/etc/sudoers.d/下为每个组建立独立文件,这样审计和回收都清晰。例如给deploy组只允许重启nginx和php-fpm:
%deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl restart php-fpm
注意这里写的是完整命令路径,而不是通配符。sudoers里的通配符是权限漏洞的重灾区,一个看似无害的“/usr/bin/cat *”就能被利用来读取任意文件。如果某个命令必须带参数,就把参数也写死,写不死的就考虑用包装脚本替代。
服务账户是第二个重灾区。CentOS上安装MySQL、Nginx、Redis等软件时,系统会自动创建专用账户,但这些账户的默认shell往往是/sbin/nologin或/bin/false。很多人以为这样就安全了,实际上nologin只阻止交互式登录,如果该账户被用于执行某个存在漏洞的服务,攻击者仍然可以通过服务的代码执行能力以该账户身份运行命令。真正的最小权限要求你检查每个服务账户的home目录权限、可写目录、以及是否被加入了不必要的附加组。例如nginx账户不应该属于任何用户组,其home目录应该指向一个不可写的空目录,并且通过systemd的ProtectHome=yes来彻底隔离。
文件系统权限的落地比大多数人想象的要复杂。一个常见的错误是使用chmod 777来“解决”权限问题。在CentOS生产环境里,任何包含777权限的目录都应该被视为安全事件。正确的做法是使用ACL(访问控制列表)来精确授权。比如web目录需要让nginx用户可读、deploy组可写,但其他用户完全无权限:
setfacl -R -m u:nginx:rx /var/www/site setfacl -R -m g:deploy:rwx /var/www/site setfacl -R -m o:--- /var/www/site
ACL的优先级高于传统rwx权限,而且可以针对单个用户授权,这是实现最小权限的核心工具。另一个容易被忽略的是挂载选项。CentOS默认挂载的分区通常带有exec、suid、dev等选项,对于存放纯数据的目录,例如/var/www、/tmp、/home,应该在/etc/fstab中加上noexec、nosuid、nodev。特别是/tmp目录,很多提权漏洞都依赖在/tmp下执行恶意二进制文件,noexec能直接阻断这条路径。
/dev/mapper/centos-tmp /tmp xfs defaults,noexec,nosuid,nodev 0 0
systemd单元文件是CentOS 7以后权限控制的核心,但大部分运维人员只关注ExecStart和Restart,忽略了大量安全指令。一个典型的PHP-FPM或Nginx服务单元,默认情况下服务进程可以访问整个文件系统、可以加载内核模块、可以修改系统时间。这些能力与服务本身无关,却为提权提供了跳板。最小权限要求在每个服务单元中加入以下指令:
[Service] ProtectSystem=strict ProtectHome=yes PrivateTmp=yes NoNewPrivileges=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectControlGroups=yes RestrictSUIDSGID=yes
ProtectSystem=strict会让整个文件系统对服务进程变为只读,只有明确用ReadWritePaths列出的目录才可写。PrivateTmp=yes为服务提供独立的/tmp目录,防止通过共享临时文件进行攻击。NoNewPrivileges=yes直接阻断服务进程通过setuid或capabilities提权的可能性。这些指令在CentOS 7的systemd版本中已经全部支持,但实际部署中很少有人完整配置。如果你管理的是对外提供Web服务的CentOS服务器,这些指令应该成为每个服务单元的默认模板。
SSH配置是另一个需要精细化处理的地方。除了禁用root登录和密码登录外,还应该使用AllowUsers或AllowGroups来限制哪些用户可以登录。更进一步的措施是使用Match块针对不同来源IP或用户组应用不同策略。例如只允许来自内网网段的用户使用密码登录,公网用户必须使用密钥:
PasswordAuthentication no
PubkeyAuthentication yes
Match Address 192.168.1.0/24
PasswordAuthentication yes
这种分层策略在混合网络环境中非常实用,既保证了内网运维的便利性,又不牺牲公网暴露面的安全性。另外,SSH的PermitRootLogin应该设置为no,而不是without-password,后者在某些旧版本中存在绕过风险。
日志审计是验证最小权限是否真正落地的唯一手段。CentOS自带的auditd可以记录所有涉及权限变化的系统调用,但默认规则太宽泛。针对最小权限原则,至少应该监控以下事件:所有sudo命令的执行、所有对/etc/passwd和/etc/shadow的访问、所有setuid程序的执行、所有对/var/www等关键目录的写入操作。配置示例:
-w /etc/sudoers -p wa -k sudoers_change -w /etc/sudoers.d/ -p wa -k sudoers_change -w /etc/passwd -p wa -k passwd_change -w /etc/shadow -p wa -k shadow_change -a always,exit -F arch=b64 -S execve -F euid=0 -k root_exec
这些规则产生的日志需要定期审查,或者接入集中日志系统进行实时告警。很多权限失控事件在发生前都有异常sudo调用或关键文件访问,只是没有被及时发现。
还有一个经常被忽略的层面是定时任务。CentOS的crontab和systemd timer都可以执行任务,但两者的权限模型不同。crontab中的任务默认以所属用户身份运行,而/etc/crontab和/etc/cron.d/中的任务可以指定运行用户。问题在于,如果某个系统级定时任务调用了一个可被普通用户写入的脚本,就构成了权限提升路径。最小权限要求所有系统级定时任务调用的脚本必须位于只有root可写的目录中,且脚本本身权限为700。同时,定期检查/var/spool/cron/目录下是否有异常的用户级定时任务,特别是那些由服务账户创建的任务。
CentOS的SELinux是很多人选择直接关闭的功能,理由往往是“太麻烦”。但在最小权限语境下,SELinux是最后一道防线。当所有用户态权限配置都失效时,SELinux的强制访问控制仍然可以阻止服务进程访问非授权资源。CentOS 7/8/9默认启用SELinux并采用targeted策略,这已经覆盖了大多数常见服务的域隔离。最小权限的落地不是要你编写自定义SELinux策略,而是至少保持SELinux处于Enforcing模式,并且学会使用audit2allow来分析拒绝日志,而不是遇到问题就setenforce 0。如果某个服务确实需要额外权限,应该通过semanage和setsebool来精确调整,而不是整体关闭。
最后谈一下补丁和软件源管理。最小权限不仅指运行时的权限控制,也包括软件供应链的权限控制。CentOS的yum/dnf仓库配置中,应该移除所有不必要的第三方源,只保留官方base、updates和epel。第三方源中的软件包没有经过与系统核心库的兼容性测试,可能引入不必要的依赖和权限配置。如果必须使用第三方软件,优先考虑官方提供的rpm包,而不是源码编译。源码编译的软件默认安装在/usr/local下,其文件权限和目录结构完全由编译参数决定,很容易出现可写的库目录或配置文件,破坏最小权限模型。
这些措施单独看都不复杂,但组合起来需要持续的执行和审计。最小权限不是一次性配置,而是一个需要定期复查的运维状态。建议每季度检查一次sudoers配置、服务账户权限、文件系统ACL和systemd安全指令,确保新增服务或账户没有破坏既有的权限边界。安全运维的本质不是堆砌工具,而是让每一个账户、每一个进程、每一个文件都只拥有完成其工作所必需的最小能力。
