回收 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)、记忆修复前备份。
7.8 KiB
建库权限 · 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 则是把当初明确否掉的形态原样装回来。此倾向可推翻。
六 拍板后要落的文档(⛔ 本轮未动,等拍板)
DEPLOY-本部署.mdDB 初始化步骤 —— 按选定档位写建库通路。交接单/插件投放与分库线-①…md §五 S4-2+§十-D1—— 建库 = admin 显式按钮(原文写"上传钩子建库",须改)。DB-03-插件数据面规范.md §三 / §四 / §六—— 谁建库 / 何时建 / 台账表plugin_datastores。- 平台侧错误码映射同步改:D 档的
PLUGIN_DB_HELPER_MISSING随档位作废。
七 未做(具名原因)
| 项 | 原因 |
|---|---|
| B / C 档的实际落地 | 依赖你拍板(§五)⇒ ⛔ 不自选 |
| 上述 §六 四份文档回改 | 内容随档位而变 ⇒ 拍板前落笔必返工 |
drop_plugin_database(彻底卸载用) |
⛔ 不在拍板范围(DB-03 §六-4 将来一并走) |