应限定sudoers中授权为绝对路径的无参脚本,如backupdeploy all=(root) nopasswd: /opt/scripts/backup.sh,禁止带参数、通配符或shell调用,脚本需root所有且0755权限,配置文件权限≤0600,并通过sudo -l -u验证精确匹配。

sudoers 文件中如何精确授权单个脚本
直接给用户 ALL=(ALL) NOPASSWD: ALL 是最常见也最危险的免密做法,等同于开放 root 权限。真正安全的做法是限定到具体脚本路径、明确指定可执行用户、禁止 shell 通配与参数注入。
正确写法示例(以授权用户 deploy 免密运行 /opt/scripts/backup.sh 为例):
/etc/sudoers.d/backup deploy ALL=(root) NOPASSWD: /opt/scripts/backup.sh
- 必须使用绝对路径,
/opt/scripts/backup.sh不能写成backup.sh或./backup.sh - 不要加引号,也不要带任何参数(如
/opt/scripts/backup.sh --force是不安全且不可靠的) - 脚本本身应设为 root 所有、权限
0755,且禁止普通用户修改:chown root:root /opt/scripts/backup.sh && chmod 0755 /opt/scripts/backup.sh - 若脚本需读取敏感配置,配置文件也应设为
root:root且权限 ≤0600
为什么不能在 sudoers 中写带参数的命令
看似方便的写法如 deploy ALL=(root) NOPASSWD: /opt/scripts/deploy.sh staging 实际上存在严重绕过风险:sudo 会把后续所有参数原样拼接执行,用户只要调用 sudo /opt/scripts/deploy.sh staging; rm -rf / 就可能触发命令注入(取决于脚本是否做输入校验)。
更隐蔽的问题是,sudo 对参数匹配采用前缀匹配而非全等匹配——只要命令行以你写的字符串开头即放行。这意味着以下调用均会被允许:
sudo /opt/scripts/deploy.sh staging --helpsudo /opt/scripts/deploy.sh staging$(python3 -c 'import os;os.system(\"id\")')
因此,sudoers 中只应授权「无参可执行文件」,所有环境差异(如 staging/prod)应由脚本内部通过配置文件或环境变量控制,而非靠 sudo 参数传递。
如何让脚本在免密前提下仍保持最小权限
即使 sudo 允许免密执行脚本,脚本自身也不能默认以 root 身份干所有事。关键原则是:能不用 root 的地方就别用。
- 用
sudo -u www-data切换到服务用户执行 Web 相关操作,而不是全程用 root - 敏感操作(如重启 systemd 服务)才用
sudo systemctl restart nginx,日志轮转等可改用logrotate配置并由 root 定时触发 - 避免在脚本里写
sudo cp ...或sudo chown ...——这些应该由 sudoers 显式授权单独命令,例如:deploy ALL=(root) NOPASSWD: /bin/cp /tmp/deploy/*.tar.gz /var/www/releases/, /usr/bin/chown -R www-data:www-data /var/www/releases/*
每次添加新 NOPASSWD 条目都要问自己:这个命令是否真的无法被组合利用?是否可被普通用户提前构造输入影响结果?
验证 sudo 配置是否生效且无宽泛匹配
改完 /etc/sudoers.d/xxx 后,别急着跑脚本。先用 sudo -l -U deploy 查看该用户实际被授予了哪些命令,确认输出中只出现你预期的那一条完整路径,没有意外的通配符或模糊匹配。
然后手动测试边界情况:
-
sudo -u deploy /opt/scripts/backup.sh→ 应成功(注意这里-u deploy是测试用,真实调用是sudo /opt/scripts/backup.sh) -
sudo /opt/scripts/backup.sh --help→ 应失败(报 “command not allowed”) -
sudo /bin/bash -c '/opt/scripts/backup.sh'→ 应失败(因为没授权/bin/bash)
最容易被忽略的是:sudoers 文件权限必须是 0440,且属主为 root:root;否则 sudo 会直接拒绝加载该文件,但不会明确提示——你只会发现配置“没生效”,却找不到原因。











