经常需要让某用户 / 自动化脚本运行少数特权命令,但又不想 sudo 让对方做任何事,
也不想每次输密码。正确做法是写一条 最小授权 的 NOPASSWD 规则。
三条原则
- 永远用
visudo—— 改坏/etc/sudoers没法回滚,全机 sudo 失效 - 放到
/etc/sudoers.d/*而不是主文件 —— 单文件出错只影响一个 drop-in - 绝对路径 + 精确命令 —— 别用 wildcard,更别
ALL
例子:让 deploy 用户重启某个特定服务
sudo visudo -f /etc/sudoers.d/deploy
内容:
# 让 deploy 用户不需要密码就能重启 app.service,且只能这个
deploy ALL=(root) NOPASSWD: /bin/systemctl restart app.service, \
/bin/systemctl status app.service, \
/bin/systemctl reload nginx.service
注意三件事:
- 第一个
ALL是允许在 哪些 host 上有效(sudoers 是网络共享时有用) (root)是允许切换为哪个用户身份- 命令必须是 绝对路径,并且每个参数都精确写出
如果只想允许重启 app-* 系列服务:
deploy ALL=(root) NOPASSWD: /bin/systemctl restart app-*.service
注意 * 在 sudoers 里是 glob,能匹配 app-frontend.service 但也能匹配
app-evil.service —— 评估清楚再用。
验证 + 测试
写完保存。visudo 在退出时做语法检查;有错会让你重新编辑,不会
落盘错配置。
# 用 deploy 身份测试
sudo -u deploy -i
sudo -l # 列出该用户被允许的命令
sudo systemctl restart app.service # 应当不要密码就成功
sudo systemctl restart sshd.service # 应当拒绝
常见反模式(千万别)
# 错: 把整个 root 让出去
deploy ALL=(ALL) NOPASSWD: ALL
# 错: 用 sudo 跑 shell
deploy ALL=(root) NOPASSWD: /bin/bash
# 完全等价于 NOPASSWD: ALL
# 错: 允许跑文本编辑器
deploy ALL=(root) NOPASSWD: /usr/bin/vim
# 在 vim 里 :!bash 就提权了
# 错: 用相对路径
deploy ALL=(root) NOPASSWD: systemctl restart app.service
# 攻击者改 PATH 就能换 systemctl 为他自己的脚本
sudoedit(即 sudo -e)是安全的编辑器封装,它把文件 copy 到临时位置
让用户编辑,结束后再 mv 回去。要让用户编辑 /etc/foo.conf:
deploy ALL=(root) NOPASSWD: sudoedit /etc/foo.conf
调用:sudo -e /etc/foo.conf,用 $EDITOR 打开编辑。
CI / 自动化的特殊建议
CI runner 上的 NOPASSWD 列表是攻击面,越短越好。如果可能:
- 用
systemctl --user+ lingering,不需要 root - 用 systemd 套接字激活,CI 只 push 文件,service 自动 reload
- 把命令写成"无参数的"脚本:CI 只能跑
/usr/local/bin/redeploy-app,
脚本内部硬编码 systemctl,攻击面收敛到一个文件
强制日志
/etc/sudoers.d/00-logging:
Defaults log_input
Defaults log_output
Defaults iolog_dir="/var/log/sudo-io/%{user}"
Defaults !syslog
Defaults logfile="/var/log/sudo.log"
log_output 会把每次 sudo 会话的输入输出记下来,需要时可以重放:
sudo cat /var/log/sudo.log
sudo sudoreplay -d /var/log/sudo-io deploy/00/00/01
踩过的坑
- 不在
/etc/sudoers.d/而直接改/etc/sudoers:升级时 dpkg 可能问你
"保留本地修改 / 用新版",不留神就丢配置。 - drop-in 文件名 不能 有
.(包括.bak);sudo 默认忽略带点的。
我们一律 .conf 后缀也不要,比如01-deploy。 NOPASSWD的命令列表里如果有逗号但漏写空格,会被解析成单个长命令名,
匹配不上。每个命令前后都加空格保险。- 给同一用户配多条规则时,最后一条生效。所以放更宽松的规则在前面、
更严格的在后面要小心。最佳实践:每个用户一份 drop-in 文件,规则集中。
登录后参与评论。