DAC(RWX)

目录

  1. 概述:什么是 DAC
  2. DAC 的核心模型:主体-客体-规则
  3. UGO 权限模型详解
  4. 特殊权限位:SUID、SGID、Sticky Bit
  5. 进程凭证:内核中的身份表示
  6. 内核中的 DAC 权限检查流程
  7. ACL:突破 UGO 的局限
  8. 实战命令大全
  9. DAC 的安全局限与 MAC 的补充
  10. 最佳实践
  11. 总结

1. 概述:什么是 DAC

DAC(Discretionary Access Control,自主访问控制)是 Linux 最基础、最核心的权限控制机制。它的核心思想是:资源的所有者可以自主决定谁可以访问该资源

DAC 在 Linux 安全体系中的位置

用户进程请求访问文件
        │
        ▼
┌─────────────────────────────┐
│  第一道防线:DAC            │  ← 本文主题
│  ├─ UGO 权限检查 (rwx)      │     传统 9 位权限
│  ├─ ACL 扩展权限            │     细粒度访问控制
│  └─ Capabilities 特权绕过   │     CAP_DAC_OVERRIDE 等
└─────────────┬───────────────┘
              │ DAC 通过
              ▼
┌─────────────────────────────┐
│  第二道防线:MAC            │
│  ├─ SELinux (标签级)        │     强制访问控制
│  └─ AppArmor (路径级)       │     补充 DAC 不足
└─────────────┬───────────────┘
              │ MAC 通过
              ▼
         执行操作

关键特性:DAC 是 Linux 访问控制的第一道防线。如果 DAC 拒绝了访问,MAC(SELinux/AppArmor)根本不会被触发。DAC 先执行,MAC 后执行。

DAC 与 MAC 的核心区别

维度 DAC(自主访问控制) MAC(强制访问控制)
决策者 资源所有者 系统管理员(全局策略)
灵活性 高:用户可以自由授权 低:全局策略强制执行
Root 权限 Root 可以绕过 Root 也可能被限制
典型实现 UGO + ACL SELinux、AppArmor
粒度 用户/组级别 进程域/文件类型级别

2. DAC 的核心模型:主体-客体-规则

DAC 基于一个简洁的主体-客体-规则模型:

┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│   主体        │       │   规则        │       │   客体        │
│  (Subject)   │──────►│  (Policy)    │──────►│  (Object)    │
│              │ 请求   │              │ 判定   │              │
│  进程        │       │  UGO + ACL   │       │  文件/目录   │
│  凭证:       │       │              │       │  属主: uid   │
│  uid/gid     │       │              │       │  属组: gid   │
│  euid/fsuid  │       │              │       │  权限: mode  │
└──────────────┘       └──────────────┘       └──────────────┘

三大要素

要素 定义 DAC 中的具体实现
主体(Subject) 发起访问请求的实体 进程,其身份由 euidegidfsuid 等凭证标识
客体(Object) 被访问的目标资源 文件、目录、设备等,其身份由 uidgidmode 标识
规则(Policy) 判定是否允许访问的规则 UGO 权限位 + ACL 扩展规则

DAC 的核心特点:自主性

文件所有者 (alice) 创建了 report.txt:
  -rw-r--r-- alice devteam report.txt

  alice 可以自主决定:
  ├─ chmod g+w report.txt      → 让同组用户可写
  ├─ chmod o-r report.txt      → 禁止其他人读取
  ├─ setfacl -m u:bob:rw ...   → 单独授权给 bob
  └─ chown bob report.txt      → 转让所有权给 bob

  这就是"自主"的含义——所有者决定权限。

3. UGO 权限模型详解

3.1 User、Group、Other 三类主体

UGO 将访问者分为三类:

文件的权限位(共 9 位):

  ┌─── User ──┐ ┌── Group ──┐ ┌── Other ──┐
  │  r   w   x  │ │ r   w   x │ │ r   w   x │
  └─────────────┘ └───────────┘ └───────────┘
      所有者权限      所属组权限      其他人权限

三类主体的判定优先级(短路匹配):
  1. User:进程的 fsuid == 文件的 uid → 应用 User 权限
  2. Group:进程属于文件的 gid 所在组 → 应用 Group 权限
  3. Other:以上都不匹配 → 应用 Other 权限

