Files
dsh_ai1net_server/交付物/建库权限-D档被证伪与修正判定-20260923.md
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复:
- 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录)
- tmp/(32.4 M,按接续棒命名的过程临时区)
- .workbuddy/tmp/(39.5 M)
- 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物)
- tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留

入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与
接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、
.workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。

排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、
打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

140 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 建库权限 · 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` 将来一并走) |