umask 077:一行配置背后,藏着多少台服务器被渗透的教训

一次血的教训
首先来讲一个实际发生过的场景。有这么一台生产环境下的跳板机,当时运维人员在 /home/deploy 这个目录底下放置了一份 id_rsa 私钥文件,权限设置成了 644。他本人当时觉得应该没什么问题,毕竟在他看来这台机器只有自己在使用。结果过了三个月左右,等到审计工作开展的时候才发现,原来这台机器上面还开通了好些个临时账号用来做数据分析,而其中有一个账号的密码早就已经泄露出去了。攻击者就顺着这把私钥,直接横向移动到了内网的数据库服务器。
后来做事后复盘的时候发现,真正的问题其实不在于那个泄露掉的密码本身,而是在于——他每次新建出来的文件默认权限都是 644,也就是说全世界都可以读取。而 umask 这个配置从来就没有人去调整过。
这一类问题在几乎每个团队里面都有可能会踩到,只不过是有没有被发现的区别罢了。这篇内容建议先收藏起来,后面提供的配置清单以及排查步骤,真正出事的时候能够直接拿过去使用。看完之后你能够拿走三样东西:umask 077 这个设置到底改动了什么内容、哪些场景下应该开启哪些场景下不该开启、以及一份可以直接拿来参考的 systemd 与 shell 配置模板。
一句话结论
先把结论摆出来,不绕什么弯子:
umask 077 所起到的作用,就是让你新建出来的每一个文件,默认权限会被设置成 600,而目录则会被设置成 700,也就是说"只有你自己这个用户能够去读写"
它并不是什么加密手段,也不是访问控制列表那种东西,只是给"新建"这个动作加上了一层默认的屏障。已经存在的那些文件它是管不着的,root 用户也照样能够去读取。但是对于绝大多数曾经被"默认权限过松"这个问题坑过的场景来说,把 umask 从 022 改成 077,可以说是投入产出比最高的一次配置操作。
什么场景应该去用?个人服务器、跳板机、密钥所在目录、多租户共享主机、任何用来存放敏感数据的进程环境。什么场景别去用?团队共享目录、Web 服务的静态资源目录、包管理器的安装路径。后面会展开来讲。
第1章 权限的默认屏障:umask 到底在过滤什么
要把 umask 077 讲清楚的话,得先把 umask 本身在干什么这件事说明白。
Unix 权限模型你应该是熟悉的:分为三组,每组三位,分别对应着 owner、group、other 这三类角色的读写执行权限。文件所能拥有的最大权限是 666(也就是 rw-rw-rw-),而目录所能拥有的最大权限则是 777(也就是 rwxrwxrwx)。这里需要注意一点,普通文件在默认情况下是不会被赋予执行位的,这个规定是在内核层面就确定下来的约定,并不是由 umask 来负责处理的。
umask 实际上是一个用来表示"需要屏蔽掉哪些权限"的掩码。它所采用的不是加法的逻辑,而是减法的逻辑。可以用一个比较生活化的方式来类比:系统本来是打算给你一栋四面通透的玻璃房子(对应权限 666),那么 umask 就相当于你往上面贴的窗帘,具体要在哪几面贴窗帘,这个决定权在你手里。umask 022 的意思就是"要把 group 和 other 这两类角色的写权限给屏蔽掉"这样一来,最终新建出来的文件权限就是 644。而 umask 077 的意思则是"要把 group 和 other 这两类角色的所有权限统统屏蔽掉"最终新建出来的文件权限就变成了 600。
这里存在一个很常见的误区:有不少人会以为 umask 是在"设置权限"但实际上它是在"设置不能给予什么权限"把这个减法关系理解透彻之后,后面所有的推导过程就都会变得顺畅了。