3.2 rwx 权限详解

对文件的权限

权限 字母 八进制 含义
r 4 读取文件内容 (cat, less, cp)
w 2 修改文件内容 (vim, >, >>)
执行 x 1 作为程序/脚本执行 (./script.sh)

对目录的权限

权限 字母 八进制 含义
r 4 列出目录内容 (ls)
w 2 在目录中创建/删除/重命名文件
执行 x 1 进入目录 (cd);访问目录中的文件
目录权限的特殊性——路径遍历:

  要访问 /a/b/c.txt,需要:
  ├─ /      有 x 权限
  ├─ /a     有 x 权限
  ├─ /a/b   有 x 权限
  └─ /a/b/c.txt 有 r 权限

  缺少任何一级的 x 权限都会导致 "Permission denied"!

  dr--r--r--  → 可以 ls 列出文件名,但不能 cd 进入
  d--x--x--x  → 可以 cd 进入并访问已知文件,但不能 ls 列出
  drwxr-xr-x  → 正常的可读写可进入目录

3.3 权限的八进制表示

# 每种权限对应一个数字:r=4, w=2, x=1

rwx = 4+2+1 = 7    rw- = 4+2+0 = 6    r-x = 4+0+1 = 5
r-- = 4+0+0 = 4    -w- = 0+2+0 = 2    --x = 0+0+1 = 1
--- = 0+0+0 = 0

# 完整权限示例
chmod 755 script.sh    # rwxr-xr-x(可执行文件标准权限)
chmod 644 data.txt     # rw-r--r--(数据文件标准权限)
chmod 600 secret.key   # rw-------(私密文件)
chmod 777 public/      # rwxrwxrwx(危险!所有人都能读写)

# 符号模式(更直观)
chmod u+x script.sh    # 给所有者加执行权限 (user +x)
chmod g-w data.txt     # 移除组的写权限 (group -w)
chmod o= secret.key    # 清空其他人的所有权限 (other =)
chmod a+r public.txt   # 给所有人加读权限 (all +r)
chmod u=rwx,g=rx,o= app   # 设置精确权限

3.4 umask:默认权限掩码

umask 控制新创建文件和目录的默认权限

# 查看当前 umask
umask          # 0022(常见默认值)
umask -S       # u=rwx,g=rx,o=rx(符号模式显示)

# umask 计算原理
# 文件默认 = 0666 - umask
# 目录默认 = 0777 - umask

umask 0022:文件 0666-0022=0644 (rw-r--r--)  目录 0777-0022=0755 (rwxr-xr-x)
umask 0002:文件 0666-0002=0664 (rw-rw-r--)  目录 0777-0002=0775 (rwxrwxr-x)
umask 0077:文件 0666-0077=0600 (rw-------)  目录 0777-0077=0700 (rwx------)

# 临时修改
umask 0027

# 永久修改
echo "umask 0027" >> ~/.bashrc

4. 特殊权限位:SUID、SGID、Sticky Bit

4.1 SUID(Set User ID)— 八进制值 4

作用于可执行文件。执行时,进程的有效 UID 变为文件所有者的 UID。

# 经典示例:passwd 命令
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root /usr/bin/passwd
#    ↑ s 在所有者 x 位 → SUID 已设置

# passwd 需要修改 /etc/shadow(仅 root 可写)
# 普通用户通过 SUID 临时获得 root 的 euid 来修改自己的密码

# 设置 SUID
chmod u+s /path/to/program
chmod 4755 /path/to/program

# 查看所有 SUID 文件
find / -perm -4000 -type f 2>/dev/null

# ⚠️ 危险示例:不要给 shell 设置 SUID!
# chmod u+s /bin/bash  ← 任何用户执行 bash 都获得 root shell

4.2 SGID(Set Group ID)— 八进制值 2

作用于可执行文件:执行时进程的 egid 变为文件所属组。

作用于目录:目录中新建文件的所属组继承目录的组

# SGID 作用于目录——共享项目目录的经典用法
mkdir /shared/project
chgrp devteam /shared/project
chmod 2770 /shared/project    # SGID + rwxrwx---
ls -ld /shared/project
# drwxrws--- 2 root devteam /shared/project
#       ↑ s 在组 x 位 → SGID 已设置

