# Manticore Search 生产环境身份验证上线清单

一份在 Manticore Search 中启用身份验证的实用清单：盘点客户端、创建最小权限用户、在 staging 中测试、切换上线，并安全地排查问题。

在生产环境里，“打开就完事”几乎从来行不通。从技术上看只有四步：`auth`、重启、管理员、更新客户端。实际操作时，它几乎总会破坏某个不显眼的东西：夜间 worker、操作员手动脚本，或者半年没人碰过的 BI 仪表盘。

所以不要把它当成一次配置修改，而要当成一个小版本发布：先盘点客户端，再测试，最后切换。这样你会提前看到哪些地方会被拒绝，而不是在事故模式下临时修所有东西。

这份清单面向计划启用身份验证、并希望尽量安全地保护当前系统的用户。请记住，在你配置 `auth` 之前，身份验证一直处于禁用状态；切换之后，仍然不带凭据的客户端会被拒绝。

## 阶段 1：在做任何更改之前先盘点

先把每一个与 Manticore Search 通信的客户端——一个应用，或应用中相对独立的一部分——都列出来。这一步要在动配置之前完成。

通常需要检查的内容：

- 应用的搜索前端
- 数据写入 worker
- cron 任务
- 仪表盘和 BI 工具
- 支持或管理工具
- 表结构变更/更新脚本
- 备份和维护脚本
- 手动运行的本地脚本

对每个客户端，大致记录这些信息：

| 客户端 | 协议 | 表或集群 | 所需操作 | 备注 |
|---|---|---|---|---|
| 商品搜索应用 | HTTP | `products` | `read` | 将使用 Bearer Token |
| 目录数据写入 worker | SQL | `products` | `write` | 使用密码身份验证 |
| 表结构迁移任务 | SQL | `products` | `schema` | 仅在部署期间运行 |
| 访问管理员 | SQL | `*` | `admin` | 管理用户和权限 |

然后确认部署的基本情况：

- 你运行的是 RT 模式还是 plain 模式？
- 在 plain 模式下，身份验证文件应当放在哪里？
- 配置文件里设置了 `pid_file` 吗？初始化需要它。
- 当凭据跨网络传输时，SQL 客户端会使用 SSL、HTTP 客户端会使用 HTTPS 吗？
- 凭据（包括临时 Bearer Token）将在哪里安全保存？
- 谁有权查看第一个管理员的密码？

不要跳过最后两项。`CREATE USER` 会返回明文 Bearer Token；`TOKEN` 会为指定用户生成新 Token。之后用 `SHOW TOKEN` 查看时，显示的是存储的 Token 哈希，而不是明文。如果明文 Token 丢失，用 `TOKEN` 轮换它并更新应用中的 Token。

## 阶段 2：设置最小权限用户

围绕任务来创建用户，而不是靠猜“以后或许用得上”。

使用一个像这样的小矩阵：

| 工作负载 | 用户 | 权限 |
|---|---|---|
| 搜索前端 | `app_read` | `GRANT read ON 'products' TO 'app_read'` |
| 数据写入 worker | `app_ingest` | `GRANT write ON 'products' TO 'app_ingest'` |
| 表结构迁移任务 | `schema_job` | `GRANT schema ON 'products' TO 'schema_job'` |
| 身份验证操作员 | `security_admin` | `GRANT admin ON * TO 'security_admin'` |

请记住，`admin` 是一个范围很窄的权限。它只允许管理身份验证与授权状态，并不意味着同时拥有 `read`、`write`、`schema` 或 `replication`。

这一点很重要，因为管理凭据的人未必需要读取业务数据。搜索商品的服务不需要写入文档。迁移任务也不需要管理用户。

现在就把拒绝测试一并规划好。对要创建的每个用户，至少挑一件它应该能做的事，和一件它不应该能做的事。

例如：

- `app_read` 可以搜索 `products`。
- `app_read` 不能向 `products` 插入数据。
- `app_ingest` 可以写入 `products`。
- `app_ingest` 不能管理身份验证。
- `schema_job` 可以修改 `products` 的表结构。
- `schema_job` 在你授予 `read` 之前不能读取表。

## 阶段 3：在 staging 中测试

使用 staging 环境，提前演练计划在生产环境中执行的操作顺序。

在 RT 模式下：

```ini
searchd {
    data_dir = /var/lib/manticore
    auth = 1
    auth_log_level = info
    ...
}
```

在 RT 模式下显式关闭身份验证：

```ini
searchd {
    data_dir = /var/lib/manticore
    auth = 0
    ...
}
```

在 plain 模式下：

```ini
searchd {
    auth = /var/lib/manticore/auth.json
    auth_log_level = info
    ...
}
```

