一、概述
Polkit(原 PolicyKit)是 Linux 系统中广泛使用的权限控制框架,其主要功能是为系统级服务提供细粒度的访问控制机制。与 sudo 不同,Polkit 不授予整个会话 root 权限,而是仅针对**单个动作(Action)**进行授权判断,实现了“最小权限原则”的模块化设计。该框架通过解耦特权操作与身份验证流程,让非特权进程能够安全地与特权进程通信。
Polkit 的核心设计哲学是:将“执行机制”(Mechanism)与“用户界面”(UI)分离。特权程序(如系统守护进程)只负责执行操作,而是否允许执行该操作的决策权交给 Polkit 这个独立的授权机构。
二、系统架构
Polkit 的架构由三个核心组件构成:
2.1 核心组件
graph TB
subgraph "用户空间"
A[Subject<br/>非特权进程<br/>如:图形应用]
B[Authentication Agent<br/>认证代理<br/>如:GNOME Polkit Agent]
end
subgraph "系统总线 D-Bus"
C[org.freedesktop.PolicyKit1<br/>D-Bus 服务接口]
end
subgraph "系统空间"
D[polkitd<br/>授权守护进程<br/>以 polkitd 用户运行]
E[Mechanism<br/>特权服务<br/>如:systemd-logind<br/>NetworkManager]
F[pkexec / pkcheck<br/>命令行工具]
end
A -->|请求特权操作| E
E -->|CheckAuthorization| C
C -->|转发请求| D
D -->|查询规则| G[(Action 策略<br/>*.policy XML)]
D -->|评估规则| H[(Authorization Rules<br/>*.rules JS)]
D -->|需要认证| B
B -->|用户输入密码| D
D -->|返回授权结果| E
E -->|执行/拒绝| A
A -->|直接调用| F
F -->|查询授权| C
2.2 组件详解
| 组件 | 角色 | 说明 |
|---|---|---|
| Subject | 主体 | 请求执行特权操作的非特权进程,通常是用户会话中的应用程序 |
| Mechanism | 机制 | 执行特权操作的系统守护进程,如 systemd-logind、udisks2、NetworkManager |
| polkitd | 授权机构 | 核心守护进程,以非特权用户 polkitd 身份运行,通过 D-Bus 接收授权查询并做出决策 |
| Authentication Agent | 认证代理 | 运行在用户会话中的图形或文本界面,负责向用户展示认证对话框并收集凭据 |
| pkexec | 执行工具 | 类似 sudo 的命令行工具,允许授权用户以另一个用户身份执行命令 |
三、执行原理与授权流程
3.1 标准授权流程
当用户尝试执行特权操作时,Polkit 的完整执行流程如下:
sequenceDiagram
participant U as 用户/应用 (Subject)
participant M as 特权服务 (Mechanism)
participant D as D-Bus 总线
participant P as polkitd
participant A as 认证代理
participant Auth as PAM/密码验证
U->>M: 请求特权操作<br/>(如:重启系统)
M->>D: 调用 CheckAuthorization
D->>P: 转发授权检查请求
P->>P: 解析 Action ID<br/>(如:org.freedesktop.login1.reboot)
alt 无需认证
P->>P: 匹配规则返回 YES
P-->>D: 授权通过
D-->>M: 允许执行
M->>U: 执行操作
else 需要认证
P->>P: 确定认证类型<br/>(auth_self / auth_admin)
P->>D: 请求启动认证代理
D->>A: 调用 BeginAuthentication
A->>U: 显示认证对话框
U->>A: 输入密码
A->>Auth: 通过 PAM 验证
Auth-->>A: 验证结果
A->>D: 发送 AuthenticationAgentResponse2
D->>P: 验证 Cookie 和结果
P-->>D: 认证成功/失败
D-->>M: 返回授权结果
opt 授权成功
M->>U: 执行特权操作
end
else 拒绝访问
P-->>D: 返回 NO
D-->>M: 拒绝执行
M->>U: 操作被拒绝
end
3.2 pkexec 的执行流程
pkexec 是 Polkit 提供的命令行工具,功能类似 sudo,但实现机制有本质差异:
flowchart TD
A[用户执行<br/>pkexec <command>] --> B{参数检查}
B -->|argc < 1| C[拒绝执行<br/>CVE-2021-4034 修复]
B -->|正常参数| D[解析目标程序路径]
D --> E{路径是否绝对?}
E -->|否| F[在 PATH 中搜索]
E -->|是| G[构造 D-Bus 请求]
F --> G
G --> H[调用 polkitd<br/>检查授权]
H --> I{授权结果?}
I -->|YES| J[以目标用户身份<br/>执行命令]
I -->|NO| K[退出码 127<br/>未授权]
I -->|需要认证| L[启动认证流程]
L --> M[用户取消?]
M -->|是| N[退出码 126<br/>用户取消]
M -->|否| O[验证密码]
O -->|成功| J
O -->|失败| K
与 sudo 的关键区别在于:pkexec 通过动态查询 polkitd 服务进行实时授权验证,而非依赖静态的 /etc/sudoers 配置文件。这使得 Polkit 能够实现更细粒度的、基于动作(Action)的访问控制策略。
3.3 临时授权缓存机制
Polkit 支持授权缓存,当使用 auth_self_keep 或 auth_admin_keep 时,成功认证后的授权会被保留一段时间(通常约 5 分钟)。在此期间,同一用户执行相同 Action 无需重复认证。
graph LR
A[首次认证] -->|auth_admin_keep| B[授权成功]
B --> C[缓存临时授权<br/>temporary_authorization_id]
C --> D[5分钟内再次请求<br/>相同 Action]
D --> E{缓存有效?}
E -->|是| F[直接授权<br/>无需密码]
E -->|否| G[重新认证]
可通过 pkcheck --list-temp 查看当前缓存的临时授权。
四、Action 动作定义
4.1 Action 概念
Action(动作)是 Polkit 授权模型的基本单位,代表一个受 Polkit 规则约束的单一活动。每个 Action 都有唯一的标识符,例如重启计算机的动作 ID 为 org.freedesktop.login1.reboot。
4.2 Action 配置文件
Actions 定义在 XML 格式的 .policy 文件中,位于 /usr/share/polkit-1/actions/。每个文件可定义一个或多个动作,包含人类可读的描述和默认授权设置。
重要提示:系统默认的 policy 文件不应直接编辑,应通过 .rules 文件覆盖。
4.3 Action 文件结构
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/software/polkit/policyconfig-1.dtd">
<policyconfig>
<vendor>GNOME</vendor>
<vendor_url>https://www.gnome.org</vendor_url>
<action id="org.gnome.gparted">
<description>Run GParted as root</description>
<description xml:lang="zh_CN">以 root 身份运行 GParted</description>
<message>Authentication is required to run GParted</message>
<message xml:lang="zh_CN">运行 GParted 需要身份验证</message>
<icon_name>gparted</icon_name>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/gparted</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>
4.4 隐式授权类型
每个 Action 的 <defaults> 标签定义了三种会话状态下的默认授权策略:
| 授权值 | 含义 | 适用场景 |
|---|---|---|
no |
拒绝授权 | 敏感操作,禁止普通用户执行 |
yes |
无需认证直接授权 | 低风险操作,如修改用户自己的密码 |
auth_self |
需要用户认证自己的密码 | 中等风险,用户需证明身份 |
auth_self_keep |
同上,但缓存授权约 5 分钟 | 频繁执行的同类操作 |
auth_admin |
需要管理员(root)密码 | 高风险系统操作 |
auth_admin_keep |
同上,但缓存授权约 5 分钟 | 管理员批量操作 |
三种会话状态:
allow_any:适用于所有上下文,包括 SSH/VNC 远程会话allow_active:适用于本地活动会话(当前正在使用的图形/文本控制台)allow_inactive:适用于本地非活动会话(如切换到其他虚拟终端后的原会话)
4.5 常用 Annotation 注解
Action 可通过 <annotate> 标签添加键值对元数据:
| 键名 | 用途 |
|---|---|
org.freedesktop.policykit.exec.path |
指定 pkexec 执行的目标程序完整路径 |
org.freedesktop.policykit.exec.argv1 |
限制 pkexec 调用时的第一个参数 |
org.freedesktop.policykit.exec.allow_gui |
布尔值,允许在图形环境中安全启动特权程序 |
org.freedesktop.policykit.imply |
定义元动作,授权此动作时同时授权列出的其他动作 ID |
org.freedesktop.policykit.owner |
指定可查询此动作授权状态的用户列表 |
五、Authorization Rules 授权规则
5.1 Rules 概述
Authorization Rules 是写在 JavaScript 中的 .rules 文件,位于两个目录:
/usr/share/polkit-1/rules.d/:系统软件包使用的规则/etc/polkit-1/rules.d/:本地管理员自定义规则(优先级更高)
规则文件包含比 Action 默认设置更复杂的逻辑,可以基于用户、组、会话状态、动作参数等条件动态决定授权结果。
5.2 规则评估机制
graph TD
A[收到授权请求] --> B[加载所有 .rules 文件]
B --> C[按文件名排序<br/>数字小的优先]
C --> D[依次执行 addRule 回调]
D --> E{返回结果?}
E -->|YES/NO/<br/>AUTH_*| F[终止评估<br/>返回结果]
E -->|null/undefined| G[继续下一个规则]
G --> H{还有规则?}
H -->|是| D
H -->|否| I[使用 Action 的<br/>默认隐式授权]
5.3 规则语法与对象
规则文件使用 JavaScript 语法,核心 API 包括:
// 添加授权规则
polkit.addRule(function(action, subject) {
// action 对象包含:
// - action.id: 动作标识符
// - action.lookup(key): 查询 annotation 值
// subject 对象包含:
// - subject.user: 用户名
// - subject.isInGroup("groupname"): 是否属于指定组
// - subject.local: 是否为本地会话
// - subject.active: 是否为活动会话
// 返回 polkit.Result 枚举值:
// - polkit.Result.YES
// - polkit.Result.NO
// - polkit.Result.AUTH_SELF
// - polkit.Result.AUTH_SELF_KEEP
// - polkit.Result.AUTH_ADMIN
// - polkit.Result.AUTH_ADMIN_KEEP
// - null (继续评估后续规则)
});
5.4 典型规则示例
示例 1:允许 wheel 组用户无需密码执行 NetworkManager 操作
polkit.addRule(function(action, subject) {
if (/^org\.freedesktop\.NetworkManager\./.test(action.id) &&
subject.local &&
subject.active &&
subject.isInGroup("wheel")) {
return polkit.Result.YES;
}
});
示例 2:允许特定用户免密设置时区
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.timedate1.set-timezone" &&
subject.user == "archie") {
return polkit.Result.YES;
}
});
示例 3:基于动作参数细粒度控制
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "hybrid.service" &&
subject.user == "michael") {
return polkit.Result.YES;
}
});
示例 4:调用外部程序辅助决策
polkit.addRule(function(action, subject) {
if (action.id.indexOf("org.freedesktop.login1.reboot") == 0) {
try {
// 外部程序返回 0 则授权
polkit.spawn(["/opt/company/bin/user-may-reboot", subject.user]);
return polkit.Result.YES;
} catch (error) {
return polkit.Result.AUTH_ADMIN;
}
}
});
5.5 Admin Rules 管理员规则
除了 addRule(),Polkit 还提供 addAdminRule() 用于定义哪些用户被视为管理员:
polkit.addAdminRule(function(action, subject) {
return ["unix-group:wheel"];
});
这决定了当需要 auth_admin 认证时,系统会提示输入哪个管理员账户的密码。
六、Polkit 与 Sudo 的对比
graph BT
subgraph "Sudo 模型"
A1[用户] -->|sudo cmd| B1[检查 /etc/sudoers<br/>静态配置]
B1 -->|授权| C1[以 root 执行<br/>整个命令]
end
subgraph "Polkit 模型"
A2[用户/应用] -->|请求 Action| B2[机制服务<br/>如 systemd-logind]
B2 -->|CheckAuthorization| C2[polkitd<br/>动态评估]
C2 -->|查询| D2[Action 策略<br/>& Rules 规则]
D2 -->|决策| C2
C2 -->|授权| E2[仅执行<br/>特定操作]
end
| 特性 | Sudo | Polkit |
|---|---|---|
| 控制粒度 | 基于命令和用户 | 基于动作(Action)和会话状态 |
| 配置方式 | 静态 /etc/sudoers |
动态 XML + JavaScript 规则 |
| 认证方式 | 密码验证 | 集成图形认证代理 + PAM |
| 桌面集成 | 弱(需终端) | 强(原生图形对话框) |
| 权限范围 | 授予整个命令的 root 权限 | 仅授权特定操作 |
| 缓存机制 | timestamp_timeout |
auth_*_keep(约 5 分钟) |
| 适用场景 | 命令行管理员操作 | 桌面应用和系统服务交互 |
七、安全考量与历史漏洞
Polkit 的设计虽然降低了传统 setuid 程序的安全风险,但其实现中的漏洞也曾引发严重安全问题:
7.1 CVE-2021-4034(PwnKit)
2022 年披露的 pkexec 本地提权漏洞,存在于 2009 年以来的所有版本。根本原因是 pkexec 在处理 argc=0 时未正确验证参数,导致越界读写环境变量,最终可注入 GCONV_PATH 执行任意代码获得 root 权限。
修复方案:在 pkcheck.c 和 pkexec.c 中添加 argc < 1 的检查。
7.2 CVE-2021-3560
另一个著名漏洞,利用 dbus-send 请求与 polkitd 查询 UID 之间的竞态条件,通过快速取消请求使 polkitd 无法获取正确的 UID,从而绕过授权检查。
八、总结
Polkit 作为现代 Linux 系统的核心授权框架,通过其独特的架构设计实现了:
- 解耦授权与执行:特权服务专注于业务逻辑,授权决策交给独立的
polkitd - 细粒度访问控制:基于 Action 的授权模型比传统 sudo 更精确
- 灵活的规则体系:JavaScript 规则文件支持复杂的动态授权逻辑
- 无缝的桌面集成:图形认证代理提供友好的用户体验
- 安全的进程隔离:
polkitd以非特权用户运行,通过 D-Bus 通信
理解 Polkit 的 Action 定义和 Rules 评估机制,是进行 Linux 系统权限管理和安全加固的重要基础。管理员应当避免直接修改系统 .policy 文件,而是通过 /etc/polkit-1/rules.d/ 中的自定义规则实现权限调整,确保系统升级时配置不会丢失。