# 任何 devteam 成员在该目录创建的文件都属于 devteam 组

4.3 Sticky Bit(粘滞位)— 八进制值 1

仅作用于目录。目录中的文件只能被文件所有者或 root 删除

# 经典应用:/tmp 目录
ls -ld /tmp
# drwxrwxrwt 1 root root /tmp
#          ↑ t 在 other x 位 → Sticky Bit 已设置

# /tmp:所有人都可以创建文件,但只能删除自己的文件
chmod +t /shared/scratch
chmod 1777 /shared/scratch

4.4 特殊权限位的显示

# 大小写含义:
# SUID:s (有执行权限时) 或 S (无执行权限时)
# SGID:s (有执行权限时) 或 S (无执行权限时)
# Sticky:t (有执行权限时) 或 T (无执行权限时)

-rwsr-xr-x  → SUID + 所有者有 x
-rwSr--r--  → SUID + 所有者无 x (大写 S 警告)
drwxrws---  → SGID + 组有 x
drwxrwxrwt  → Sticky + other 有 x

5. 进程凭证:内核中的身份表示

5.1 struct cred 与四种 UID

内核不识别用户名,只识别 UID。每个进程通过 struct cred 维护其安全凭证:

// 简化的 struct cred(内核源码:include/linux/cred.h)
struct cred {
    kuid_t  uid;      /* 实际 UID (real UID) — 进程的真实身份 */
    kgid_t  gid;      /* 实际 GID */
    kuid_t  euid;     /* 有效 UID (effective UID) — 权限检查的核心 */
    kgid_t  egid;     /* 有效 GID */
    kuid_t  suid;     /* 保存的 UID (saved UID) — 暂存切换前的 euid */
    kgid_t  sgid;     /* 保存的 GID */
    kuid_t  fsuid;    /* 文件系统 UID — 用于 VFS 操作的权限检查 */
    kgid_t  fsgid;    /* 文件系统 GID */
    // ...
};
字段 全称 作用
uid Real UID 进程的真实身份,通常是登录用户的 UID
euid Effective UID 权限检查的核心依据,决定进程能以谁的身份操作
suid Saved UID 保存 euid 被修改前的值,用于权限恢复
fsuid Filesystem UID Linux 特有,专门用于文件系统操作的权限检查
通常情况下:
  uid == euid == suid == fsuid

SUID 程序执行后:
  uid = 1000 (alice 的真实身份)
  euid = 0   (临时提升为 root)
  suid = 0   (保存 root 身份,用于切换)
  fsuid = 0  (文件系统操作也以 root 身份)

  → 进程以 root 权限运行,但真实身份仍是 alice

5.2 权限提升:SUID 的内核实现

// 内核在 execve() 时处理 SUID(简化版)
// 源码路径:fs/exec.c → bprm_fill_uid()

static void bprm_fill_uid(struct linux_binprm *bprm) {
    // 检查文件是否设置了 SUID 位
    if (bprm->file->f_inode->i_mode & S_ISUID) {
        // 将新进程的 euid 设置为文件所有者的 uid
        bprm->cred->euid = bprm->file->f_inode->i_uid;
        bprm->cred->fsuid = bprm->file->f_inode->i_uid;
    }

    // 检查文件是否设置了 SGID 位
    if (bprm->file->f_inode->i_mode & S_ISGID) {
        bprm->cred->egid = bprm->file->f_inode->i_gid;
    }
}

5.3 权限降级:setuid 系列系统调用

// 三种 setuid 系统调用

// 1. setuid() — 简单设置
//    Root:可设为任意值,且 uid/euid/suid/fsuid 全部被设置
//    非 root:只能设为 uid 或 euid 或 suid 中的值
int setuid(uid_t uid);

// 2. setreuid() — 分别设置 real uid 和 effective uid
//    非 root:只能将 ruid 设为 ruid 或 euid,将 euid 设为 ruid/euid/suid
int setreuid(uid_t ruid, uid_t euid);

