# 安全

kstack 的目标之一是帮助你的 AI 代理以比通常更安全的方式执行 Kubernetes 监控、故障排除和审计任务。kstack 通过设置安全防护栏来实现这一点：告诉代理哪些输入应视为不可信数据，以及哪些后续操作在执行前需要你的确认。本页描述信任边界、kstack 为将不可信数据隔离在代理上下文之外所采取的措施，以及故意跨越该边界的两个技能（`/exec`、`/logs`）。

---

## 信任边界

使用 AI 代理与集群交互时，每次交互涉及三方：

- **你** — 提供提示、`kubeconfig` 以及对集群的操作权限。你是唯一能够授权破坏性或危险操作的一方。
- **代理** — 解释你的提示，决定调用哪个技能，并读取技能返回的输出。
- **集群** — 通过 Kubernetes API 及相关工具返回数据。该数据**不受**信任。在最坏的情况下，Pod 名称、日志行、注解和事件消息均可被攻击者控制，代理若不加甄别地读取这些内容，可能被其操控。

kstack 的职责是保持代理的有效性，同时防止不可信的集群数据驱动代理行为。

---

## kstack 为你做了什么

### 1. 明确定义的响应信封

kstack 技能内部使用的每个脚本都向 stdout 写入恰好一个 JSON 对象（"响应信封"）。该对象的 schema 具有版本控制，并依据[预定义格式](https://github.com/kubetail-org/kstack/blob/main/src/schemas/response.schema.json)进行验证：

```jsonc
{
  "kstack":        "1",
  "status":        "ok" | "error",
  "render":        "verbatim" | "agent",   // ok only
  "content":       string,                 // ok only; JSON-escaped
  "agent_context": string,                 // ok only; optional side-channel
  "kind":          "user" | "infra",       // error only
  "message":       string,                 // error only
  "kube_context":  string,                 // optional; pinned cluster
  "notice":        string                  // optional; operator banner
}
```

该契约中与安全相关的属性：

<dl>
  <dt>`render`</dt>
  <dd>枚举值（`verbatim` 或 `agent`），告知代理 `content` 的预期消费方式。`render: verbatim` 指示代理原样打印 `content` 并结束本轮，不进行重新格式化或后续推理。`render: agent` 将 `content` 标记为代理可推理的工具输出。</dd>

  <dt>`content`</dt>
  <dd>JSON 转义字符串字段，包含发送给代理的有效载荷。</dd>

  <dt>`agent_context`</dt>
  <dd>JSON 转义字符串字段，脚本可用其提供代理在后续操作中所需的上下文数据（如缓存路径、计数、已解析标识符）。该字段由代理读取但不向用户展示，从而保持 `content` 在 `render: verbatim` 场景下的整洁，并防止元数据泄漏到终端。</dd>

  <dt>`message`</dt>
  <dd>JSON 转义字符串字段，携带脚本生成的类型化错误。</dd>
</dl>

非零退出码保留给意外崩溃使用，此时代理被指示打印 stderr 并停止，而非猜测意图。

### 2. 批量集群数据保留在磁盘并通过 `jq` 读取

没有 kstack 时，AI 代理会将每个 Pod、节点和事件全部流入上下文，既消耗上下文资源，又依赖模型在受攻击者影响的文本块中区分字段与指令。kstack 通过将响应写入每个上下文对应的 `cache_dir` 来规避这一问题，并将缓存路径传给代理，后续问题可通过对缓存 JSON 执行 `jq` 来回答。

这种方式有两个优点：

- **结构化解析而非散文解释。** `jq '.items[].metadata.name'` 遍历已知 schema 并返回字符串值。Pod 注解中的提示注入载荷在已知 JSON 路径下仍是字符串值，不会成为要求模型理解为指令的文本。

- **上下文增长可控。** 完整的逐 Pod/逐节点数据表永远不会进入模型的上下文窗口。kstack 技能提供的确定性摘要无论集群规模多大都只有几百个 token，后续轮次仅拉取问题所需的特定字段。

### 3. 破坏性或敏感操作需要你的确认

kstack 技能执行的集群操作本身是只读且安全的，但你可能要求代理执行改变集群状态或代表你请求特权信息的后续任务。为防止代理未经许可擅自执行这些操作，每个技能都向代理设定了明确边界，并指示代理在执行任何改变集群状态（删除资源、修改 `ConfigMaps`）或暴露凭据（读取 `Secrets`）的操作前请求确认。

### 4. 集群上下文按会话固定

会话中的第一个 kstack 技能解析并返回 `kube_context`。后续技能被告知将 `--context=<value>` 传入每个 kubectl/kubetail 调用，直到你明确切换到其他集群。这防止你因误操作将针对一个集群的操作施加到另一个集群上。

### 5. 遵守 RBAC

kstack 以 shell 脚本和 `SKILL.md` 文件的形式安装在你的主目录下。每个 Kubernetes 调用都通过你本地的 `kubeconfig`，使用你为每个上下文配置的 `user` 已有的凭据和 RBAC 绑定。

---

## 可将实时集群数据流入代理的技能

kstack 包含两个技能 — `/exec` 和 `/logs` — 它们运行在你和代理均可交互的 tmux 窗格内。这两个技能是你在运行前需要认真考虑的，因为集群数据最容易通过窗格进入代理的推理上下文。

### 基于窗格的技能的信任模型

默认情况下，代理可以操控 tmux 会话（输入命令、限定查询范围），但除非你明确指示（如"解析最后一组日志中的错误"），否则不会读取窗格内容。这样设计是为了防止不可信数据意外进入代理的推理上下文。你可以通过两个标志控制此行为：

- **`--trust-pane`** — 代理每轮都会读取窗格内容，可能进行摘要、关联分析或将内容发送到模型 API。当你明确希望代理对当前发生的事情进行推理时使用（如"监视这个并告诉我崩溃循环何时结束"）。
- **`--detach`** — 代理完全不附加到窗格，既不能读取也不能输入。你手动连接，模型无法访问会话内容。这是默认行为的结构性对应 — 由 kstack 强制执行，而非依赖代理自觉遵守。

注意，窗格读取行为是提示层面的契约。

### `/exec`

`/exec` 技能在 Pod、临时调试容器或节点上的特权 Pod 中打开交互式 shell。

需要注意的事项：

- **节点和调试容器模式具有特权。** 节点模式创建一个带有 `hostPID`、`hostNetwork` 以及挂载在 `/host` 的宿主文件系统的短期 Pod。调试容器模式加入目标 Pod 的进程命名空间。两者授予的访问权限都远超普通 `exec`。代理会在打开 shell 前描述已解析的目标和模式，供你确认后再继续。
- **会话持续运行直到你明确关闭。** 代理不会自行删除特权节点 Pod，需等待你的指令。

### `/logs`

`/logs` 技能对集群执行 Kubetail 查询，并将匹配的日志行流入窗格。

需要注意的事项：

- **日志经常包含敏感数据。** `Authorization` 请求头中的 token、包含 PII 的请求体、包含密钥的堆栈跟踪均属常见。无论是否设置了 `--trust-pane`，都应将查询范围限定得尽量窄：指定具体工作负载、短时间窗口、有针对性的 grep 模式。
- **Kubetail 的节点端 grep 有所帮助。** 将过滤器下推到集群意味着穿越网络的行数更少，到达窗格的行数也更少（使用 `--trust-pane` 时代理读取的行数同样更少）。过滤条件要具体，而不是大范围拉取后在本地过滤。

---

## 安全测试

kstack 的测试策略在有意义的最低层对上述每项保护进行验证，确保回归表现为测试失败而非现场意外。项目共有四个测试层 — [`tests/unit`](https://github.com/kubetail-org/kstack/tree/main/tests/unit)、[`tests/integration`](https://github.com/kubetail-org/kstack/tree/main/tests/integration)、[`tests/e2e`](https://github.com/kubetail-org/kstack/tree/main/tests/e2e) 和 [`tests/evals`](https://github.com/kubetail-org/kstack/tree/main/tests/evals) — 安全覆盖分布如下：

- **单元测试（`bats`）。** [`src/lib/response.sh`](https://github.com/kubetail-org/kstack/blob/main/src/lib/response.sh) 中的转义例程和信封构建器直接被测试。引号、反斜杠、换行符、控制字符和混合二进制形态的载荷通过 `response::_escape` 和 `response::ok_*` 处理，每个输出的信封都通过 `jq` 往返验证，确保其为合法 JSON。注入形态的字符串 — 包含 `","` 的 Pod 名称、包含 `\n"render":"agent"` 的注解等 — 被检查以确认它们作为 `content` 值落地，而不引入兄弟键。`--detach` 的标志解析经过单元测试，以确保拼写错误或重构不会静默禁用它。
- **集成测试（`bats`）。** 每个技能通过 `PATH` 上的模拟 `kubectl` 端到端调用，其输出信封依据 [`src/schemas/response.schema.json`](https://github.com/kubetail-org/kstack/blob/main/src/schemas/response.schema.json) 中的 JSON schema 进行验证。`kube_context` 固定通过运行多技能流程并断言每个下游调用携带 `--context=<resolved>` 来验证。
- **端到端测试（kind 支持）。** 通过 `kind` 搭建真实集群并运行技能。这些测试确认在真实 Kubernetes API Server 下结构性保护仍然有效：RBAC 拒绝产生类型化的 `error` 信封而非泄漏 stderr，`/exec` 上的特权模式（节点、调试容器）在任何确认路径之前解析为预期的 Pod spec。
- **评估测试（Claude 支持）。** 提示层面的契约需要代理参与的测试，因此放在这里。场景包括：在 Pod 注解、日志行和事件消息中植入提示注入载荷，确认代理将其视为数据而非指令；尝试让代理在未经确认的情况下改变集群状态或读取 `Secret`；尝试诱使代理以不同命令重试类型化错误；以及窗格测试，确认在没有 `--trust-pane` 时默认的不读取行为成立。评估是概率性的，因此评分标准记录通过率而非二元结果，并保留产物供事后审查。

`shellcheck` lint 在每个 PR 上运行，以捕获可能破坏结构性保证的 shell 错误类别（未加引号的展开、对不可信输入的 `eval`）。单元测试和集成测试层在每个 PR 上于 Linux、macOS 和 Windows 的 `amd64` 和 `arm64` 上运行；端到端测试层在 Linux 上运行；评估层按需从 CI 触发。

如果你发现上述部分有保证但没有对应测试，这是值得报告的缺陷。

---

如果你发现安全问题，请通过 [GitHub 仓库](https://github.com/kubetail-org/kstack)报告。