# 功能开关

> Agent 如何决定自身暴露哪些功能 —— 完全在 Agent 主机上配置。

URL: https://docs.serverbee.app/zh/docs/capabilities

ServerBee 通过 Agent 能力（Capabilities）落实最小权限原则。**能力由 Agent 主机拥有**：每个 Agent 从自己的配置文件（以及可选的 CLI 参数）计算出自身的能力集合并上报给 Server。Server 可以读取并展示这些能力，但**无法**修改它们 —— 服务端没有任何开关。

<Callout type="info">
  这是一个有意为之的信任边界设计。运行 Agent 的那台机器，是唯一决定该 Agent 行为的地方。一个被攻陷或配置错误的 Server，无法在你的主机上悄悄打开远程终端、文件访问或命令执行。
</Callout>

## 功能列表 [#功能列表]

ServerBee 定义了 11 个功能位，分为两个风险等级，有效掩码为 `2047`（bits 0..=10）。

### 高风险（默认关闭） [#高风险默认关闭]

| 功能            | 键名         | 位值                 | 说明                      |
| ------------- | ---------- | ------------------ | ----------------------- |
| **Web 终端**    | `terminal` | `CAP_TERMINAL` (1) | 允许通过浏览器打开远程终端           |
| **远程执行**      | `exec`     | `CAP_EXEC` (2)     | 允许远程执行命令                |
| **文件管理**      | `file`     | `CAP_FILE` (64)    | 允许远程浏览、编辑、上传/下载文件       |
| **Docker 管理** | `docker`   | `CAP_DOCKER` (128) | 允许 Docker 容器监控、日志流和容器操作 |

<Callout type="warn">
  这些能力允许在目标服务器上执行任意代码或访问文件系统，因此默认关闭。只在受信任的主机上、通过编辑该主机的 Agent 配置来开启它们。
</Callout>

<Callout type="info">
  文件管理还需要 Agent 侧的额外配置（`root_paths`、`deny_patterns`）以实现路径沙箱安全。详见 [Agent 部署](/zh/docs/agent) 和 [配置](/zh/docs/configuration) 页面。
</Callout>

### 低风险（默认开启） [#低风险默认开启]

| 功能            | 键名                | 位值                          | 说明                                                                                                         |
| ------------- | ----------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------- |
| **自动升级**      | `upgrade`         | `CAP_UPGRADE` (4)           | 允许远程二进制升级                                                                                                  |
| **ICMP Ping** | `ping_icmp`       | `CAP_PING_ICMP` (8)         | 允许 ICMP 探测任务                                                                                               |
| **TCP 探测**    | `ping_tcp`        | `CAP_PING_TCP` (16)         | 允许 TCP 端口探测任务                                                                                              |
| **HTTP 探测**   | `ping_http`       | `CAP_PING_HTTP` (32)        | 允许 HTTP 探测任务                                                                                               |
| **安全事件**      | `security_events` | `CAP_SECURITY_EVENTS` (256) | 允许 Agent 上报 SSH 登录 / 暴力破解 / 端口扫描事件（见 [安全事件](/zh/docs/security-events)）                                     |
| **防火墙封禁**     | `firewall_block`  | `CAP_FIREWALL_BLOCK` (512)  | 允许 Agent 应用 Server 下发的 nftables 封禁列表。需要 root 或 `CAP_NET_ADMIN` 及主机上的 `nft` 命令。见 [防火墙封禁](/zh/docs/firewall) |
| **IP 质量**     | `ip_quality`      | `CAP_IP_QUALITY` (1024)     | 允许 Agent 运行服务解锁探测并上报结果；可选的元数据与风险评分由 Server 完成                                                              |

没有任何 `[capabilities]` 覆盖的 Agent 默认值为 `1852`（自动升级 + 三种 ping 探测 + 安全事件 + 防火墙封禁 + IP 质量），即高风险的终端、执行、文件和 Docker 能力保持关闭。

## 配置能力（在 Agent 主机上） [#配置能力在-agent-主机上]

Agent 按如下方式计算自身的能力位图：

1. 从内置默认集合开始（`CAP_DEFAULT = 1852`）。
2. 应用配置文件 `[capabilities]` 段 —— `allow` 增加位，`deny` 移除位。在这一层中 `deny` 优先于 `allow`。
3. 在其上应用 CLI `--allow-cap` / `--deny-cap` 参数，用于临时覆盖。CLI 层优先于配置层。
4. 与子系统可用性对账：`file` 仅在 `[file].enabled = true` 且至少配置一个 `root_path` 时保留；`firewall_block` 在启动时的 `nft` 探测失败（缺少二进制、内核支持或权限）时被剔除。上报的能力始终意味着 Agent 真正能提供该功能。

未知的能力键名在启动时会直接报错，因此拼写错误会快速失败，而不是悄悄丢掉某个能力。

### 配置文件 [#配置文件]

安装脚本管理的部署请在 Agent 主机上编辑 `/opt/serverbee/etc/agent.toml`。手工部署也可能使用 `/etc/serverbee/agent.toml` 或工作目录下的 `agent.toml`：