// 3. setresuid() — 精确设置三个 UID(最灵活)
//    非 root:每个参数只能设为当前 ruid/euid/suid 中的值
int setresuid(uid_t ruid, uid_t euid, uid_t suid);

// 典型用法(sudo 等工具):
// 1. 临时提升到 root → setresuid(-1, 0, -1)
// 2. 完成操作后降级 → setresuid(target_uid, target_uid, target_uid)

6. 内核中的 DAC 权限检查流程

6.1 VFS 层的权限检查路径

open("/a/b/c.txt", O_RDONLY) 为例:

用户空间:open("/a/b/c.txt", O_RDONLY)
    │
    ▼
VFS:do_sys_open()
    │
    ▼
路径遍历——对每一级目录检查 x 权限
    │
    ├─ may_lookup("/")        → 检查 / 的 x 权限
    ├─ may_lookup("/a")       → 检查 /a 的 x 权限
    ├─ may_lookup("/a/b")     → 检查 /a/b 的 x 权限
    │
    ▼
may_open("/a/b/c.txt", O_RDONLY)
    │
    ├─ 检查 O_RDONLY → 需要 r 权限
    ├─ 检查 O_WRONLY → 需要 w 权限
    ├─ 检查 O_TRUNC  → 需要 w 权限
    └─ 检查 O_APPEND → 需要 w 权限
    │
    ▼
inode_permission(inode, MAY_READ)
    │
    ├─ generic_permission()  → UGO 检查
    └─ acl_permission_check() → ACL 检查

6.2 acl_permission_check() 核心函数

// 内核源码:fs/namei.c(简化版)
static int acl_permission_check(struct inode *inode, int mask)
{
    unsigned int mode = inode->i_mode;

    // 1. Root 特权检查
    if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID))
        return capable(CAP_DAC_OVERRIDE) ? 0 : -EACCES;
    // 如果 fsuid==0 且有 CAP_DAC_OVERRIDE → 直接放行

    // 2. User 匹配:进程 fsuid == 文件 uid
    if (uid_eq(current_fsuid(), inode->i_uid)) {
        mode >>= 6;  // 取 owner 权限位
        goto check;
    }

    // 3. ACL 检查(如果存在)
    if (IS_POSIXACL(inode)) {
        int error = posix_acl_permission(inode, current_fsuid(), mask);
        if (error != -EAGAIN)
            return error;  // ACL 明确允许或拒绝
    }

    // 4. Group 匹配:进程属于文件 gid 所在组
    if (in_group_p(inode->i_gid)) {
        mode >>= 3;  // 取 group 权限位
        goto check;
    }

    // 5. Other:以上都不匹配
    // mode 保持为 other 权限位

check:
    // 检查 mask 要求的权限是否在 mode 中
    if ((mask & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0)
        return 0;  // 权限足够
    return -EACCES;
}

6.3 DAC 与 MAC 的执行顺序

// 完整的文件访问检查流程(简化)
int do_file_open(const char *pathname, int flags) {
    struct inode *inode;

    // 步骤 1:DAC 检查
    int error = inode_permission(inode, acc_mode);
    if (error)
        return error;  // DAC 拒绝 → 直接返回,MAC 不执行

    // 步骤 2:MAC 检查(LSM Hook)
    error = security_inode_permission(inode, acc_mode);
    if (error)
        return error;  // SELinux/AppArmor 拒绝

    // 步骤 3:执行实际操作
    return do_actual_open(inode, flags);
}

关键顺序:DAC → MAC。如果 DAC 拒绝了,MAC 检查被跳过。这就是为什么 SELinux 的 AVC 日志只在 DAC 允许后才出现。


7. ACL:突破 UGO 的局限

7.1 为什么需要 ACL

UGO 模型只能为一个文件设置一个所有者、一个组、一个"其他人",无法为多个特定用户设置不同权限:

问题场景:
  report.txt 属于 alice:devteam
  - alice 需要 rw-(读写)
  - bob 需要 r--(只读)
  - carol 需要 ---(禁止访问)
  - devteam 组需要 r--(只读)

  UGO 无法实现!因为:
  ├─ User 只能匹配一个用户(alice)
  ├─ Group 只能匹配一个组(devteam)
  └─ bob 和 carol 要么在 devteam 组,要么在 Other

