Files
dsh_ai1net_server/交付物/建库权限-D档被证伪与修正判定-20260923.md
T

139 lines
7.8 KiB
Markdown
Raw Normal View History

# 建库权限 · D 档被证伪后的修正判定(2026-09-23)
> **一句话结论**:2026-09-22 拍板的 **D 档(`SECURITY DEFINER` 函数建库)在 PostgreSQL 上不可实现** —— 这是 PG 内核硬限制,不是实现姿势问题。**已回滚,服务器零残留**。修正后可行路径为 **B 档 / C 档**二选一,**等你拍板**。
> 🔴 **2026-09-23 05:4x 更新:用户已拍板「B」** ⇒ 落地件另见 `交付物/插件建库权限-B档落地-20260923.md`(已在 47 落地 + 11 条验收通过)。本文以下保留为**判定过程与证据记录**。
> **机位**:仅 **47**(PG 只在 Manager 上;106 无 PG)。
> **本轮对生产的净影响**:**零**(建了又按回滚流程清掉;`dshs` 权限、内核 13 表、服务状态全部原样)。
---
## 一 D 档为什么不可实现(实测证据,非推断)
PG 对 `CREATE DATABASE` 有**两层独立禁止**,任一层单独就足以否决:
| 层 | 报错原文 | 触发条件 |
|---|---|---|
| 事务层 | `CREATE DATABASE cannot run inside a transaction block` | 任何事务内 —— PL/pgSQL 函数体**恒在事务内** |
| 函数层 | **`CREATE DATABASE cannot be executed from a function`** | PG 显式禁止从函数发起(即使 `SECURITY DEFINER`) |
实测对照(同一私有 schema、同一 `SECURITY DEFINER` 姿势,只换语句):
```
函数内 CREATE SCHEMA → schema_ok ✅ 成功
函数内 CREATE ROLE → role_ok ✅ 成功
函数内 CREATE DATABASE → ERROR: CREATE DATABASE cannot be executed from a function ❌ 内核拒绝
```
⇒ **`SECURITY DEFINER` 这条封装路径对 `CREATE DATABASE` 无效**。当初选 D 档的动机("让平台能建库,但不把 `CREATEDB` 给 `dshs`")在这条路上**无法同时满足**。
### 一并探明的旁路(避免重复试)
| 旁路 | 结论 | 证据 |
|---|---|---|
| `dblink` 扩展 | ❌ 不可用 | `pg_available_extensions` 无该条目;`select dblink_connect(...)` ⇒ `function does not exist` |
| `postgres_fdw` 扩展 | ❌ 不可用 | 同上,未安装且不可安装 |
| 已装扩展 | 仅 `plpgsql` | `select extname from pg_extension` ⇒ `plpgsql`(单条) |
| `su postgres` 到平台进程 | ❌ 不存在该用户 | `su: user dshs does not exist` —— `dshs` 是 **systemd 服务名**,不是系统用户 |
---
## 二 修正后可行路径(两条,各有优有劣 ⇒ 交你拍板)
### 候选 B · 角色级授权(给 `dshs` 加 `CREATEDB`)
做法:`ALTER ROLE dshs CREATEDB`,平台侧用现有 `dshs` 连接直接执行 `CREATE DATABASE dshs_pl_<id>`。
**优点**
- 实现最直:平台代码里零新增凭据、零新增连接路径,一条 `Pool.query` 即可。
- 与现有部署形态完全同构(`DSHS_DB_URL` 不变)⇒ 换机器重建时无额外步骤。
- 权限面收在 PG 内,`pg_roles` / 审计里可查可审计。
**缺点**
- `dshs` 拿到**全局** `CREATEDB` ⇒ PG 没有"只允许建 `dshs_pl_*` 前缀库"的原生机制,白名单只能靠平台代码自觉。
- 一旦平台代码被注入,攻击面从"当前库"扩到"可建库"(虽然仍不能 `DROP` 别人的库)。
- 这正是 09-22 否掉 A 档的**同一条理由**,换个档位名原样复现。
### 候选 C · 平台侧超管直连建库
做法:平台侧持一份超管凭据(只落 systemd drop-in,600),建库时单开一条连接、单语句执行、用完即断。
**优点**
- `dshs` 角色权限**完全不扩大**(保持 `f|f|f`)—— 保住 09-22 的原始意图。
- 建库动作天然单点:只有这一处代码能建库,grep 可穷举,审计点唯一。
- PG 侧不受"函数/事务"限制(已实测单语句 `-c` 建删成功)。
**缺点**
- 超管凭据进入**应用进程环境** ⇒ 凭据面从"运维手上"扩到"平台进程内存里"。
- 平台进程以 root 运行 ⇒ 凭据泄露的最坏后果是同机提取,风险等级高于 B。
- 换机器重建时多一条必须同步的凭据项(漏了就建不了库)。
---
## 三 实测读数总表(2026-09-23 · 全部现场取证)
| 项 | 读数 | 含义 |
|---|---|---|
| PG 版本 | `PostgreSQL 13.23` | 与既有认知一致 |
| 已装扩展 | `plpgsql` | 无 `dblink` / `postgres_fdw` |
| `dshs` 权限 | `rolsuper=f` `rolcreatedb=f` `rolcreaterole=f` | 无间接提权 |
| `dshs` 在 `dshs` 库 | `has_database_privilege('dshs','dshs','CREATE') = t` | 建 schema **零扩权** |
| 平台进程用户 | `systemctl show dshs -p User` ⇒ 空 | 以 **root** 运行 |
| `pg_hba` | `local all all peer` + `host … 127.0.0.1/32 scram-sha-256` | 超管走 socket peer;平台走 TCP 口令 |
| 超管通路 | `su postgres -c 'cd /tmp && psql -h /var/run/postgresql -p 15432 -d dshs …'` | ✅ 可用(⚠️ 必须先 `cd /tmp`) |
| `dshs_pl_%` 库数 | **0** | 尚无业务插件库 |
| 内核表数 | **13** | 与 §2-2 既有认知一致,未被本轮触碰 |
| `lib/` 内建库代码 | **0 处** `CREATE DATABASE` | 建库面确实从零起 |
| 服务状态 | `dshs` = `active` | 全程未重启 |
---
## 四 本轮实际执行与回滚(完整可复现)
```
1 抢全局锁 → ✓ 已持(插件投放与分库线-执行棒②)
2 落地 D 档 DDL → DDL_RC=0(schema dshs_int + 函数 create_plugin_database 创建成功)
3 跑 7 条验收 → ①create/②幂等 报 ERROR(事务块)⇒ 判定不可实现
4 旁路探测 → dblink / postgres_fdw / create role / create schema 逐个取证
5 回滚 → REVOKE → DROP FUNCTION → DROP SCHEMA
6 回滚后核查 → dshs_int 残留 0 · 函数残留 0 · dshs 权限 false|false|false
内核表 13 · 服务 active · 探测残留(probe%)全 0
```
**净影响 = 零。** 服务器状态与本轮开工前逐项一致。
---
## 五 待你拍板(一项)
> ✅ **2026-09-23 05:4x 已拍板 = 「B」**。以下候选原文保留作记录;**落地件 ⇒ `交付物/插件建库权限-B档落地-20260923.md`**。
**建库通路选 B 还是 C** —— 两者各有优有劣、客观标准分不出高下,故不替你拍:
**候选 B · 角色级授权(给 `dshs` 加 `CREATEDB`)**
- 优点:实现最直、零新增凭据、与现有部署同构、权限面可在 PG 内审计。
- 缺点:`dshs` 拿到全局 `CREATEDB`,白名单只能靠代码自觉;这正是 09-22 否掉 A 档的同一条理由。
**候选 C · 平台侧超管直连建库**
- 优点:`dshs` 权限完全不扩大(保住 09-22 原始意图)、建库动作单点可穷举、PG 侧无函数/事务限制。
- 缺点:超管凭据进入以 root 运行的应用进程 ⇒ 凭据面扩大,最坏后果同机提取。
我的倾向:**C** —— 它保住了 09-22 拍 D 档时想守的那条线(角色权限不扩大),而凭据面的代价可以通过"只落 600 drop-in + 单点调用 + 全量审计"压到与 B 同级;B 则是把当初明确否掉的形态原样装回来。此倾向可推翻。
---
## 六 拍板后要落的文档(⛔ 本轮未动,等拍板)
1. `DEPLOY-本部署.md` DB 初始化步骤 —— 按选定档位写建库通路。
2. `交接单/插件投放与分库线-①…md §五 S4-2` + `§十-D1` —— 建库 = **admin 显式按钮**(原文写"上传钩子建库",须改)。
3. `DB-03-插件数据面规范.md §三 / §四 / §六` —— 谁建库 / 何时建 / 台账表 `plugin_datastores`。
4. 平台侧错误码映射同步改:D 档的 `PLUGIN_DB_HELPER_MISSING` 随档位作废。
---
## 七 未做(具名原因)
| 项 | 原因 |
|---|---|
| B / C 档的实际落地 | 依赖你拍板(§五)⇒ ⛔ 不自选 |
| 上述 §六 四份文档回改 | 内容随档位而变 ⇒ 拍板前落笔必返工 |
| `drop_plugin_database`(彻底卸载用) | ⛔ 不在拍板范围(`DB-03 §六-4` 将来一并走) |