身份验证文件要保持私有。在首次初始化之前，Manticore Search 可能会创建一个空的身份验证文件。初始化之后，该文件会存储身份验证数据和凭据哈希。

启动 `searchd`，然后初始化第一个管理员：

```bash
searchd --config /etc/manticoresearch/manticore.conf --auth
```

用于脚本化设置：
```bash
printf 'admin\nStrongPass#2026\nStrongPass#2026\n' | \
  searchd --config /etc/manticoresearch/manticore.conf --auth-non-interactive
```
⚠️ 警告：这种情况下，密码会以明文形式通过命令行传递，可能进入 shell 历史记录和进程列表。只应在受控环境中使用。

这条命令会创建第一个管理员，并把所有操作授予该用户。它不会返回 Bearer Token。如果该管理员需要 HTTP Bearer 访问，请以管理员身份连接并运行 `TOKEN`，或使用 HTTP `POST /token` 端点。

接下来，根据前面阶段的记录创建 staging 用户。例如：

```sql
CREATE USER 'app_read' IDENTIFIED BY 'ReadPass#2026';
GRANT read ON 'products' TO 'app_read';

CREATE USER 'app_ingest' IDENTIFIED BY 'IngestPass#2026';
GRANT write ON 'products' TO 'app_ingest';

CREATE USER 'schema_job' IDENTIFIED BY 'SchemaPass#2026';
GRANT schema ON 'products' TO 'schema_job';

CREATE USER 'security_admin' IDENTIFIED BY 'AdminPass#2026';
GRANT admin ON * TO 'security_admin';
```

把返回的 Bearer Token 保存到安全存储中。请记住：明文 Token 不能留在日志、shell 历史记录或未受保护的文件中。如果需要轮换其中某一个，请使用：

```sql
TOKEN 'app_read';
```

`SHOW TOKEN` 不能用于找回明文 Token：

```sql
SHOW TOKEN FOR 'app_read';
```

查看用户和权限：
```sql
SHOW USERS;
SHOW PERMISSIONS;
SHOW PERMISSIONS FOR 'app_read';
```

为每个用户运行一次允许测试和一次拒绝测试。对只读用户：
```bash
curl -H "Authorization: Bearer *** \
  http://127.0.0.1:9308/sql?mode=raw \
  -d "SELECT * FROM products LIMIT 10"
```

然后测试权限不足的情况：
```bash
curl -H "Authorization: Bearer *** \
  http://127.0.0.1:9308/sql?mode=raw \
  -d "INSERT INTO products(id,title) VALUES(1,'test')"
```

对于这个 HTTP 检查，预期结果是 `403 Forbidden`。通过 SQL/MySQL 时，权限不足会返回 `ERROR 1045`，并附带权限被拒绝的报错信息。

SQL 客户端应当用 Manticore Search 的用户名和密码连接。Manticore 的 SQL/MySQL 协议支持 `mysql_native_password`。

```bash
MYSQL_PWD=ReadPass#2026 \
  mysql -h127.0.0.1 -P9306 -uapp_read \
  -e "SELECT * FROM products LIMIT 10"
```

HTTP 客户端可以使用 Basic 认证或 Bearer Token：

```bash
curl -u app_read:ReadPass#2026 \
  http://127.0.0.1:9308/sql?mode=raw \
  -d "SELECT * FROM products LIMIT 10"
```

HTTP 身份验证方案（`Basic`、`Bearer`）不区分大小写；用户名区分大小写。

如果你在维护期间绕过守护进程直接编辑了身份验证文件，请重新加载它：

```sql
RELOAD AUTH;
```

## 阶段 4：生产上线清单

把它当作切换上线时的清单。你甚至可以打印出来逐项打勾。

- [ ] 确认你有一份当前的配置备份。
- [ ] 确认现有的网络防护仍然到位。
- [ ] 确认 `pid_file` 已在配置文件中设置。
- [ ] 确认身份验证文件将在哪里创建或从哪里加载。
- [ ] 确认密码和 Token 的存放位置已经就绪。
- [ ] 在计划好的上线窗口内启用身份验证。
- [ ] 启用身份验证后，未带凭据的客户端会失败——这是预期行为。
- [ ] 初始化第一个管理员。
- [ ] 创建生产环境的用户和权限。
- [ ] Token 一经签发，立即保存到受保护的 secrets 存储中。不要把明文 Token 保存在文件、shell 历史记录或日志里。
- [ ] 更新 SQL 连接代码，使其发送用户名和密码。
- [ ] 更新 HTTP 连接代码，使其使用 Basic 认证或 Bearer Token。
- [ ] 把 staging 里的允许和拒绝测试再跑一遍。
- [ ] 检查身份验证日志。
- [ ] 对应用做一轮压力测试，覆盖搜索、数据写入、仪表盘和维护脚本。
- [ ] 轮换上线过程中用过的所有临时凭据。
- [ ] 不要把第一个管理员的凭据用于应用的日常访问。

