root@LAPTOP-LU4HDAFV:~# id
uid=0(root) gid=0(root) groups=0(root),987(docker)
root@LAPTOP-LU4HDAFV:~# echo $$
285399
root@LAPTOP-LU4HDAFV:~# cat /proc/$$/uid_map
0 0 4294967295
root@LAPTOP-LU4HDAFV:~#
UID/GID 详解
基本概念
- UID (User ID):用户标识符,Linux 用数字标识用户
-
GID (Group ID):组标识符,Linux 用数字标识用户组
┌─────────────────────────────────────────────────────────┐
│ Linux 用户系统结构 │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 用户名 (Username) │ │
│ │ │ │
│ │ 例如:alice, bob, root │ │
│ │ (人类可读的名称) │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ │ 映射关系 │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ UID (User ID) │ │
│ │ │ │
│ │ 例如:1000, 1001, 0 │ │
│ │ (系统内部使用的数字) │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ │ 属于某个组 │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ GID (Group ID) │ │
│ │ │ │
│ │ 例如:1000, 1001, 0 │ │
│ │ (用户所属组的数字) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 系统实际使用 UID/GID,而不是用户名! │
└─────────────────────────────────────────────────────────┘
为什么需要 UID/GID?
- 性能:数字比较更快
- 唯一性:每个用户有唯一 UID
-
系统内部:内核使用数字,用户名只是映射
┌─────────────────────────────────────────────────────────┐
│ 用户名 vs UID │
│ │
│ 人类视角: │
│ whoami │ │ alice │ │ │ │ 系统视角: │ │ id │
│ uid=1000(alice) gid=1000(alice) │
│ │
│ 内核实际使用: │
│ UID: 1000 ← 内核只认识这个数字 │
│ GID: 1000 ← 内核只认识这个数字 │
│ │
│ 用户名 "alice" 只是方便人类记忆的别名! │
└─────────────────────────────────────────────────────────┘
User Namespace 工作原理详解
核心概念:UID/GID 映射
User Namespace 通过映射表将 Namespace 内的 UID/GID 映射到主机上的真实 UID/GID。
┌─────────────────────────────────────────────────────────┐
│ User Namespace 的 UID/GID 映射机制 │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 主机上的真实用户 │ │
│ │ │ │
│ │ 用户:alice │ │
│ │ UID: 1000 (主机上的真实 UID) │ │
│ │ GID: 1000 (主机上的真实 GID) │ │
│ │ │ │
│ │ 执行命令: │ │
│ │ unshare --user --map-root-user /bin/bash │ │
│ │ │ │ │
│ │ └─────────┐ │ │
│ └───────────────────┼──────────────────────────────┘ │
│ │ │
│ │ 创建新的 User Namespace │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ User Namespace 内部 │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ UID/GID 映射表 │ │ │
│ │ │ │ │ │
│ │ │ Namespace UID → 主机 UID │ │ │
│ │ │ ────────────── ───────── │ │ │
│ │ │ 0 → 1000 │ │ │
│ │ │ 1 → 1001 │ │ │
│ │ │ 2 → 1002 │ │ │
│ │ │ ... → ... │ │ │
│ │ │ 65534 → 106534 │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 进程在 Namespace 内看到: │ │
│ │ $ id │ │
│ │ uid=0(root) gid=0(root) groups=0(root) │ │
│ │ │ │
│ │ 但实际上映射到主机的 UID 1000! │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 关键:Namespace 内的 root (uid=0) 映射到主机的 UID 1000 │
└─────────────────────────────────────────────────────────┘
为什么不需要 root 权限?
Linux 内核允许普通用户创建 User Namespace(自 Linux 3.8 起),原因:
- 安全性:Namespace 内的 root 权限被限制在 Namespace 内
- 隔离性:无法影响主机上的其他进程
-
映射机制:通过 UID/GID 映射表控制权限
┌─────────────────────────────────────────────────────────┐
│ 权限检查流程 │
│ │
│ 进程在 User Namespace 内执行操作: │
│ │
│ 1. 访问 Namespace 内的文件 │
│ ↓ │
│ 检查:Namespace 内的 UID (0) │
│ ✓ 允许(在 Namespace 内是 root) │
│ │
│ 2. 访问主机上的文件 │
│ ↓ │
│ 检查:映射后的主机 UID (1000) │
│ ✗ 拒绝(主机上不是 root) │
│ │
│ 3. 影响主机上的其他进程 │
│ ↓ │
│ 检查:映射后的主机 UID (1000) │
│ ✗ 拒绝(主机上不是 root) │
│ │
│ 结论:Namespace 内的 root 权限被限制在 Namespace 内! │
└─────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────┐
│ 1. 普通用户执行命令 │
│ │
│ unshare --user --map-root-user /bin/bash │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 2. 内核创建新的 User Namespace │ │ │ │ │ │ │ │ • 分配新的 Namespace ID │ │ │ │ • 创建新的进程(bash) │ │ │ │ • 设置初始 UID/GID 映射 │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ │ ↓ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 3. --map-root-user 自动设置映射表 │ │ │ │ │ │ │ │ /proc/self/uid_map: │ │ │ │ 0 <当前用户UID> 1 │ │ │ │ │ │ │ │ /proc/self/gid_map: │ │ │ │ 0 <当前用户GID> 1 │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ │ ↓ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 4. 进程在 Namespace 内运行 │ │ │ │ │ │ │ │ id │ │
│ │ uid=0(root) gid=0(root) │ │
│ │ │ │
│ │ 但实际权限检查时: │ │
│ │ • Namespace 内操作 → 使用 UID 0 │ │
│ │ • 主机操作 → 使用映射后的 UID 1000 │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 5. 权限检查机制 │ │
│ │ │ │
│ │ 访问 Namespace 内资源: │ │
│ │ ✓ 使用 Namespace UID (0) → 有 root 权限 │ │
│ │ │ │
│ │ 访问主机资源: │ │
│ │ ✗ 使用主机 UID (1000) → 只有普通用户权限 │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