4. ACL继承机制:显式继承与隐式继承、继承标志位、继承冲突解决
ACL继承,说白了就是权限怎么往下传。你想想看,一个目录下面有几百个文件,难道要一个个去设权限?那不得累死。继承机制就是来解决这个问题的。
我刚开始接触ACL时,觉得继承很简单——父目录有权限,子文件自然就有呗。后来被坑过几次才发现,这里面的门道比想象中深。今天咱们就把继承机制彻底掰开揉碎。
4.1 继承的本质:权限的传递规则
ACL继承的核心逻辑其实就一句话:子对象从父对象那里“复制”一份权限配置。但“复制”的方式有两种,而且差别很大。
关键概念:继承不是运行时动态查询,而是在子对象创建时,把父对象的ACL条目“拷贝”过来。之后两者就独立了。
我记得有一次排查问题,用户说“我改了父目录的权限,为什么子文件没变?”嗯,这就是没理解继承的时机。继承发生在创建时,不是每次访问时。
4.2 显式继承 vs 隐式继承
这两种继承方式,我习惯用“主动给”和“默认有”来区分。
| 特性 | 显式继承 | 隐式继承 |
|---|---|---|
| 触发条件 | 父对象ACL明确标记“可继承” | 子对象未设置任何ACL,默认继承父对象权限 |
| ACL条目 | 父对象的ACL条目被复制到子对象 | 子对象没有独立ACL,完全依赖父对象 |
| 独立性 | 子对象有独立ACL,可后续修改 | 子对象无独立ACL,修改父对象会影响子对象 |
| 常见场景 | 文件服务器、共享目录 | 普通文件系统默认行为 |
显式继承:父目录的ACL条目上带着“继承”标志位。子文件创建时,系统把这些条目原样复制过来。之后你改子文件的ACL,父目录管不着。
隐式继承:子对象压根没有自己的ACL。你查它的权限,系统说“你等等,我去问问它爹”。这种模式下,父目录一改,所有子对象跟着变。
我的建议:生产环境尽量用显式继承。隐式继承虽然省事,但一旦父目录权限配错,影响面会像多米诺骨牌一样倒下去。我在一个金融项目里见过,就因为一个根目录的隐式继承,导致整个部门文件全部暴露——那场面,啧。
4.3 继承标志位:控制继承的四个开关
继承标志位,就是ACL条目上的“小旗子”。每个标志位控制一个维度的继承行为。我整理了一张表,你一看就明白。
| 标志位 | 缩写 | 含义 | 作用范围 |
|---|---|---|---|
| OBJECT_INHERIT | OI | 允许继承到子文件 | 子文件(非目录) |
| CONTAINER_INHERIT | CI | 允许继承到子目录 | 子目录 |
| INHERIT_ONLY | IO | 该条目只用于继承,不作用于当前对象 | 仅子对象 |
| NO_PROPAGATE_INHERIT | NP | 阻止继续向下传播 | 仅下一级 |
这四个标志位可以组合使用。比如:
- OI | CI:最常见的组合,子文件和子目录都继承
- OI | CI | IO:只用来给后代设权限,当前目录自己不受影响
- OI | NP:只传给子文件,子文件再往下传?不行
我曾经踩过的坑:有一次配共享目录,给父目录加了“OI | CI”标志位,结果子目录里又建了子目录,权限一层层传下去,最后整个目录树权限乱成一锅粥。后来才意识到,应该在合适的层级加NP标志位,切断传播链。
4.4 继承冲突:当显式遇上隐式
冲突是怎么来的?很简单:一个子对象既有自己显式设置的ACL,又从父对象继承了ACL。这时候听谁的?
我见过最典型的场景是这样的:
# 父目录 /project 的ACL
user:alice:rwx # 显式,带 OI|CI 标志
user:bob:r-x # 显式,带 OI|CI 标志
# 子文件 /project/report.txt 的ACL
user:alice:r-- # 显式设置,覆盖继承
user:bob:rwx # 显式设置,覆盖继承
这时候alice对report.txt的权限是r--还是rwx?答案是r--。因为显式设置的优先级高于继承来的。
冲突解决规则其实就三条:
- 显式优先:子对象自己设的ACL条目,永远覆盖继承来的同名条目
- 继承条目合并:父对象继承来的条目,如果子对象没有同名条目,就保留
- 拒绝优先:如果显式和继承都有,取更严格的(deny优先于allow)
核心原则:显式继承的条目在子对象中变成“本地条目”,和子对象自己设的条目地位相同。冲突时,按ACL的常规评估顺序(deny > allow)处理。
4.5 继承机制全景图
下面这张图,我把整个继承流程画出来了。你盯着看两分钟,应该就能理清脉络。
4.6 实战中的继承策略
说了这么多理论,来点实际的。我在项目中总结了几条继承策略,你可以直接拿来用。
策略一:分层控制
顶层目录用宽权限(比如整个部门可读),越往下权限越窄。每一层用NP标志位切断不必要的传播。
# 顶层:部门共享目录
setfacl -m g:engineering:rx,d:g:engineering:rx /shared/eng
# 子层:项目A目录(切断继承,重新设)
setfacl -m g:project-a:rwx,d:g:project-a:rwx /shared/eng/proj-a
setfacl -R -m g:engineering:--- /shared/eng/proj-a # 覆盖继承
策略二:只继承不应用(IO标志位)
有时候你希望某个权限只传给后代,自己不受影响。IO标志位就是干这个的。
# 给审计组设权限:只继承,不作用于当前目录
setfacl -m g:audit:rx,d:g:audit:rx,IO /shared/eng
这样审计组进不了/shared/eng目录本身,但能访问里面的所有子文件。
我的习惯:每次配完继承,我都会用 getfacl -R 检查一下整个目录树。别嫌麻烦,我曾经就因为少配了一个NP标志位,导致权限泄露到三级子目录——还好是测试环境。
4.7 避坑指南
最后分享几个我亲身踩过的坑,你遇到了能少走弯路。
- 坑一:移动文件时继承不生效。把文件从一个目录mv到另一个目录,ACL不会重新继承。我见过有人mv完发现权限不对,排查了半天。记住,继承只在创建时发生,移动不算创建。
- 坑二:继承标志位被意外覆盖。你给父目录配了OI|CI,结果有人用setfacl -b清空了ACL,所有子对象的继承条目就断了。建议对关键目录做ACL变更审计。
- 坑三:隐式继承的“幽灵权限”。子对象没有ACL时,你以为它没权限,其实它从父目录继承了。我曾经在安全审计时发现一个文件权限是空的,但用户能访问——就是因为隐式继承。
重要提醒:生产环境慎用隐式继承。显式继承虽然配置起来麻烦一点,但每个对象的权限都是明确的,出了问题好排查。隐式继承就像隐式类型转换——方便一时,调试一世。