文件在磁盘上存在,并不意味着任何进程都应该能够读取它。Linux 的访问检查与进程身份、文件所有者、组和权限位有关。理解这一点,才能判断一个服务究竟能读什么、改什么。
本篇只在配套容器的临时目录里操作,不修改宿主 SSH、防火墙或账户设置。
一、先确认“谁在执行”
在仓库根目录执行:
./bin/site.sh security-up |
接下来直到 exit 都是在容器内:
id |
预期 UID 为 10001,而不是 root。权限判断关注的是进程的有效身份,不是你的终端窗口显示了什么名字。实验镜像故意使用普通用户,这样权限被撤销时,程序确实会遭遇拒绝。
umask 影响新建文件的默认权限,不会追溯修改已存在的文件。它对初始申请权限做位屏蔽:普通文件通常从 666 出发,目录通常从 777 出发。例如常见 022 屏蔽组与其他人的写权限,并不凭空给普通文件增加执行位。
二、读懂 rwx 与数字模式
-rw------- owner group ... private.txt |
每一组三个位分别是 r=4、w=2、x=1,相加得到一位八进制数。600 表示所有者可读写,组与其他人没有权限;700 常用于只允许所有者访问的目录。
| 权限 | 普通文件 | 目录 |
|---|---|---|
| r | 读取内容 | 列举条目名字 |
| w | 修改内容 | 与适当的 x 配合创建、删除、重命名条目 |
| x | 尝试作为程序执行,仍需合法格式/解释器等条件 | 搜索、穿越目录路径 |
删除文件主要受其父目录权限控制,不是简单看文件本身有没有写位。ACL、特殊权限、挂载选项和特权能力会进一步影响行为;本篇先固定在普通用户和基础权限位这个范围。Linux 路径解析与权限说明
三、实验 A:拒绝读取,再恢复最小权限
在容器 shell 中逐行执行:
work=$(mktemp -d /tmp/security-permissions.XXXXXX) |
最后一条应出现 Permission denied,这是实验预期,不是需要绕过的故障。
chmod 600 "$work/private.txt" |
恢复为 600 后可以重新读取。所有者即使暂时不能读文件,通常仍能修改这个文件的权限。这里不需要把权限改成 777。
**记录:**修改前后的 ls -l、当前 id、哪一步成功或失败,以及为什么同一个路径结果会变化。
四、实验 B:目录 x 位与路径穿越
mkdir "$work/box" |
没有 x 位时,列出名字与访问名字指向的文件是两回事。cat 应失败;不要用“我已经能看到名字”推断自己一定能读取内容。
chmod 700 "$work/box" |
恢复目录 x 位后,再解释为什么结果改变。这里的 700 是目录权限,和普通文件的可执行意义不同。
五、把文件权限联系到服务权限
一个 Web 服务的能力可以按三个问题分开检查:
- 身份:进程以哪个 UID/GID 运行?
- 资源:哪些目录可写?是否挂载了宿主敏感目录?
- 可达性:哪些端口被发布给了谁?
本实验 Compose 中的普通 UID、只读 root、临时 /tmp 和回环端口分别约束不同边界。容器内监听 0.0.0.0:8080 与宿主只发布 127.0.0.1:18080 可以同时成立,因为它们属于不同网络命名空间。
避免把“跑在 Docker 里”当作自动安全的证明。例如挂入 Docker socket、给予过多能力或把敏感目录可写挂载进去,会改变它拥有的权限。这里没有这些挂载。容器隔离也不是对内核漏洞的绝对保证。
六、清理与复习
仍在同一个容器 shell 中:
rm "$work/box/note.txt" "$work/private.txt" |
随后回到仓库终端,可继续下一篇,或执行 ./bin/site.sh security-down。实验文件只存在于这次容器的临时文件系统。
自测:为什么不把所有权限错误都修成 chmod 777?
这样会把读写或目录遍历能力给不需要它的身份,掩盖实际的所有权、组或路径问题。应先确定哪个进程需要什么操作,再授予对应身份需要的最小权限。本实验只需恢复所有者的权限。
自测:文件只读,就一定不能被删除吗?
不一定。删除涉及父目录中的条目;需要结合父目录写与搜索权限、sticky bit 等约束判断。不要把文件写权限与目录条目管理混为一谈。