ACL 解决方案:
  setfacl -m u:bob:r-- report.txt      # bob 只读
  setfacl -m u:carol:--- report.txt    # carol 禁止
  setfacl -m g:devteam:r-- report.txt  # devteam 只读

7.2 ACL 条目类型

# 查看 ACL
getfacl report.txt

# 输出示例:
# file: report.txt
# owner: alice
# group: devteam
user::rw-           # 所有者 ACL(对应 UGO 的 User)
user:bob:r--        # 指定用户 ACL
user:carol:---      # 指定用户 ACL
group::r--          # 所属组 ACL(对应 UGO 的 Group)
group:auditors:r--  # 指定组 ACL
mask::r--           # ACL Mask(有效权限上限)
other::---          # 其他人 ACL(对应 UGO 的 Other)
条目类型 语法 对应
所有者 user::rwx UGO 的 User 权限
指定用户 user:alice:rw- ACL 扩展
所属组 group::r-x UGO 的 Group 权限
指定组 group:devteam:r-- ACL 扩展
Mask mask::r-x 有效权限上限
其他人 other::r-- UGO 的 Other 权限

7.3 ACL Mask 与有效权限

ACL Mask 是 ACL 中最重要的概念之一——它是所有指定用户、指定组和所属组的有效权限上限

# 设置 ACL
setfacl -m u:bob:rwx report.txt

# 查看
getfacl report.txt
# user:bob:rwx
# mask::r--        ← Mask 只有 r--
#
# 实际有效权限:bob 只有 r--(被 mask 限制)
# 显示为:user:bob:rwx  #effective:r--

# 原理:
# 指定用户/组的 实际权限 = 设置的权限 ∩ Mask
# rwx ∩ r-- = r--  → bob 实际只有读权限

# 更新 Mask
setfacl -m mask::rwx report.txt
# bob 现在有完整的 rwx 了

Mask 自动重算规则:

当使用 setfacl 添加/修改/删除 ACL 条目时,
Mask 会自动重新计算为:
  max(所有指定用户权限, 所有指定组权限, 所属组权限)

7.4 Default ACL:目录继承

Default ACL 让目录中的新创建文件和子目录自动继承 ACL

# 设置 Default ACL
setfacl -m d:u:bob:r-- /shared/project
setfacl -m d:g:devteam:rwx /shared/project

# 查看
getfacl /shared/project
# user::rwx
# group::r-x
# other::---
# default:user::rwx
# default:user:bob:r--
# default:group::r-x
# default:group:devteam:rwx
# default:mask::rwx
# default:other::---

# 新建文件自动继承
touch /shared/project/newfile.txt
getfacl /shared/project/newfile.txt
# user::rw-
# user:bob:r--        ← 自动继承
# group::r-x          ← 自动继承
# group:devteam:rwx   ← 自动继承

7.5 getfacl 与 setfacl 实战

# ========== getfacl:查看 ACL ==========
getfacl file.txt                    # 查看单个文件
getfacl -R /shared/project/         # 递归查看目录
getfacl -t file.txt                 # 表格形式输出
getfacl -c file.txt                 # 简洁模式(不显示注释)
getfacl --omit-header file.txt      # 不显示头部

# ========== setfacl:设置 ACL ==========
# 基本语法:setfacl -m <条目> <文件>

# 添加用户权限
setfacl -m u:bob:rw- report.txt     # bob 读写
setfacl -m u:carol:r-- report.txt   # carol 只读

# 添加组权限
setfacl -m g:auditors:r-- report.txt

# 修改权限
setfacl -m u:bob:rwx report.txt     # 修改 bob 的权限

# 设置 Mask
setfacl -m mask::rwx report.txt

# 删除 ACL 条目
setfacl -x u:bob report.txt         # 删除 bob 的条目
setfacl -x g:auditors report.txt    # 删除 auditors 组的条目

# 删除所有 ACL
setfacl -b report.txt               # 删除所有扩展 ACL
setfacl -k /shared/project/         # 删除所有 Default ACL

# 递归操作
setfacl -R -m u:bob:r-- /shared/    # 递归设置