```toml
[capabilities]
# 开启该主机应暴露的高风险能力。
allow = ["terminal", "file"]
# 移除该主机不应暴露的默认能力。
deny = ["ip_quality"]
```

重启 Agent 使更改生效：

```bash
sudo serverbee restart agent
```

### 环境变量 [#环境变量]

同样的配置也可以通过环境变量提供（Figment，`SERVERBEE_` 前缀，`__` 表示嵌套）：

```bash
SERVERBEE_CAPABILITIES__ALLOW=["terminal","file"]
SERVERBEE_CAPABILITIES__DENY=["ip_quality"]
```

### CLI 参数 [#cli-参数]

如需一次性覆盖，可在启动 Agent 时传入可重复的参数：

```bash
serverbee-agent --allow-cap terminal --allow-cap file --deny-cap ip_quality
```

### 安装时 [#安装时]

安装脚本支持 `--caps`，在注册时为 Agent 的能力配置预置初值：

```bash
curl -fsSL https://raw.githubusercontent.com/ZingerLittleBee/ServerBee/main/deploy/install.sh | sudo sh -s -- agent \
  --server-url https://monitor.example.com \
  --enrollment-code YOUR_ONE_TIME_CODE \
  --caps terminal,file
```

交互式运行安装脚本时也会提示选择能力，默认项已预先勾选。

## 临时授予 [#临时授予]

编辑配置并重启 Agent 是**永久**开启某个能力的正确方式。而对于短时、临时性的需求 —— 比如「给这台机器 30 分钟的终端」—— Agent 自带一个主机本地 CLI，可以临时开启某个默认关闭的能力，并在时间窗结束时自动关回去。

<Callout type="info">
  临时授予是一种**主机本地**机制，与「能力由 Agent 拥有」的信任模型完全一致：只有在 Agent 主机上拥有 shell 的人才能发起。Server 以及 Web/iOS UI 始终只读，**无法**授予能力。Server 只是镜像 Agent 上报的内容、据此拦截控制面请求、审计变更，并可触发告警。
</Callout>

### CLI [#cli]

在 **Agent 主机**上运行这些子命令（通常需要 `sudo`，因为它们写入 `state_dir`，而守护进程一般以 agent 用户/root 运行）。它们共用守护进程的配置（安装脚本部署为 `/opt/serverbee/etc/agent.toml`），因此 grants 文件位置和最大时长都来自同一个 `[capabilities]` 段。

```bash
# 临时开启某个默认关闭的能力，限定一个时间窗。
serverbee-agent grant terminal --for 30m --reason "debugging a stuck deploy"
serverbee-agent grant file --for 2h
serverbee-agent grant docker --for 1d

# 提前撤销一个生效中的授予。
serverbee-agent revoke terminal

# 列出当前生效的授予（能力、剩余秒数、授予者、原因）。
serverbee-agent grants
```

* 传给 `--for` 的时长格式为 `<数字><单位>`，单位为 `s`（秒）、`m`（分钟）、`h`（小时）、`d`（天）之一 —— 例如 `90s`、`30m`、`2h`、`1d`。`--reason` 为可选的自由文本，会随授予一同记录。
* `grant` 只能**开启**当前**关闭**的能力。对在 `agent.toml` 中已启用的能力执行授予会被拒绝（`'terminal' is already enabled in agent.toml; nothing to grant`）。
* 超过 `temporary_max_duration`（默认 `24h`，见 [配置](/zh/docs/configuration)）的时长会被拒绝。
* CLI 是一次性的：写完 grants 文件即退出。运行中的守护进程会在数秒内拾取该变更 —— 无需重启 Agent。

### 重启与过期语义 [#重启与过期语义]

授予会持久化到 `<state_dir>/capability_grants.json`（默认 `/var/lib/serverbee/capability_grants.json`，以 `0600` 权限写入），其 `expires_at` 是**绝对**时间戳（Unix epoch），而非相对倒计时。这让行为足够健壮：

* **跨重启存活** —— 若 Agent 在时间窗中重启，授予会被重新加载，并在原始时间窗内保持生效。
* **在原始截止时刻过期** —— 重启不会延长授予。`30m` 的授予在发起后 30 分钟过期，无论期间 Agent 重启多少次。
* **失败时安全归零（OFF）** —— 若 grants 文件缺失、为空、损坏，或由未知 schema 版本写入，Agent 会将其视作「无授予」，对应能力保持关闭。临时授予永远不会*增强*永久能力集，它只能在自己的时间窗内把一个原本关闭的位翻为开启。
* **过期约束的是运行中的工作，而不只是新请求** —— `terminal` 授予过期或被撤销时，在该授予下打开的终端会话会被立即关闭（浏览器端会看到会话结束）；`security_events` 授予生效的那一刻就会启动 Agent 的安全事件管道，时间窗结束时将其停止 —— 包括在 Agent 已运行期间发起的授予。

### Server 看到什么 [#server-看到什么]

当某个授予生效（或过期 / 被撤销）时，Agent 会重新上报其有效能力集，因此 Server 会**实时**打开或关闭对应的门控：

