# 建库权限 · 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_`。 **优点** - 实现最直:平台代码里零新增凭据、零新增连接路径,一条 `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` 将来一并走) |