第2章 拆解 077:三个数字,三层屏蔽
把 077 拆开来看:
- 第一位 0:不屏蔽 owner 的任何权限
- 第二位 7:屏蔽 group 的读(4)、写(2)以及执行(1)
- 第三位 7:屏蔽 other 的读(4)、写(2)以及执行(1)
套用到新建文件上:666 - 077 = 600,也就是 rw-------。你自己能读能写,group 和 other 则什么都看不到。套用到新建目录上:777 - 077 = 700,也就是 rwx------。你自己能进去、能列出、能修改,其他人连 ls 都做不到。
这里有个细节常常会被问到:为什么设置 umask 077 之后,普通文件还是没有执行位?缘由在于文件的初始权限本身就没给 x,减了自然也还是没有。umask 只影响"最多能有什么",并不影响"内核默认不给什么"。要想给可执行文件加上 x,还是得借助 chmod +x,这一步绕不开。
常常被拿来和 077 对比的是 027。027 的意思是"owner 全部开放、group 只允许读、other 完全屏蔽",新建文件是 640、目录是 750。027 更适宜"团队内部共享、对外封闭"这类场景,比如同一个业务组的开发机。而 077 则是更彻底的"只留自己"。选哪一个,看的不是安全等级的高低,而是要看协作模型。
现象拆解:默认 022 是怎么把你坑到的
很多团队所使用的默认 umask 值是 022,这个值其实也是大多数 Linux 发行版在出厂时就设置好的。它的问题就在于——你压根就感知不到它的存在。
典型的翻车姿势大概有这么几种:
- 用
ssh-keygen生成密钥之后忘记执行 chmod 操作,公钥和私钥的权限都是 644。ssh 客户端会直接拒绝加载这个密钥,日志里面会出现一句 "UNPROTECTED PRIVATE KEY FILE",新手要排查半天 - 应用程序把 config.yaml 写到 /etc 目录下,里面明文记录着数据库密码,权限是 644,所有能登录的用户都可以用 cat 命令查看
- 备份脚本 dump 出来的 sql 文件,权限是 644,跑批任务的用户就能看到全库的明文数据
- 临时生成的 token 文件、session 文件,权限也是 644,同一台机器上部署的另一个服务就能顺手读取走
这些问题的共同点在于:不是代码层面的 bug,也不是配置方面的错误,而是"默认值本身就不够安全",而 umask 022 恰恰就是那个默认值。要是把它改成 077,上面提到的这些场景基本能一次性解决大半。

第3章 什么场景该用 077,什么场景别用
直接给出判断表,不多说废话:
推荐运用 077 的场景:
- 个人开发机、跳板机以及堡垒机
- 用于存放 SSH 密钥、TLS 证书以及 API Token 的目录
- 数据库、消息队列的数据存放目录
- 用户的 home 目录,尤其是多个用户共享的主机
- 任何会去处理敏感数据的后台进程(用户目录加上服务级 umask 的双重保险)
建议运用 027 的场景:
- 团队内部共享的开发服务器,同组的成员需要互相查看代码
- Web 应用的运行目录,进程属主和 nginx/php-fpm 属于同一个组
- 需要 group 层面协作但并不对外暴露的中间件目录
不要运用 077 的场景:
- 安装软件包时所处的构建环境。很多软件包在 make install 这个阶段,会把文件写入 /usr/local/share、/etc、/var 下面的公共路径,umask 077 就会导致这些文件其他用户读不到,服务也就起不来了
- Web 静态资源所在的目录。nginx 通常以 www-data 这个用户来运行,如果部署脚本借助你自己的账号跑,umask 077 生成出来的文件 nginx 读不了,直接就是 403
- CI/CD 当中的构建产物目录,下游节点会拉不到
如果你们线上也曾遇到过"手动跑没问题,换另一个用户跑就 permission denied"这种诡异的现象,可以先去查一下部署账号的 umask。很多所谓的"偶现"问题,本质上并不是偶现,只是触发条件还没有凑齐罢了。
第4章 配置实战:三个层级都要覆盖
umask 存在三个层级的生效方式,很多人只是改了 shell 当中的设置,以为这样就万事大吉了,结果 systemd 拉起来的服务还是保持老样子。这里把三层都给写出来。
层级一:用户级(交互式 shell)
# 写入 ~/.bashrc 或 ~/.zshrc
# 只影响当前用户交互式会话新建的文件
umask 077
层级二:系统级(所有登录用户)
# Debian/Ubuntu: /etc/login.defs
UMASK 077
# 同时检查 /etc/profile 和 /etc/pam.d/login
# 有的发行版会用 pam_umask 覆盖 login.defs
session optional pam_umask.so umask=077
层级三:服务级(systemd 拉起的进程)
# /etc/systemd/system/your-app.service
[Service]
# 关键:交互式 shell 的 umask 对 systemd 服务无效
# 必须在 unit 文件里显式声明
UMask=0077
ExecStart=/usr/local/bin/your-app
第三个是最容易被忽略掉的。systemd 在启动服务的时候不会去读你的 .bashrc,也不会去读 /etc/profile。你在终端里边 echo $(umask) 显示的是 077,但服务进程里边可能还是 0022。所有敏感服务的 unit 文件都应该显式写上 UMask=0077,这可以说是保命的一行配置。
改完之后一定要进行验证。别相信"我改过了"这种说法,只相信 su - deploy -c 'umask' 以及 systemctl show your-app -p UMask 这两个命令的输出结果。