当启用身份验证时，身份验证事件会被写入一个单独的身份验证日志。如果守护进程日志是 `/var/log/manticore/searchd.log`，那么身份验证日志就是 `/var/log/manticore/searchd.log.auth`。

`auth_log_level` 的取值有：

- `disabled`
- `error`
- `warning`
- `info`
- `all`
- `trace`

默认是 `info`。除非你有理由让日志更少，或者相反，需要更详细，否则就从它开始。`trace` 只用于诊断；它还会记录所有成功的内部身份验证流量。

有用的清理和维护命令：
```sql
SET PASSWORD 'NewReadPass#2026' FOR 'app_read';
REVOKE read ON 'products' FROM 'app_read';
DROP USER 'app_read';
```

`SET PASSWORD` 更改用于 SQL/MySQL 和 HTTP Basic 认证的密码。它不会吊销已有的 Bearer Token。若要轮换 Bearer 访问，请用 `TOKEN` 或 `POST /token` 创建新的 Token 并更新客户端。

## 阶段 5：回滚与排查

如果出了问题，回滚应当平淡无奇：
- 恢复之前的配置
- 重启 Manticore
- 恢复身份验证上线之前就存在的旧访问限制
- 如果客户端已经切换到新凭据，也要准备好把应用配置一并回滚。

不要删除上线笔记。要想弄清哪个客户端更新过、哪个 Token 存放在哪里、创建了哪些权限，它们通常是最快的途径。

| 症状 | 可能原因 | 检查 |
|---|---|---|
| SQL 访问被拒绝 | 用户错误、密码错误，或客户端身份验证方式不匹配 | 检查所配置的用户，并确认客户端能够使用 `mysql_native_password`。 |
| HTTP 401 | 缺少或无效的凭据 | 检查 `Authorization` 头，以及客户端使用的是 Basic 认证还是 Bearer Token。 |
| HTTP 403 | 用户已通过身份验证但缺少权限 | 检查 `SHOW PERMISSIONS FOR '<user>'`。 |
| Bearer Token 不再有效 | Token 丢失、复制有误，或已经被吊销 | 运行 `TOKEN '<user>'`，保存返回的 Token，并更新客户端。 |
| 用户能做的比预期少 | 缺少对应操作的授权 | 检查该操作需要的是 `read`、`write`、`schema`、`replication` 还是 `admin`。 |
| 用户能做的比预期多 | 目标过宽或缺少显式拒绝 | 检查通配符授权、精确目标授权，以及任何 `WITH ALLOW 0` 规则。 |

权限规则按操作类型确定。规则冲突时，显式拒绝始终优先于允许，即使允许规则更具体。如果没有匹配的允许规则，访问就会被拒绝。

## 复制中的身份验证注意事项

如果你使用复制，请规划一次单独的身份验证上线。上面的单节点清单仍然有用，但集群会带来一些容易被忽略的失败模式。

对分布式远程代理的查询以当前会话用户的身份进行身份验证。远程守护进程需要有该用户匹配的身份验证数据，以及对远程目标表的相应权限。

复制相关的操作使用 `replication` 这个操作。只把它授予应当运行并负责复制操作的用户。

启用身份验证后，加入集群时，本地的身份验证数据可能会被你所加入集群的身份验证数据替换。在加入之前，确保复制用户和身份验证数据在各节点之间保持一致。在集群操作期间，请把 `searchd.log.auth` 视为敏感数据，因为身份验证备份可能包含盐值和凭据哈希。

请把完整的集群迁移流程放到单独的文档中：这里重要的是指出与集群身份验证相关的风险。

## 最终检查

在宣布上线完成之前，请确认：

- [ ] 使用 Manticore Search 的每个系统独立部分都有自己的用户。
- [ ] 每个用户都只有自己需要的操作。
- [ ] Bearer Token 保存在受保护的 secrets 存储中；明文 Token 不会保存在受控环境之外。
- [ ] 运维团队知道 `SHOW TOKEN` 不会返回明文 Token，而是显示它的哈希；如需获取新 Token，应使用 `TOKEN` 或 HTTP 端点。
- [ ] SQL 和 HTTP 客户端都已更新。
- [ ] 已测试预期的拒绝。
- [ ] 身份验证日志可查看。
- [ ] 回滚流程清楚且已有文档。

祝你顺利完成身份验证和授权上线！
