在CentOS服务器管理中,给普通用户分配sudo权限几乎是绕不开的操作。但不少管理员习惯直接用vi打开/etc/sudoers文件修改,这种做法隐藏着很大的风险:sudoers文件对语法极其敏感,一个多余的分号、一处写错的别名引用,都可能导致整个sudo机制瘫痪,连root用户自己都无法通过sudo执行命令。本文将介绍CentOS官方推荐的安全编辑方式,并结合实际场景讲解sudoers的配置方法。

为什么必须用visudo而不是直接vi编辑
visudo并不是一个编辑器,而是一个包装程序。它内部会调用系统默认的编辑器(通常是vi或vim,可以通过EDITOR环境变量修改),但在保存退出时增加了一道关键工序:语法校验。visudo会先解析整个sudoers文件,确认没有语法错误后才会真正写入/etc/sudoers。如果校验失败,它会提示你问题出在哪一行,并给出选择:重新编辑、放弃修改,或者强制保存。这相当于给危险操作装了一个安全气囊。
另一个容易被忽视的机制是文件锁。visudo编辑期间会对sudoers文件加锁,防止多个管理员同时编辑造成互相覆盖。而直接用vi编辑完全没有这层保护。此外,直接编辑还可能破坏文件的属主和权限设置——sudoers文件必须属于root:root且权限为0440,如果权限设置错误,sudo会直接拒绝工作并报错提示,vi编辑保存时可能意外改变这些属性。
visudo还内置了防止自我锁死的检查。历史上曾出现过一个经典问题:管理员在sudoers里写了一条错误的D Defaults语法,导致sudo完全不可用,自己又没有root密码,只能进单用户模式救援。使用visudo时,这类错误根本无法通过校验保存进文件。
sudoers文件的基本语法结构与授权规则
打开/etc/sudoers,核心的授权规则遵循统一格式:谁 在哪台主机 以什么身份 执行什么命令,四个字段依次对应用户、主机、身份和命令列表。例如最常见的这行:
# 允许wheel组的成员在任何主机上以任何身份执行任何命令 %wheel ALL=(ALL) ALL # 允许用户zhangsan重启网络服务,无需输入密码 zhangsan ALL=(root) NOPASSWD: /usr/bin/systemctl restart network
规则中有几个细节值得注意。用户名前面加%表示用户组;命令必须写绝对路径,可以用which命令查询;命令列表中如果包含目录(如/usr/bin/)表示允许执行该目录下所有程序,但这种写法风险较高,因为用户可以把自己控制的脚本放进可写目录。身份字段(root)限定只能以root身份运行,写成(ALL)则允许切换到任意用户。
对于复杂的授权需求,sudoers提供了四种别名:Host_Alias(主机别名)、User_Alias(用户别名)、Runas_Alias(身份别名)和Cmnd_Alias(命令别名)。别名的好处是把重复的规则收敛成一处定义,后续维护只需改别名即可。比如授权运维团队管理数据库服务:
# 定义运维团队的命令集合
Cmnd_Alias DB_CMDS = /usr/bin/systemctl restart mysqld, \
/usr/bin/systemctl status mysqld, \
/usr/local/mysql/bin/mysqladmin
# 定义运维用户组
User_Alias DB_ADMINS = zhangsan, lisi, wangwu
# 授权:运维团队成员可以免密切换到mysql用户执行数据库命令
DB_ADMINS ALL = (mysql) NOPASSWD: DB_CMDS
当某天需要新增或移除人员时,只需要修改DB_ADMINS这一行,而不必到处查找散落的规则。注意别名名称必须是大写字母、数字和下划线的组合,且不能与现有关键字冲突。多条命令之间用逗号分隔,换行续写时需要在行尾加反斜杠\。
利用sudoers.d目录拆分配置
CentOS的sudoers文件末尾有这样一行:@includedir /etc/sudoers.d,它的作用是让sudo加载该目录下所有文件中的规则。相比把所有内容堆在主文件里,拆分配置有明显优势:每个应用或团队一个独立文件,职责清晰;修改某个文件不影响主文件;方便通过自动化工具批量分发配置文件。比如给部署账号单独建一个文件:
# 创建独立配置文件,注意文件名不能包含点号 visudo -f /etc/sudoers.d/deploy # 文件内容示例 deploy ALL=(root) NOPASSWD: /usr/local/scripts/deploy.sh
使用visudo的-f参数可以编辑指定文件,保存时同样会做语法校验,之后还会把该文件合并到整体规则中验证是否有冲突。需要特别提醒的是,sudoers.d目录下的文件名中不能包含点号,带点号的文件会被sudo忽略。文件权限同样要求0440,属主为root。
排查配置问题时,可以用sudo -l命令查看当前用户实际生效的sudo权限列表,这个命令会显示规则来自哪个文件,非常适合验证拆分配置是否正确加载。
安全加固与常见配置陷阱
授权本身是把双刃剑,放权太宽等于变相交出root。以下几条实践建议值得遵守:第一,永远使用命令的绝对路径,避免PATH劫持;第二,警惕通配符,NOPASSWD: /usr/bin/systemctl restart *这种写法允许重启任意服务,包括sshd;第三,避免授权vim、less、find这类可以执行shell命令的程序,用户在vim中按:!bash就能拿到root shell,类似的逃逸漏洞可以查阅GTFOBins项目了解。
合理利用Defaults选项可以进一步加固。例如记录所有sudo操作的日志、限制sudo只能在特定终端执行、设置密码尝试次数:
# 记录详细的sudo日志到syslog Defaults logfile="/var/log/sudo.log" Defaults log_year, logfile="/var/log/sudo.log" # 限制错误密码尝试次数为3次 Defaults passwd_tries=3 # 要求sudo会话需要终端,防止脚本滥用 Defaults requiretty
日志审计方面,CentOS上sudo的默认日志走authpriv facility,可以在/var/log/secure中查到每次sudo执行的命令、执行者和时间。对于合规要求较高的环境,建议单独配置logfile选项,把命令参数也完整记录下来,便于事后追溯。
最后总结一个操作铁律:任何sudoers修改都必须通过visudo完成,改完之后不要急着退出当前会话,先开一个新终端用sudo -l验证权限符合预期,确认无误后再关闭旧会话。这个习惯能帮你避开绝大多数因配置失误把自己锁在门外的尴尬局面。