在多用户和高安全性要求的系统中,文件与目录权限控制是实现资源隔离与系统安全的基础机制。Linux 操作系统的文件权限模型提供了灵活强大的权限控制机制,通过对用户、组和其他用户的权限设置,确保系统资源的安全性和合规性。
作为一款支持 Linux 系统的分布式文件系统,JuiceFS 需要与 Linux 权限管理模型兼容,以实现一致的访问控制和数据安全。本文将深入探讨 JuiceFS 在实际应用中的权限管理实践,帮助用户更好地理解和应用。
01 访问控制模型:DAC 和 MAC
访问控制是系统安全的重要组成部分,旨在管理对资源(如文件、网络服务等)的访问。Linux上常见的访问控制模型有 DAC(Discretionary Access Control,自主访问控制)和 MAC(Mandatory Access Control,强制访问控制)。这两种模型有不同的授权机制和应用场景。
二者的区别如下:
特性 | DAC(分散式机制,用户自主访问控制) | MAC(中心化机制,管理员强制访问控制) |
---|---|---|
控制主体 | 资源所有者 | 系统级策略 |
权限分配 | 资源所有者自主分配 | 中央管理员统一管理 |
灵活性 | 高(用户自主控制) | 低(严格策略限制) |
安全等级 | 中等(依赖用户决策) | 高(适用于敏感环境) |
修改权限 | 资源所有者可随时修改 | 只能由管理员修改 |
常用的 Unix Permission 和 POSIX ACL 实现了自主访问控制 (DAC),允许资源所有者自由管理文件和目录的访问权限,而 SELinux 和 AppArmor 实现了强制访问控制(MAC),通过系统定义的安全策略来限制访问权限,从而提供更为严格的安全控制。
在下面的内容中,我们会展开介绍这些机制的细节和在 JuiceFS 的相关使用。
02 Unix Permission
Unix Permission 是从 Unix 系统中继承的一种权限管理机制,用于控制文件和目录的访问权限。 该机制简单总结如下:
主体(Subject)对客体(Object)的行为根据规则进行控制。
- 主体:操作者是用户或者说进程,划分为三类(ugo)
- u: 文件所有者(Owner)
- g: 文件所属组(Owner Group)
- o: 其他用户(Other)
- 客体:操作对象是文件或者目录
- 规则:三种权限(rwx),对于文件和目录有不同的含义
- r:读取权限
- 文件:读取文件内容,拓展属性,symlink等
- 目录: 列出目录内列表等
- w:写入权限
- 文件:修改文件内容,拓展属性等
- 目录:创建,删除,移动文件或目录
- x:执行权限
- 文件:执行文件
- 目录:切换目录,查看内部文件属性等
- r:读取权限
特殊权限位
简单的 ugo+rwx 权限模型,是大家比较熟悉的。在这基础上,还有一些拓展的特殊权限位:
- SUID: 当一个文件被赋予了 SUID 权限,其他用户在执行这个文件时,其权限会暂时提升到该文件所有者的权限。
- SGID: SGID 权限与 SUID 类似,但它影响的是文件的组权限而不是所有者权限。当一个文件被赋予了 SGID 权限,其他用户在执行这个文件时,其权限会暂时提升到该文件所属组的权限。
- SBIT: Sticky Bit 是一种特殊的目录权限。当一个目录被赋予了 Sticky Bit 权限,用户只能删除自己拥有的文件,即使他们对这个目录有写和执行权限。这个权限通常用在临时文件目录(如/tmp),以防止用户删除其他用户的文件。
JuiceFS 的权限校验
JuiceFS 是基于 FUSE 的用户态文件系统,默认情况下,JuiceFS 会开启 default_permissions 配置,即启用内核的权限检查。文件操作请求到达 JuiceFS 用户态前,内核会根据文件属性中的 mode 先进行 Unix Permission 的权限校验,通过后再交给 JuiceFS 处理。
此外,JuiceFS 本身在用户态也实现了兼容的权限校验逻辑,用于处理一些特殊的权限场景。
-
SDK 访问: 一个是 JuiceFS 提供了多种 SDK 访问方式,这些 SDK 请求不经过内核,所以 JuiceFS 需要自己实现权限校验;
-
Squash 功能:当启用 squash 功能时,JuiceFS 会将某些用户映射为指定的用户,在这种情况下,无法依赖内核权限校验,因此 JuiceFS 会启用用户态的权限校验。
下面简单介绍一下 squash 两种模式:
- root-squash (Root 降权映射)
root-squash 模式用于将本地 root 用户映射为其他用户,从而增强系统安全性。在该模式下,即使操作系统中的 root 用户,执行文件操作时也会受到权限控制,无法随意修改其他用户的文件。
shell
# root in /tmp/jfs
$ ll /tmp/jfs/f1
-rw------- 1 user2 user2 0 2月 19 16:26 /tmp/jfs/f1
# 读取用户user2的文件,可以直接读取
$ cat /tmp/jfs/f1
hello
映射后
shell
# root用户映射为普通用户user1(uid=1001,gid=1001)
./juicefs mount sqlite3://test.db /tmp/jfs -o allow_other -root-squash=1001:1001
# 读取user2的文件,权限不足
$ cat /tmp/jfs/f1
cat: /tmp/jfs/f1: Permission denied
- all-squash (所有用户映射为指定用户)
在 all-squash 模式下,系统将所有用户映射为指定的用户(例如 user1),此模式通常用于统一权限管理,所有操作都会以指定用户的身份进行。该功能将在 JuiceFS 社区 1.3 版本发布。
shell
# 所有用户映射为user1(uid=1001,gid=1001)
$ ./juicefs mount sqlite3://test.db /tmp/jfs -o allow_other -all-squash=1001:1001
# user2 创建文件
$ whoami
user2
$ touch f1
# user3 创建文件
$ whoami
user3
$ touch f2
# 最后文件拥有者都是user1
$ ls -l .
total 0
-rw-rw-r-- 1 user1 user1 0 2月 19 14:10 f1
-rw-rw-r-- 1 user1 user1 0 2月 19 14:11 f2
03 POSIX ACL
Unix Permission 已经能够满足大部分的权限管理需求,但是在某些场景下,我们需要更加细粒度的权限控制。比如需要单独为某个用户开放文件权限的时候,这时候就可以用 ACL 做精细控制。 POSIX ACL(Access Control List)是一种扩展的权限控制机制,虽然没有统一的标准,但是草案《POSIX 1003.1e draft 17》是被广泛接受的。基于这个草案的实现,在 2002 年 11 月被添加到 Linux 内核的 2.5.46 版本中。
传统的 Unix Permission 只对主体做了三种分类(owner、group、other),而 POSIX ACL 在此基础上,对主体进行了更细致的分类,可以对任意的用户/组分配 rwx 权限。所有的用户权限定义在 ACL 中可以抽象成一条 entry 定义。每一条 entry,包含用户 id,以及他的权限定义,结构定义如下:
go
type Permission struct {
Owner Mode
Group Mode
Other Mode
}
type ACL struct {
Owner Mode
Group Mode
Other Mode
Mask Mode // 权限上界
NamedUsers Entry // 新增指定用户权限定义
NamedGroups Entry // 新增指定用户组权限定义
}
type Entry struct {
Uid uint32
Perm Mode
}
POSIX ACL 新增了指定用户(NamedUsers)和指定用户组(NamedGroup)的权限。从定义上看,我们可以把 POSIX ACL 新增的 entry(包括 Mask)划分到 group class,将 group class 的定义从原来所在组的权限,扩展到 group class 中所有 entry 的权限上界。
分组 | entry 项 | 文本格式 |
---|---|---|
owner class | owner | user::rwx |
group class | named user | user:name:rwx |
group class | owning group | group::rwx |
group class | named group | group:name:rwx |
group class | mask | mask::rwx |
other class | others | other::rwx |
当没有配置额外的 named user 和 named group 时,称之为 Minimal ACL, 与 Unix Permission 等价。
- Minimal ACL:等同于普通 unix permission
- Extended ACL:配置了任意 named user 或 named group 项
下面操作展示了 POSIX ACL 与 Unix Permission 之间的关联性。
shell
# 查看权限
$ ls -l
drwxr-xr-x 2 root root 4.0K 4月 12 09:32 d1
# 通过setfacl设置d1目录的group1组权限
$ setfacl -m g:group1:rwx d1
# 再次查看权限
$ ls -l
drwxrwxr-x+ 2 root root 4.0K 4月 12 09:32 d1
设置了 ACL 之后,文件的 group class 也发生了变化, 增加了 "w" 权限。这是因为 ACL 拓展了 group class(请参考表格 1 中的分组)。
unix permission 中的 group class 仅代表了 owner group 的权限,但 POSIX ACL 中代表 group class 所有条目的权限上界。因此 group class 的权限位也会随条目变动而变化.
ACL 新增条目:Mask
上文提到 ACL 中新增的条目 "mask" ,该条目用于表示 group class 的动态权限变化。设置了 ACL 后,permission mode 的 group class 显示就是 mask。

