umask(User file-creation mode mask,用户文件创建权限掩码) 是 Linux/Unix 系统中一个非常重要的安全概念。它决定了用户在创建新文件或新目录时的默认基础权限。umask就像一个“过滤器”,它告诉操作系统**“在创建文件/目录时,默认要拿掉哪些权限”**。
umask 的核心工作原理
在 Linux 中,创建文件和目录都有一个最大初始权限(也叫基准权限):
- 目录的最大初始权限是
0777(rwxrwxrwx):因为目录需要执行权限 (x) 才能cd进去。 - 文件的最大初始权限是
0666(rw-rw-rw-):为了安全,Linux 默认不允许新创建的文件带有可执行权限。
实际计算公式:
默认权限 = 最大初始权限 & (^umask)
💡 通俗理解法(做减法):
虽然底层是二进制位运算,但大多数情况下你可以直接用**“减法”**来理解(对应位减去对应位,如果不够减就当做 0):
实际权限 = 最大初始权限 - umask
常见计算示例:
1. 当 umask = 0022 时 (最常见的默认值)
- 新建目录:
0777 - 0022 = 0755(rwxr-xr-x,即所有者拥有全部权限,同组和其他人只能读和进入)。 - 新建文件:
0666 - 0022 = 0644(rw-r--r--,即所有者可读写,同组和其他人只能读)。
2. 当 umask = 0077 时 (你的 Ubuntu 系统上的配置)
- 新建目录:
0777 - 0077 = 0700(rwx------,只有所有者有全部权限,其他人没有任何权限)。 - 新建文件:
0666 - 0077 = 0600(rw-------,只有所有者能读写,其他人没有任何权限)。 (这也完美解释了为什么你刚才在 Ubuntu 22.04 上执行代码,最终权限变成了0700)。
3. 当 umask = 0002 时
- 新建目录:
0777 - 0002 = 0775 - 新建文件:
0666 - 0002 = 0664
如何查看和设置 umask
查看当前 umask
在终端直接输入:
umask输出通常是类似 0022 这样的八进制数字(第一位0代表特殊权限位,后面三位代表 User、Group、Other)。
你也可以加 -S 参数,以人类可读的字母形式查看最终会保留的权限:
umask -S
# 输出类似: u=rwx,g=rx,o=rx (对应 0022 时的目录权限)临时设置 umask(仅对当前终端/会话有效)
umask 0077永久设置 umask
如果要永久生效,需要修改配置文件。通常有以下几个位置:
- 系统级全局生效:
/etc/profile或/etc/login.defs(通常用于定义系统底层默认安全策略)。 - 单个用户生效:修改用户家目录下的
~/.bashrc或~/.profile文件,在文件末尾加上umask 0022,然后执行source ~/.bashrc即可生效。
Docker 使用 umask
- Entrypoint 脚本(最推荐,适合生产环境)
这种方法最优雅,不会破坏原有的 CMD 结构,并且能确保主进程正确接收停止信号(如 SIGTERM)。
1. 创建一个启动脚本 entrypoint.sh
#!/bin/sh
# 设置 umask 为 0000
umask 0000
# 执行 Dockerfile 中 CMD 传入的命令,并将其替换为 PID 1 的主进程
exec "$@"2. 在 Dockerfile 中配置该脚本
FROM ubuntu:latest # 替换成你的基础镜像
# 将脚本复制进容器
COPY entrypoint.sh /entrypoint.sh
# 赋予执行权限
RUN chmod +x /entrypoint.sh
# 设置入口点
ENTRYPOINT ["/entrypoint.sh"]
# 你的应用程序启动命令
CMD ["your-app-command", "arg1", "arg2"]- Dockerfile 中修改 CMD / ENTRYPOINT
# 必须使用 shell 模式(或者指定 sh -c),不能只写二进制路径
CMD sh -c "umask 0000 && exec your-app-command"docker run命令中临时修改
如果你只是想在运行某个现有镜像时临时修改 umask,可以覆盖它的启动命令。
docker run -d --name my-container my-image sh -c "umask 0000 && exec my-command"docker-compose.yml中修改
如果你使用 Docker Compose,可以直接通过覆盖 command 来实现:
version: '3.8'
services:
myapp:
image: my-image:latest
command: sh -c "umask 0000 && exec your-app-command"
volumes:
- ./data:/app/dataumask 的常见使用场景
umask 主要用于在安全性和协作性之间寻找平衡,以下是 4 个最典型的应用场景:
场景 1:标准个人电脑/通用服务器 (umask 0022)
- 目的:默认保护文件的写入权限,但允许别人查看。
- 应用:绝大多数 Linux 发行版(如 CentOS, Ubuntu 的 root 用户等)的默认设置。这保证了用户创建的普通文件不会被其他用户意外修改或删除,但系统服务(如 Nginx、Apache)可以读取用户创建的静态资源(如 html 文件)。
场景 2:高安全性服务器/多租户环境 (umask 0077)
- 目的:极端的隐私保护,防横向越权。
- 应用:存放敏感信息(如金融数据、密钥凭据、数据库存储目录)的服务器。设置为
0077意味着“除了我(和 root),谁也别想看、别想碰我创建的任何文件”。你的 Ubuntu 22.04 当前应该就是处于这种高安全配置下。
场景 3:团队共享协作开发 (umask 0002)
- 目的:方便同一个用户组内的成员共同编辑文件。
- 应用:假设公司有个开发组 group 叫
developers,有一个共享目录/data/project。如果 umask 是0022,张三建的文件李四只能看不能改。 如果在该目录下要求umask 0002(通常配合共享目录的chmod g+s强制继承组),那么张三新建的目录权限是0775,文件是0664,此时李四(同属 developers 组)就可以直接修改张三创建的文件了。
场景 4:后台服务/守护进程安全加固 (umask 0027)
- 目的:精细化控制服务的日志和数据文件。
- 应用:在编写系统服务(Daemon)或者容器(Docker)的启动脚本时,经常会看到在启动主程序前先执行
umask 0027。0027意味着:屏蔽同组的写权限 (w),屏蔽其他人的所有权限 (rwx)。- 效果:服务生成的日志文件、配置文件,所属组(可能是运维组)可以查看排错,但不能修改;而无关人员(Other)连看都看不了,防止日志中包含的敏感信息(如 Token、密码)泄漏。
代理示例
package main
import (
"fmt"
"os"
"syscall"
)
func main() {
// 设置当前进程的 umask 为 0022 (Go 1.13+ 推荐使用 0o 前缀表示八进制)
// 注意:syscall.Umask 会影响整个进程,实际项目中通常由操作系统环境去配置,不需要在代码里写死
oldUmask := syscall.Umask(0o022)
defer syscall.Umask(oldUmask)
dirName := "test_dir"
fileName := "test_file.txt"
// 测试前清理可能存在的旧文件
os.RemoveAll(dirName)
os.Remove(fileName)
defer os.RemoveAll(dirName)
defer os.Remove(fileName)
// 1. 创建目录,请求权限 0777
err := os.MkdirAll(dirName, 0o777)
if err != nil {
panic(err)
}
// 2. 创建文件,请求权限 0777
err = os.WriteFile(fileName,[]byte("hello golang"), 0o777)
if err != nil {
panic(err)
}
// 3. 读取并打印实际权限
dirInfo, err := os.Stat(dirName)
if err != nil {
panic(err)
}
fileInfo, err := os.Stat(fileName)
if err != nil {
panic(err)
}
// %04o 格式化输出 4 位八进制数
// .Mode().Perm() 仅获取权限位,.Mode().String() 会输出带文件类型标志(如 d 或 -)的完整字符串
fmt.Printf("请求目录权限 0777 + Umask 0022 -> 实际目录权限: %04o (%s)\n", dirInfo.Mode().Perm(), dirInfo.Mode().String())
fmt.Printf("请求文件权限 0777 + Umask 0022 -> 实际文件权限: %04o (%s)\n", fileInfo.Mode().Perm(), fileInfo.Mode().String())
}运行输出结果:
请求目录权限 0777 + Umask 0022 -> 实际目录权限: 0755 (drwxr-xr-x)
请求文件权限 0777 + Umask 0022 -> 实际文件权限: 0755 (-rwxr-xr-x)最近更新
最新评论