> 原始 Markdown 孪生体（构建期从源 Markdown 生成）。渲染页：https://docs.gatellm.io/zh-CN/reference/audit-and-security-config · 文档索引：https://docs.gatellm.io/zh-CN/llms.txt


# 审计与安全配置

本章讲配置变更的审计记录，以及控制台的安全设置。审计日志有控制台页面（仅 admin）；个别安全能力（如手动 IP 封禁）有意不做界面，属设计取舍，本章如实说明。

## 审计日志

审计日志记录控制台里的**配置变更**操作（增/删/改）、登录/登出与密码修改等安全事件，与请求日志（[日志与日志体存储](/zh-CN/reference/logs-and-body-storage.md)，记录每次 API 调用）互补。

**查看方式**：管理员登录后，进入 **Settings → Audit**（仅 admin 可见）。页面支持按操作者（子串匹配）、操作类型、实体类型、时间范围过滤，按时间倒序分页浏览；点击任意行可查看变更详情（diff 与元数据）。对应 API 是 `GET /console/api/audit-logs`（仅 admin），见 Console API 文档的「审计日志」一节。读取审计日志本身不会产生审计记录。

每条审计记录的字段：

| 字段 | 说明 |
|------|------|
| actor | 发起主体：控制台用户名；`secret_key`（密钥 bearer 直调）；`api_key:<掩码>`（某 access key 持有者发起，如 MCP 工具调用、上下文刷新） |
| action | 配置实体为 `create` / `update` / `delete` 等 CRUD 动作；另有 `rotate`（access key 凭证轮换）/ `batch_update` / `batch_commit` / `import`（SSO 凭据）；安全事件为 `login.success` / `login.failure` / `logout` / `change_password` / `admin_change_password` / `sessions.revoke_all`；有主体的 MCP 事件为 `tool.call` / `acl.violation` / `mcp.context.refreshed` |
| entity_type | `access_key` / `upstream` / `model` / `model_pricing` / `mcp_server` / `console_user` / `console_session` / `load_balancer` / `lb_static_binding` / `lb_static_binding_pin` / `sso_credential` / `search_provider`（搜索引擎）/ `search_config`（搜索配置）/ `batch`；MCP 工具调用与 ACL 违规为 `mcp`，客户端发起的 MCP 会话刷新为 `mcp_context` |
| entity_id | 受影响实体标识 |
| diff | 变更差异（update / rotate 操作），超长截断；凭证字段（`api_key` / `api_keys` / `auth_header`）只记录掩码，不落明文 |
| ip_address | 操作者 IP |
| request_id | `z-request-id` 头（若有） |
| occurred_at | 事件发生时间（UTC） |

审计只记录**有发起主体**的事件。无主体的系统自驱行为（MCP 服务器启动注册、会话淘汰）不进审计表，走运行日志——审计回答"谁对什么负责"，系统自述归运维侧。

写入是「即发即忘」：审计写入失败不影响主操作，仅告警。MCP 工具调用与 ACL 违规也记录到审计日志。审计行在 **SQLite 与 PostgreSQL 两种存储模式下都会持久化**（`STORAGE_MODE` 只有这两个合法值，不存在可配置的 memory 模式）。注意这与分布式状态后端（`REDIS_URL` 是否配置决定的 Redis / 内存后端，管会话、限流、广播）是两个正交维度——审计持久化与否只取决于持久化存储后端，与 Redis 无关。

> 说明：审计日志定位为运维元数据，不等同完整审计系统。生产级合规审计如需更强保证，建议配合外部日志采集。

## IP 封禁

控制台登录防爆破由 IP 封禁自动处理：

- 连续登录失败达 `CONSOLE_MAX_FAILURES`（默认 20）→ 自动临时封禁该 IP `CONSOLE_BAN_DURATION`（默认 300 秒）。
- 封禁存于内存（重启丢失）与 Redis（多实例持久）。

**没有手动封禁/解封的入口**。这是有意的：手动 IP 管理属于 WAF（Web 应用防火墙）的职责，网关不做。如需手动 IP 控制，在网关前部署 WAF 或反代。

## 限流 {#rate-limit}

三层限流：

| 层级 | 配置 | 默认 |
|------|------|------|
| 每访问密钥 | 密钥或所属组的 `rate_limit`（`rpm` / `tpm`），另有全局默认（`/settings/rate-limit`） | 未设（不限） |
| 每 IP | `IP_RATE_LIMIT_RPM` | 禁用 |
| 每上游/每模型 | `rpm` / `max_concurrency` / `tpm` / `tps_min_interval_ms` / `wait_timeout_secs`（默认 10s）/ `initial_tokens`（0–100，默认 100）（控制台数据库侧） | 按需配 |

只读快照：`GET /console/api/rate-limit-status` 返回各层当前令牌/并发占用。每密钥限流在控制台配置（三层 RPM/TPM——见 ADR-026），每上游/每模型限流也在控制台配置，无独立写 API。

调用方遇限流时会收到 HTTP `429 Too Many Requests`。每访问密钥、每上游/每模型限流会在响应里带 `Retry-After` **响应头**（单位秒，表示距下次可用还需等待的时间）；每访问密钥还会带 `z-rate-limited: access_key:<rpm|tpm>:<scope>` 标签头。每 IP 限流返回的 429 不带此头。429 的来源与区分见 [错误码](/zh-CN/reference/error-codes.md)。

## 访问密钥掩码

列表里密钥只显示前后 4 位（如 `sk-a***xxxx`）。完整值只在「复制」时按需取出（`GET /access-keys/{name}/api-key`），匿名密钥不支持取完整值。上游的 API key 在控制台永不暴露。

## 密码策略

- bcrypt 哈希存储。
- 新建/重置/改密均要求 ≥8 字符。
- 禁用控制台用户 → 该用户现有会话立即吊销。

## 安全响应头

控制台路由统一带：

| 头 | 值 |
|----|-----|
| `X-Content-Type-Options` | `nosniff` |
| `X-Frame-Options` | `DENY` |
| `Referrer-Policy` | （策略值） |
| `Content-Security-Policy` | （策略值） |

## 常见问题

**Q：在控制台找不到审计日志页？**
Audit 页在 **Settings → Audit**，仅 admin 可见。如果你用 monitor 登录，侧边栏不会显示该入口（设计如此，非 bug）。

**Q：怎么手动封禁某个恶意 IP？**
做不到，网关不提供手动封禁。IP 封禁是自动的（失败次数触发）。手动 IP 控制用 WAF/反代。

**Q：登录被自己锁了怎么办？**
等封禁超时（默认 300 秒）自动解。或让管理员调整 `CONSOLE_MAX_FAILURES`/`CONSOLE_BAN_DURATION`（需重启容器）。

**Q：访问密钥列表的掩码能关掉看完整值吗？**
不能，掩码是强制的。要取完整值用该行的「复制」按钮（调接口取一次）。

**下一步**：[配置 Header ACL 访问规则](/zh-CN/howto/configure-header-acl.md) 看准入控制；[日志与日志体存储](/zh-CN/reference/logs-and-body-storage.md) 看请求日志；[访问密钥与密钥组字段](/zh-CN/reference/access-keys-groups-fields.md) 看密钥管理；[环境变量配置参考](/zh-CN/reference/configuration.md) 看全部环境变量。