通过 setfacl 设置 ACL, 默认会自动计算出 mask。比如上文所示, mask = owner group entry | group1 entry, 取并集"rwx"; 也可以独立设置 mask, 则 group class 中 entry 项的最终权限需要与 mask 取交集. 如下文:
- owner group 最终生效的权限为:"r-x" & "-wx" = "--x"
- group1 权限为:"rwx" & "-wx" = "-wx"
shell
# 接上文操作, 设置mask为"-wx"
$ setfacl -m m::-wx d1
# 查看ACL
$ getfacl d1 --omit-header
user::rwx
group::r-x #effective:--x
group:group1:rwx #effective:-wx
mask::-wx
other::r-x
ACL 类别
ACL 分为两种类别,Access ACL 和 Default ACL。
- Access ACL: 用于权限定义和检查, 适用于文件和目录,定义如上面章节所述。
- Default ACL:结构与 Access ACL 相同,但仅应用于目录。子文件/子目录的 Access ACL将继承自父目录的 Default ACL。(继承后的权限,结合创建文件对象的系统调用的mode 参数生成最终的 Access ACL, 其中 owner/mask/other与mode 取交集)。
shell
# 设置access acl
$ setfacl -m u:user1:rwx d1
# 设置default acl (with -d flag)
setfacl -d -m u:user1:rwx d1
# 查看acl
$ getfacl -c d1
# 前5项是access acl配置
user::rwx
user:user1:rwx
group::rwx
mask::rwx
other::r-x
# 后面5项是default acl配置
default:user::rwx
default:user:user1:rwx
default:group::rwx
default:mask::rwx
default:other::r-x
Access ACL 权限判断规则
进程请求访问文件对象,权限判断规则如下:
- 首先找到一条最匹配的 ACL entry
- entry按照以下顺序查找:
- owner
- named users
- owning or named groups
- others
- 一个进程可能属于多个组,有多条group class entry匹配(上面第3项),那么取拥有权限的那条entry。如果有匹配的group class entry,但都不满足权限要求,那么请求被拒绝。
- entry按照以下顺序查找:
- 检查这条 entry 的权限要求是否满足请求
如果对细节感兴趣,可以参考 JuiceFS 社区版 pkg/acl/acl.go
中的实现。
JuiceFS 的 ACL
JuiceFS 通过将 ACL 和扩展属性(xattr)解耦来优化 ACL 的存储。每个文件不再单独保存一份 ACL,而是将 ACL 存储在一个独立的元数据结构中,并通过全局唯一的 ACL ID 来引用相同的 ACL 配置。这种方法显著减少了重复数据存储需求,并提高了性能,特别是在使用 Default ACL 的目录下创建文件时,减少了存储 ACL 请求的频率。详情,请参考文章:ACL 功能全解析,更精细的权限控制;配置参考文档:POSIX ACL、JuiceFS POSIX ACL 权限管理上手指南。
04 Capability,细分权限能力,降低系统安全风险
传统的 unix 权限只区分了两种类别进程,特权 (root) 和非特权。 root 进程拥有系统的所有权限, 比如前面所述的 unix permission 和 acl 权限检查对其无效,可以任意访问/修改文件对象。
从 Linux 内核 2.2 版本之后,加入了 Capability 机制,将权限细分成不同能力。这样可以给非特权进程赋予部分特权,降低了系统的安全风险。
二进制文件的 capability 集合是记录在命名空间 security 下的拓展属性的。可以通过 getcap 和setcap 命令查看和设置 capability。
如果在 JuiceFS中(1.3版本)使用的话,需要同时开启拓展属性(挂载参数 --enable-xattr
)和capability (挂载参数 --enable-cap
) 支持。默认 capability 是关闭的,因为一般场景比较少使用到,开启后会增加许多对拓展属性的元数据请求,影响性能。
shell
# 挂载, 需要启动xattr和cap
$ ./juicefs mount sqlite3://test.db /tmp/jfs --enable-cap --enable-xattr
# 设置capability:普通用户执行bin二进制,可以拥有修改文件owner的权限
$ sudo setcap 'cap_chown=+ep' bin
# 查看capability
$ getcap bin
bin cap_chown=ep
05 SELinux:增强 Linux 系统安全的内核模块
前面讲述 DAC 与 MAC 两种模型的原理与区别,DAC 比较灵活但不严格,有一定的安全隐患。在 DAC 的访问控制机制中,传统 Linux 由于 root 权限的"权力"过大而存在安全威胁。
MAC 模型则能解决这个问题,它是一种强制访问控制机制,由系统管理员配置策略,严格执行。SElinux 是实现 MAC 模型的一个安全增强的 Linux 内核模块,它控制了无限的 root 权限。
虽然 SELinux 和 AppArmor 都是 Linux 上的实现 MAC 的安全模块。但是使用和实现上有一些区别。不同 Linux 发行版默认使用不同,SELiunx 主要用于 RHEL/Fedora,AppArmor主要用于 Ubuntu 和 SUSE。这里主要介绍 SELinux。
在启用了 SELinux 的 Linux 操作系统中,系统权限管理要先通过 DAC 机制的检测, 再检测 SELinux 的安全策略。在 SELinux 中,每个对象(比如文件)都有一个 安全上下文(Security Context),通过文件拓展属性,记载着该对象具有的权限(SELinux 定义的权限)。
如果在 JuiceFS 中(1.3 版本)使用的话,需要同时开启拓展属性(挂载参数--enable-xattr
)和SELinux (挂载参数--enable-selinux
)支持。默认 ``--enable-selinux` 也是关闭的。
06 小结
本文全面梳理了 Linux 文件权限管理模型,并探讨了 JuiceFS 在此基础上的权限管理实现。作为一款基于 FUSE 的用户态分布式文件系统,JuiceFS 不仅兼容传统的 Unix 权限机制,还通过用户态权限校验、root-squash 和 all-squash 等特殊功能,提供了更加灵活的访问控制策略。这些功能使得 JuiceFS 能够在多种权限管理需求下稳定运行,特别适用于需要高安全性和复杂用户管理的环境。
通过灵活配置权限管理,JuiceFS 能有效提高数据的安全性、合规性,并确保在多用户环境中提供稳定和高效的数据存取服务,帮助企业应对更复杂的存储和安全挑战。