# 从文件读取 ACL
getfacl source.txt | setfacl --set-file=- target.txt  # 复制 ACL

8. 实战命令大全

8.1 基础权限管理

# 查看权限
ls -l file.txt              # 详细列表
ls -ld /etc                 # 目录本身而非内容
stat file.txt               # 更详细的信息(含八进制权限)

# 修改权限
chmod 755 script.sh         # 八进制
chmod u+x,g+w,o-r file.txt  # 符号模式
chmod -R 755 /var/www/      # 递归修改

# 修改所有者和组
chown alice file.txt                    # 改所有者
chown alice:devteam file.txt            # 改所有者和组
chown :devteam file.txt                 # 仅改组
chgrp devteam file.txt                  # 改组
chown -R alice:devteam /var/www/        # 递归

# 查看用户和组信息
id alice                    # 用户 UID 和所属组
groups alice                # 用户的所有组
getent passwd alice         # passwd 条目
getent group devteam        # group 条目

8.2 特殊权限管理

# 查找特殊权限文件
find / -perm -4000 -type f 2>/dev/null    # SUID
find / -perm -2000 -type f 2>/dev/null    # SGID
find / -perm -1000 -type d 2>/dev/null    # Sticky Bit
find / -perm -6000 -type f 2>/dev/null    # SUID + SGID

# 设置
chmod u+s /usr/bin/myapp    # SUID
chmod g+s /shared/dir       # SGID
chmod +t /shared/scratch    # Sticky Bit

# 移除
chmod u-s /usr/bin/myapp    # 移除 SUID
chmod g-s /shared/dir       # 移除 SGID
chmod -t /shared/scratch    # 移除 Sticky Bit

8.3 ACL 管理

# 查看
getfacl file.txt

# 设置
setfacl -m u:alice:rw file.txt
setfacl -m g:devteam:r file.txt
setfacl -m d:u:bob:r /shared/   # Default ACL

# 删除
setfacl -x u:alice file.txt     # 删除条目
setfacl -b file.txt             # 删除所有扩展 ACL

# 批量备份和恢复
getfacl -R /important/ > acl_backup.txt
setfacl --restore=acl_backup.txt

8.4 权限诊断与调试

# 模拟权限检查
namei -l /path/to/file
# 输出每一级目录的权限

# 测试文件是否可读/可写/可执行
test -r /path/to/file && echo "readable"
test -w /path/to/file && echo "writable"
test -x /path/to/file && echo "executable"

# 以特定用户身份测试
sudo -u alice test -r /path/to/file && echo "alice can read"
sudo -u alice cat /path/to/file

# 查看进程的实际身份
ps -eo pid,user,group,comm
cat /proc/$$/status | grep -E "^Uid|^Gid"

# 审计文件权限问题
find /home -type f -perm 0777     # 查找权限过宽的文件
find / -nouser -o -nogroup        # 查找无主文件
find / -perm -4000 -type f -ls    # 审计 SUID 文件

9. DAC 的安全局限与 MAC 的补充

DAC 的主要局限

局限 说明 示例
粒度粗 UGO 只有三类主体 无法给第 4 个用户单独设权限(需 ACL)
Root 全能 Root 可以绕过所有 DAC 检查 被攻破的 root 进程可以读取任何文件
无法防恶意软件 用户运行的恶意程序继承用户的所有权限 恶意脚本可以删除用户的所有文件
用户可自由授权 用户可能不小心给了过多权限 chmod 777 important_data/
无法基于进程类型限制 DAC 只看 UID,不看程序是什么 不能限制"nginx 只能读 /var/www"
硬链接可能绕过 同一个 inode 可以通过不同路径访问 无权限用户可以通过硬链接访问受保护文件

MAC 如何弥补 DAC 的不足

DAC 场景:alice 运行了一个有漏洞的 Web 服务器
  → Web 服务器以 alice 身份运行
  → 漏洞被利用后,攻击者可以读取 alice 的所有文件
  → DAC 无法阻止(进程就是 alice 的身份)

MAC 场景:同样的漏洞
  → SELinux 策略:httpd_t 域只能读 httpd_sys_content_t 类型
  → 即使进程以 alice 身份运行,SELinux 仍然限制它
  → 攻击者无法访问 alice 的其他文件