一个容易翻车的细节:umask 并不会追溯到已有文件
这一点特别值得单独拎出来讲一讲。
umask 只会对"新建"的文件起作用。你要是把 umask 从 022 改成了 077,那么之前生成的那些 644 的密钥、配置以及备份,权限还是纹丝不动。想要把它们全部收紧,就必须手动去执行 chmod。
这里给出一段可以直接拿来用的清理脚本片段:
# 收紧 home 目录下的敏感文件权限
# 只处理常见敏感扩展名,避免误伤可执行文件
find ~/ -type f \( -name '*.key' -o -name '*.pem' \
-o -name 'id_rsa*' -o -name '*.env' \
-o -name 'credentials*' \) -exec chmod 600 {} \;
# .ssh 目录整体收紧
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_* ~/.ssh/authorized_keys ~/.ssh/config 2>/dev/null
chmod 644 ~/.ssh/*.pub 2>/dev/null # 公钥保持可读,否则某些工具会告警
需要留意最后一行,公钥反而要保持在 644。这也是很多"直接把 .ssh 整个 chmod 600"的同学会踩到的坑——ssh-copy-id 以及 git 推送的时候都会去读公钥,权限要是设得太严格,反而会出问题。所以说安全并不是越紧越好,而是要做到"够紧但又不误伤"。
第5章 陷阱清单:077 会咬人的六个地方
把踩过的坑归拢一下,方便大家直接拿来对照排查:
- systemd 服务不生效:改动了 shell 却没有同步改 unit 文件,服务所生成的文件依然是 022 状态下的产物
- 软件包安装失败:编译安装类的软件,尤其是老式的 configure && make install,会因为其他用户读不到共享文件而出现运行异常的情况
- Web 服务 403:部署用户和运行用户不是同一个,nginx/php-fpm 就读不到静态资源
- cron 任务产物无人可读:定时任务所生成的报表以及日志,别的账号根本看不到,就会误以为任务压根没有跑起来
- Docker 挂载卷权限错乱:宿主机在 umask 077 的情况下生成的挂载目录,容器里的非 root 用户进不去
- 多人协作时 git pull 拉不下来:共享工作区的 .git 目录变成了 700,另一个账号执行 fetch 就会直接失败
应对的思路其实也很简单:默认收紧,例外放开。全局 umask 选用 077 或者 027,需要共享的目录借助 setgid 搭配 ACL 单独放开,千万别反过来采用"全局宽松,局部收紧"那种模式,几乎必然会有疏漏。
要是你团队里有人在负责服务器初始化脚本、镜像基线,或者 CI/CD 部署账号的配置,这份清单可以直接转给他,让他对照检查一遍,比出事之后再来复盘要划算得多。
上线检查清单:抄走即可
把这篇里能带走的东西压缩整理成一张单子。下次去做基线加固、编写 Ansible playbook,或者评审安全审计报告的时候,就可以直接拿来对照着看:
一句话总结:umask 是"新建"这个动作的默认约束条件,并不是访问控制方面的银弹。它的价值就在于,能把默认值调整到"安全的那一侧",这样在你忘记 chmod 的那一天,系统还会替你兜着底。
写在最后
技术层面上,有那么一类问题特别让人头疼:没出事的时候,看起来完全属于多此一举,可一旦出了事,又会悔得肠子都青了。umask 就是这一类的典型。它并不会给你带来任何性能上的收益,也不会让代码变得更加优雅,但它很可能就是某一天挽救一次事故的那一行配置。
关于 077 和 027 之间该怎么选,其实并没有所谓的"最佳实践",只有"更契合你团队协作模型"的那一个。个人机器、堡垒机以及密钥服务器,闭着眼睛上 077 就行。要是团队共享的环境,027 用起来会更顺手一些。其他的场景,沿用默认的 022 也没什么问题,但那些存放敏感数据的目录,一定要单独收紧。
如果这篇文章帮你把 umask 这件小事想明白了,欢迎点个赞。要是你团队里有人正在做服务器基线、写部署脚本,或者盯着安全审计的整改单发愁,也可以顺手转给他,那份上线检查清单直接照抄就行。评论区也欢迎聊一聊——你在生产环境上,因为 umask 而踩过的最莫名其妙的坑是什么?答案估计会比想象中要精彩得多。

从新建文件默认权限 644 到 600,其实只差一个 umask 值的设定。本文会讲清楚 umask 077 到底改了哪些东西、在什么场景下应当使用、在什么场景下会踩到坑,并且给出用户级别、系统级别以及 systemd 服务级别的完整配置清单。适宜运维人员、SRE 工程师、后端开发者以及安全方面的同学阅读。
浙公网安备 33010602011771号