* 变更会镜像到 `servers.capabilities` 并广播给浏览器，UI 实时更新。
* 该转换会写入 [审计日志](/zh/docs/admin)，动作为 `capability_temporarily_granted`、`capability_grant_expired` 或 `capability_grant_revoked`。
* 对**高危**能力（`terminal`、`exec`、`file`、`docker`）的临时授予还会评估事件驱动的 `capability_grant_detected` [告警规则](/zh/docs/alerts)。过期、撤销以及低风险授予会被审计，但不触发告警。
* Agent 为该服务器执行的全部工作会按新的能力集重新同步：ping 任务重新过滤、网络探测目标与 IP 质量服务重新下发、防火墙黑名单先重置（仅当 `firewall_block` 仍生效时才会重新推送）。撤销 ping 能力会立即停止对应探测；撤销 `firewall_block` 会立刻移除 ServerBee 在主机上的 nftables 规则，无需等待重连。

<Callout type="warn">
  在 Agent 与 Server **断连**期间发起的授予，会在本地生效并在重连后展示出来，但**不会**产生 `granted` 审计记录或告警。Server 只能看到重连后的能力状态，看不到转换发生的瞬间，因此无法区分「刚刚授予」与「本就生效」的能力。若你把该告警当作触发器（tripwire）使用，请考虑这一点。
</Callout>

### Web UI 中的展示 [#web-ui-中的展示]

在临时授予生效期间，受影响的能力会带一个琥珀色的 **Temporary** 徽章和一个到期实时倒计时，**服务器详情 → 能力** 弹窗与 **设置 → 能力开关** 全机群矩阵中均会显示。授予过期或被撤销后，徽章会自动消失。

## 强制执行模型 [#强制执行模型]

Agent 主机是唯一的权威来源，但为了纵深防御，强制执行发生在两个边界上。

### 服务端拦截（基于上报的能力） [#服务端拦截基于上报的能力]

Server 记录每个 Agent 上报的能力，并拒绝 Agent 未启用能力对应的控制面请求：

* **终端**：WebSocket 升级被 403 拒绝
* **执行**：`POST /api/tasks` 与定时任务运行会过滤掉 Agent 缺少 `exec` 的服务器，并写入合成结果（`exit_code = -2`）
* **自动升级**：未上报 `upgrade` 时，`POST /api/servers/{id}/upgrade` 返回 403
* **Ping 与 Traceroute**：探测任务按能力过滤；traceroute 需要 `ping_icmp`
* **文件管理**：未上报 `file` 时，文件接口在下发前即拒绝请求
* **Docker**：Docker 读取/操作接口及 Docker 日志 WebSocket 路由需要 `docker` 以及 Agent 运行时支持 Docker

由于能力由 Agent 拥有，拒绝原因始终是 `agent_capability_disabled`。

### Agent 侧强制执行 [#agent-侧强制执行]

即使某个服务端请求被绕过，Agent 仍会在本地复核能力：

* 对未授权命令返回 `CapabilityDenied` 消息
* Server 收到 `CapabilityDenied` 后写入合成结果（`exit_code = -1`）
* 拒绝事件记录到审计日志

### 上报与展示 [#上报与展示]

Agent 连接（或重连）时会发送携带 `agent_local_capabilities` 的 `SystemInfo`。Server 会：

1. 将该值持久化到 `servers.capabilities` 列作为展示镜像（这样即使 Agent 离线，仪表盘也能展示能力）。
2. 向所有已连接的浏览器广播 `CapabilitiesChanged`，使 UI 实时反映当前集合。

如果重连之间 ping 相关位发生变化，Server 会自动为该 Agent 重新同步 ping 任务。

## 前端行为 [#前端行为]

Web 和 iOS 客户端以**只读**方式展示能力：

* **服务器详情 → 能力**：只读展示该 Agent 已启用的能力，并提示它们在 Agent 配置文件中设置
* **设置 → 能力开关**：只读的全机群矩阵，按服务器展示启用/关闭状态
* **远程命令页**：Agent 缺少 `exec` 的服务器置灰
* **终端按钮**：对 Agent 缺少 `terminal` 的服务器隐藏
* **文件按钮**：对 Agent 缺少 `file` 的服务器隐藏
* **Docker 入口**：对 Agent 缺少 `docker` 的服务器隐藏

## 运行时能力字段 [#运行时能力字段]

Server 响应暴露三个能力字段，在能力由 Agent 拥有的模型下它们都相等：

* `capabilities`：Agent 上次上报值的持久化镜像
* `agent_local_capabilities`：已连接 Agent 上报的实时值
* `effective_capabilities`：实际强制执行的值（与 agent-local 值一致）

这些字段在 API 中保持区分，以便向前兼容，并使离线服务器仍能从镜像展示最后已知的能力。

<Cards>
  <Card title="Web 终端" href="/zh/docs/terminal" />

  <Card title="文件管理" href="/zh/docs/file-manager" />

  <Card title="Ping 监控" href="/zh/docs/ping" />

  <Card title="管理员指南" href="/zh/docs/admin" />
</Cards>