完整的 Linux 权限检查链

                    ┌─────────────────────────┐
进程请求访问文件     │  1. DAC (自主访问控制)   │
    │               │  ├─ UGO 权限位            │
    ▼               │  ├─ ACL 扩展规则          │
┌───────┐           │  └─ Capabilities 特权绕过 │
│ DAC  │── 拒绝 ──► 返回 EACCES                 │
└───┬───┘           └─────────────────────────┘
    │ 通过
    ▼               ┌─────────────────────────┐
┌───────┐           │  2. MAC (强制访问控制)   │
│ MAC  │── 拒绝 ──► │  ├─ SELinux (标签级)     │
└───┬───┘           │  └─ AppArmor (路径级)    │
    │ 通过           └─────────────────────────┘
    ▼
  执行操作

10. 最佳实践

10.1 权限设置原则

# ✅ 最小权限原则
chmod 600 secret.key          # 只有所有者可读写
chmod 750 ~/scripts/           # 所有者 rwx,组 rx,其他人无权限
chmod 640 /etc/app/config.yml  # 所有者 rw,组 r,其他人无

# ✅ 使用组而非单独授权
groupadd devteam
usermod -aG devteam alice bob carol
chgrp devteam /shared/project
chmod 770 /shared/project

# ✅ 需要单独授权时使用 ACL
setfacl -m u:contractor:r-- /shared/project/report.txt

# ❌ 永远不要这样做
chmod 777 important_data/         # 所有人可读写!
chmod -R 777 /var/www/            # Web 目录 777!
chmod u+s /bin/bash               # Shell 的 SUID!

10.2 安全审计 Checklist

# 1. 检查全局可写目录
find / -type d -perm -002 -not -perm -1000 2>/dev/null

# 2. 检查无主文件
find / -nouser -o -nogroup 2>/dev/null

# 3. 审计 SUID/SGID 文件
find / -perm -6000 -type f 2>/dev/null > suid_audit.txt

# 4. 检查用户家目录权限
find /home -maxdepth 1 -type d -perm /027

# 5. 检查 SSH 相关文件的权限
ls -l /etc/ssh/ssh_host_*_key   # 应为 600
ls -l ~/.ssh/id_*               # 应为 600
ls -l ~/.ssh/authorized_keys    # 应为 600

# 6. 定期审查 ACL
getcap -r / 2>/dev/null > caps_audit.txt
getfacl -R /etc /var 2>/dev/null | grep -E "^user:|^group:" | sort -u

10.3 常见配置模板

# Web 服务器目录
chmod 755 /var/www/                        # 目录
chmod 644 /var/www/*.html                  # 静态文件
chmod 640 /var/www/app/config.php          # 配置文件

# 用户家目录
chmod 700 /home/alice                      # 或 750
chmod 600 /home/alice/.ssh/id_rsa          # 私钥
chmod 644 /home/alice/.ssh/id_rsa.pub      # 公钥
chmod 600 /home/alice/.ssh/authorized_keys

# 共享项目目录
chmod 2770 /shared/project                 # SGID + 组权限
setfacl -m d:g:devteam:rwx /shared/project # 默认 ACL

11. 总结

核心要点

要点 说明
DAC 是第一道防线 在所有访问控制中首先执行,DAC 拒绝则 MAC 不触发
UGO 是基础 User/Group/Other 三类主体 + rwx 三种权限 = 9 位权限位
四种 UID uid/euid/suid/fsuid 共同决定进程的身份和权限
SUID 提升权限 执行时 euid 变为文件所有者,是 sudo/su 的基础
SGID 组继承 目录设置 SGID 后,新建文件自动继承目录的组
Sticky Bit 保护 /tmp 目录的经典应用,防止误删他人文件
ACL 突破 UGO 限制 可为任意多个用户/组设置不同权限
umask 控制默认权限 新文件的默认权限 = 0666 - umask
DAC + MAC 纵深防御 DAC 提供基础控制,MAC 提供强制策略

推荐资源


免责声明:本文内容基于 Linux 手册页和内核文档编写。具体行为可能因内核版本和发行版配置而异,请以实际运行环境为准。