ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

sudo 访问受限目录全指南:从权限模型到 sudoers 配置实战

sudo 访问受限目录全指南:从权限模型到 sudoers 配置实战 你刚装了台 Debian 13想进/root看看日志随手敲了句sudo ls /root结果系统反手问密码输完还甩你一句“xlous 未出现在 sudoer 中”。这套组合拳打下来不少刚接触 Linux 的人当场懵掉。sudo 就是为了打开“受限目录”这扇门的钥匙可问题在于很多人拿着钥匙也开不了门。这篇文章就围绕sudo 访问受限目录这件事把权限模型、常用姿势、自动化排错、sudoers 配置和几个高频实战场景一次讲透。无论你是刚接触 Linux 的新手还是被线上服务器折腾到头疼的运维下面这些命令和排查思路都能直接抄作业。很多人把“受限目录”理解得太窄以为只有/root算。其实系统里凡是普通用户不能读、不能写、不能执行的路径都属于这个范畴。理解这一点你才知道 sudo 什么时候能救你、什么时候救不了你。1. 受限目录到底“限”住了谁先理解权限模型1.1 三类最常见的“进不去”目录我在日常运维里遇到最多的是这三类特征完全不同root 专属管理目录/root、/etc/ssl/private、/etc/sudoers.d默认只有 root 能进权限通常写成700或600。想读取里面文件普通用户连路径都列不出来。服务运行目录/var/lib/docker、/var/lib/mysql、/var/lib/postgresql属主是 root 或特定服务账号。比如 Docker 的数据目录大量文件权限是600普通用户就算有 sudo 也不是所有文件都能直接 cat因为 Docker 本身会约束访问。其他用户的 home 目录当某个账号的 home 是0700时其他普通用户会被挡在门外。调试 nginx 配置、查 node 应用日志时经常要跨用户去读文件。还有一个容易忽略的“受限资源”系统级包管理。你执行sudo apt update或sudo dnf install实际上是在写/var/lib/dpkg、/var/lib/dnf、/usr这些 root 专属位置。很多用户只把“目录”理解成文件路径其实这些操作同样走了 sudo 的权限提升逻辑。搞清楚这点你就能理解为什么 sudo 几乎天天都在用而不是只在“进目录”时才想起它。1.2 sudo 在权限链中的位置Linux 的权限检查很简单内核拿到进程的 uid/gid再去跟目标文件的 owner/group 和权限位比对。普通用户访问/root时你的 uid 不是 0文件 owner 是 root两者不匹配于是拒绝访问。sudo 做的事情本质是临时把当前进程的 euid有效用户 ID换掉默认换成 root。换完之后内核再看这个进程时它就是一个 root 身份对大多数文件都能访问。打个比方门禁系统里你的普通卡刷不进机房sudo 就是临时给你发一张 root 级别的门禁卡。但这张卡有两个特点有效期很短默认 15 分钟只对当前这条命令有效。你敲完sudo ls /root下一个普通命令还是普通身份。所以sudo 不是一个“目录切换工具”而是一个“进程身份切换工具”。你要访问的“目录”只是结果身份切换才是过程。1.3 sudo 也不是次次灵什么情况下会被拒这个必须说清楚否则你会排查到怀疑人生。网络文件系统NFS做 root squash服务器端把 root 映射成匿名用户nobody你就算用 sudo 冲过去对面看到的是一个没有权限的 nobody。管理员得去检查/etc/exports里的root_squash选项。文件被加了不可变属性chattr i /path/to/file之后即使你是 root直接改文件内容也会失败系统会报Operation not permitted。需要先chattr -i解除。挂载选项 noexec如果某个分区以noexec挂载目录里的二进制文件谁都不能执行root 也一样。SELinux / AppArmor 限制这是 MAC强制访问控制层面的限制比 DAC自主访问控制权限更靠前。你明明是 root但 SELinux context 不对照样 Permission denied。碰到这种情况sudo ls看不出问题得看/var/log/audit/audit.log或dmesg才能发现真凶。遇到“sudo 了还不行”别急着怀疑 sudo先按上面四个方向查一遍。我自己排查时至少有一半的“灵异事件”最后都落在文件属性和挂载选项上。2. 从“能进”到“进得舒服”sudo 访问受限目录的几种姿势2.1 临时查看与进入 root shell先分清sudo -s和sudo -i最直接的用法就是跟一条具体命令sudo ls -l /root sudo cat /etc/ssl/private/ssl-cert-snakeoil.key sudo tail -f /var/log/syslog这样用没问题但如果要在受限目录里连续操作比如一层层翻目录、看多个配置文件每次都敲 sudo 就很烦而且容易手误。这时可以切换到 root shellsudo -s # 以 root 身份启动 shell但保留当前用户的 HOME 等环境变量 sudo -i # 以 root 身份登录 shell模拟 root 的完整登录环境区别很关键sudo -s进去后HOME还是你自己的比如/home/youpwd也是当前目录sudo -i进去后HOME变成/root会加载 root 的.bashrc。如果你只是临时想进/root翻东西sudo -s更方便路径还是你自己熟悉的如果你要完整模拟 root 的操作环境比如加载 root 的 alias 脚本用sudo -i。还有一种常见需求是以其他用户身份进入别人的 home比如sudo -u www-data whoami sudo -u www-data cat ~/config.json-u参数指定切换到哪个用户适合读服务账号自己生成的配置。2.2 重定向陷阱为什么sudo cat a b还是 Permission denied这是新手最容易踩的坑连老手偶尔都会翻车。比如你想把受限目录里的文件复制出来sudo cat /root/secret.txt /root/copy.txt结果 Shell 报Permission denied。原因在于重定向是由当前 shell 执行的不是由 sudo 执行的。shell 在打开/root/copy.txt时用的还是你的普通用户身份对/root没有写权限自然失败。正确做法有三种# 方式一把整条命令交给 root shell 执行 sudo bash -c cat /root/secret.txt /root/copy.txt # 方式二用 tee 接收输出 cat /root/secret.txt | sudo tee /root/copy.txt # 方式三tee 接收输入适合追加场景 cat /root/secret.txt | sudo tee -a /root/copy.txt我比较推荐第二种。tee本身以 root 身份运行把标准输入原样写入目标文件符合 Unix“谁打开文件谁负责”的思路。追加时记得加-a不然会覆盖原文件。2.3 编辑受限目录里的文件请优先用 sudoedit如果你要修改/etc/nginx/nginx.conf、/root/.config/xxx这种受限文件不要直接sudo vim /path用sudoedit更安全sudoedit /etc/nginx/nginx.conf它的工作流程是先把目标文件复制成一个你当前用户可写的临时副本然后用你平时用的编辑器打开保存后 sudoedit 再把临时副本原子替换回原位置。这样做有三个好处编辑器以你的普通用户身份运行不会读取到 root 才有的配置和插件减少污染临时文件在/tmp或变体位置避免编辑器崩溃留下半截 root 文件配合 sudoers 可以精确控制“谁能编辑哪个文件”后面会讲。如果默认编辑器不是你想要的可以用sudo EDITORvim sudoedit /etc/nginx/nginx.conf2.4 连续操作不反复输密码默认 sudo 的有效期是 15 分钟在这期间再敲 sudo 不用输密码。如果你正在长时间排查问题可以先执行sudo -v这句什么也不干只是验证身份并刷新时间戳让有效期续上。反过来想立刻让缓存失效用sudo -k。有个细节要注意很多发行版默认开启了“每个终端独立计时”所以在 A 终端sudo -v不会让 B 终端免密。这算是一种安全设计别在脚本里依赖终端间共享时间戳。3. 自动化与远程场景sudo 的三大拦路虎3.1 “sudo: a terminal is required” 到底想说什么搜索热词里有一堆人遇到这个错误比如error invoking remote method apiinvoke: error: sudo: a terminal is required这句话的意思是sudo 默认需要从终端TTY读取密码而当前调用它的进程没有终端。常见于桌面 GUI 程序背后调用 system helper或者你在 CI/远程命令里直接跑了 sudo。解决思路有两种关掉 requiretty如果/etc/sudoers里有Defaults requirettysudo 会强制要求终端即使能输密码也不行。现代发行版默认不启用它但有些老系统或加固过的系统会加。可以用sudo visudo注释掉这行。给 sudo 一个读密码的方式用sudo -S从标准输入读密码或者配置SUDO_ASKPASS。下面会讲。如果错误来自某个 GUI 程序比如 Ubuntu 软件中心、系统设置里弹出来的多数情况下是 polkit 的问题和 sudo 本身无关需要检查对应的org.freedesktop.policykit策略文件。命令行用户只需要知道这个报错不代表你密码错了而是“没有地方让你输密码”。3.2 ssh 远程执行 sudo加不加 -t 完全是两回事在本地跑没问题一放到 ssh 远程就报错这是运维日常。比如ssh userserver sudo cat /root/config.yaml大概率会得到sudo: no tty present and no askpass program specified因为 ssh 默认只为交互式登录分配远程 TTY你直接传命令时不分配sudo 又想要终端读密码于是卡死。最简单的解决方法是给 ssh 加-t参数强制分配一个伪终端ssh -t userserver sudo cat /root/config.yaml如果你是 Windows 下用 plinkPuTTY 的命令行工具同样有-t选项plink -ssh userserver -t sudo tail -f /var/log/nginx/error.log注意加了-t之后远程命令的输出会带有终端控制字符。如果你需要把输出重定向到本地文件建议提前-t然后cat -v清理或者干脆不用-t改成下面这种无 TTY 方案ssh userserver echo your_password | sudo -S cat /root/config.yaml但这种明文密码方案只适合临时调试绝对不要写进生产脚本。正确的做法是配置 SSH 密钥登录然后在 sudoers 里给特定命令开 NOPASSWD下一章讲。这样既不需要 TTY也不需要密码。3.3 脚本里用 sudo -S 的细节sudo -S告诉 sudo 从标准输入读取密码而不是打开/dev/tty。典型写法echo your_password | sudo -S cat /root/config.yaml有几个坑需要提醒sudo 会把密码提示符写到标准错误比如[sudo] password for user:如果你把 stderr 重定向到日志里面会混进这行提示。可以用-p 把提示符清空echo your_password | sudo -S -p cat /root/config.yaml管道里的密码会出现在 shell history 里危险。脚本里可以放在环境变量或从文件读取SUDO_PASS$(cat /run/secrets/sudo_pass)。sudo -S仍然会去读 sudoers 的时间戳如果 15 分钟内已经认证过可能不会真正读管道里的密码但也不报错行为有时会让人困惑。所以脚本里建议先sudo -k清掉缓存保证你测的就是真实流程。4. 按需免密sudoers 该怎么改才安全4.1 先搞懂 sudoers 基础条目管理 sudo 权限的文件是/etc/sudoers但正式环境最好不要直接改这个文件而是放到/etc/sudoers.d/下单独创建。这样升级系统时不会冲突也方便备份。sudoers 的一条核心条目长这样user host(run_as) commands四个字段分别是哪个用户、在哪台机器、能以谁的身份、执行哪些命令。举例ops ALL(ALL) /bin/ls, /bin/cat表示ops可以在所有主机上、以任意身份默认 root、执行ls和cat。这看起来没问题但有个安全漏洞——他可以用cat读取/etc/shadow也能cat /root/.ssh/id_rsa。所以简单按命令名授权远远不够必须结合路径白名单。修改 sudoers 一定要用visudo不要直接vim /etc/sudoers。visudo会在保存前检查语法防止你把自己锁在门外。4.2 把“访问受限目录”做成白名单假设我希望运营同学能查看/root目录、读取/etc/ssl/private下的证书但其他 root 操作一概不允许。可以这样配置# /etc/sudoers.d/ops-viewer ops ALL(root) NOPASSWD: /bin/ls -l /root, /bin/cat /etc/ssl/private/*这里有个非常隐蔽的坑sudoers 的通配符只是文本匹配不会做路径归一化。/bin/cat /etc/ssl/private/*不会匹配cat /etc/ssl/private/../shadow。从保护角度看这反而是好事能挡住一部分路径穿越尝试但如果你依赖*匹配到所有子目录文件也得知道它匹配不了被..绕过去的写法。如果你要给用户开放某个具体文件的修改权限比起给命令白名单更推荐用sudoedit白名单ops ALL(root) NOPASSWD: sudoedit /etc/nginx/nginx.conf这样用户只能用sudoedit编辑这一个文件改完之后立刻生效没法顺手把 shell 弹出来。这是非常实用的做法我线上给开发开配置改权限都是用这条而不是随便放一个ALL。还要注意给了NOPASSWD的命令也不是随手就能执行sudo 仍会检查完整命令行匹配。用户不能通过给cat加参数绕到别的文件因为 sudoers 匹配的是完整命令字符串。当然如果通配符写得太宽比如NOPASSWD: /bin/cat /root/*用户完全可以用sudo cat /root/../../etc/shadow之类的方式穿出去。所以路径白名单里的通配符要尽量精确到文件名前缀不要图省事写一个深层目录级别的*。4.3 “用户不在 sudoers 中”的修复全流程热词里那个场景很有代表性xlousdebian13:~$ sudo apt update [sudo] xlous 的密码: xlous 未出现在 sudoer 文件中用户输了自己的密码后系统说这个用户不在 sudoers 名单里。本质原因是你还没有被加入管理员组。在 Debian/Ubuntu 上拥有 sudo 权限的用户必须在sudo组里在 RHEL/CentOS/Fedora 上是wheel组。检查方法groups修复方式按操作难度排序如果知道 root 密码直接以 root 登录或su -然后usermod -aG sudo xlous如果不知道 root 密码但系统有桌面环境和 polkit可以在普通用户终端里执行pkexec usermod -aG sudo xlous如果这是云服务器用服务商的 VNC/管理终端进入系统用单用户模式或救援模式执行同样的命令。改完之后让xlous重新登录或者执行newgrp sudo激活新组sudo 才能生效。这里必须纠正一个高频误解普通用户执行 sudo 时输入的密码是当前用户自己的登录密码不是 root 的密码。很多人下意识输 root 密码输三次失败后满屏报错还以为自己密码错了。Debian 默认的 root 账号可能根本没有设置密码自然更不可能接受你输 root 密码。4.4 改坏 sudoers 的应急保住最后一条命误改 sudoers 后最吓人的场景是保存时语法错误导致所有 sudo 命令直接失效。这时候再执行sudo visudo也会被拒因为 sudo 本身已经没法用了。正确应急顺序保持冷静不要再随便乱敲 sudo 命令让问题恶化。若还能登录 root比如 root 密码已知或直接允许 root SSH立刻用 root 执行pkexec visudo或者如果桌面 polkit 可用pkexec nano /etc/sudoers如果连 root 登录都不行用云厂商的 VNC 或物理机控制台进入单用户模式把根文件系统重新挂载为读写再修复mount -o remount,rw / visudo -c修复之前先备份cp /etc/sudoers /root/sudoers.bak.$(date %F)。我先备份再改坏了大不了还原这习惯救过我两次。5. 几个容易翻车的实战场景5.1 “Ubuntu 下 Docker 必须要 sudo 吗”不必组权限更优雅很多人第一次在 Ubuntu 上敲docker ps被系统提示权限不足然后养成了sudo docker ps的习惯。Docker 之所以要 sudo是因为 Docker CLI 需要跟/var/run/docker.sock这个 socket 通信而 socket 的权限默认是root:docker权限 660。普通用户不在 docker 组自然碰不了。更优雅的解法是把用户加入 docker 组sudo usermod -aG docker $USER重新登录后docker ps直接就能跑。这里有两个提醒。第一加入 docker 组后在重新登录之前不会生效别问为什么这是组缓存机制。第二docker 组拥有和 root 几乎等同的权限因为容器可以通过挂载/等方式提权。生产环境给开发加 docker 组要谨慎最好按项目划分命名空间隔离。如果只是临时想看 Docker 数据目录下到底存了什么别直接sudo ls /var/lib/docker。Docker 的数据目录结构复杂里面还有 overlayfs 层直接看容易绕晕不如用docker inspect container-name docker exec -it container-name ls /让 Docker 自己回答你比手工翻底层目录优雅得多。5.2 飞牛 NAS 这类 Debian 系设备的 sudo 密码到底是哪个飞牛fnOS这类基于 Debian 的 NAS 系统很多人第一次 SSH 进去后执行sudo -i被要求输密码时很迷茫是 admin 密码还是 web 管理界面密码根据我的经验这类家用 NAS 初次创建的管理员账号sudo 密码就是你登录该 Linux 系统时用的用户密码。如果你是用 web 界面创建的第一个账号然后用它 SSH 登录那么 sudo 密码就是这个账号的 SSH/登录密码不是 web 管理密码、更不是 root 密码。如果你在终端里输了几次都不对先去确认当前登录的用户名whoami然后尝试用自己的登录密码作为 sudo 密码。如果提示“不在 sudoers 中”回到 4.3 的修复流程。很多 NAS 系统在 web 设置里看见的用户名和系统真实用户名不一定完全一样需要核对/etc/passwd或 web 界面上的用户列表。5.3 查看、修改其他用户家目录时的权限边界有次排查某 Java 应用发现日志写在它自己的 home 目录下普通用户看不了但服务无法重启只能先读日志定位问题。这时有两种办法# 临时用 root 看 sudo tail -f /home/javaapp/logs/app.log # 或者切换成应用用户再看避免把 root 看日志的习惯养成 sudo -u javaapp tail -f ~/logs/app.log我更推荐第二种。用 root 读应用日志很容易让你误以为日志权限设置没问题实际上应用部署时应该把日志目录设成750组内成员可读这样调试时根本不需要 sudo。如果目标是“修改其他用户目录里的某个文件”比如帮同事修配置千万别直接sudo vim /home/other/config.yml会改变文件属主吗不会sudoedit 只改内容文件属主和权限不变。但如果你用重定向写入可能把文件属主变成 root后面同事自己反而不能写了。这又是一个“sudo 管住了权限、但留下了权限混乱”的例子。最后再分享一个小技巧说实话sudo 卡住我的次数已经数不清了90% 都是“没分配 TTY”和“重定向写错位置”这类小问题。现在我养成的习惯有三个任何要定期执行的脚通通用sudo -S -p 从受保护的文件读密码绝不把明文密码写在命令行里任何远程执行带 sudo 的操作第一反应加-t改 sudoers 之前一定先visudo -c校验语法、并备份原文件。最后一个很实用的小技巧多级系统默认 sudo 时间戳是 15 分钟长时间翻受限目录时先sudo -v把时间戳刷新一下能省掉一堆反复敲密码打断思路的麻烦。
返回列表