target pg36-l2-vagrant/pg-test
Pigsty v4.5.0
PostgreSQL 18.6
PgBouncer 1.25.2
database test
leader pg-test-1
replicas pg-test-2, pg-test-3
timeline 11 before and after
data four fixed synthetic rows
production data=false, traffic=false
实验明确不做:
production approval
CA/private-key rotation
HBA/firewall mutation
real customer data
malicious root test
topology change
default privileges 同时约束未来由 owner 创建的 table/function,防止下一次
migration 恢复 PUBLIC 暴露。
23.7.2 通过 PgBouncer 事务池验证 RLS
应用事务
runner 模拟应用:
connection.execute("BEGIN")connection.execute("SET LOCAL ROLE pg36_ch23_runtime")connection.execute("SELECT set_config('app.tenant_id', %s, true)",(authorized_tenant_id,),)rows=connection.execute("""
SELECT tenant_id, account_id, display_name
FROM pg36_ch23.account
ORDER BY tenant_id, account_id
""").fetchall()connection.execute("COMMIT")
role 来自固定 allowlist,tenant id 使用参数绑定,并被标记为 synthetic
server-authorized mapping。实验不把终端用户输入直接信任为 context。
direct PostgreSQL sslmode=require success
direct verify-full matching name success
direct verify-full wrong name rejected
direct verify-full + channel_binding=require success
direct sslmode=disable success, known gap
pooled sslmode=disable success, known gap
pooled sslmode=require rejected, known gap
然后 A 和一个新 client B 都只在事务内设置 runtime role,不再设置 tenant。
结果:
client A backend pid 72521
client B backend pid 72521
same backend true
B context tenant A
B visible rows 2 tenant-A rows
B 从未声明 tenant A,却继承 A 的 session state。这条反例是 validator 的必须
项;如果没有复现,实验不会用“可能没复用”蒙混通过。
修复验证
清理 server connection 后,四个逻辑 client 依次执行完整事务合同:
A
missing
B
missing
全部复用 PID 72578:
A context A, 2 A rows
missing context NULL, 0 rows
B context B, 2 B rows
missing context NULL, 0 rows
因此修复证据同时包含:
same backend reused
AND
previous transaction state absent
若只验证两个 backend PID 不同,不能证明 transaction-local 合同。
密码轮换注入
pg36_ch23_rotate 只用于 direct PostgreSQL 认证,不进入 PgBouncer 声明面。
runner 用随机、只存在内存/私密进程输入中的 v1/v2:
enable LOGIN with v1
new direct auth v1 success
pooled auth rejected
change verifier to v2
new direct auth v1 rejected
new direct auth v2 success
session authenticated with v1 still usable
set NOLOGIN
new direct auth rejected
existing session still usable
set PASSWORD NULL
final role state NOLOGIN/password absent
1. freeze secret distribution and identify exact identity/scope
2. block new auth at IdP/proxy/PostgreSQL
3. set role NOLOGIN and revoke password/token/certificate
4. stop app pools from reconnecting with old material
5. enumerate existing sessions across direct and proxy paths
6. assess in-flight transactions and terminate sessions
7. rotate downstream/shared credentials
8. inspect role grants, RLS/DDL, exports, logs and backups
9. validate old auth fails and new controlled auth succeeds
10. preserve secret-free evidence and start incident review
exact schema owner/comment
exact five role comments/attributes
no active synthetic-role sessions
known memberships
然后只删除:
schema pg36_ch23
five pg36_ch23_* roles
memberships introduced by this lab
它保留已有 test login 和全部非 fixture 对象。该命令是破坏性操作;本书正式
验收没有执行它,fixture 留作复查。
生产门槛
实验正式通过:
role separation pass
object minimum privilege pass
forced two-tenant RLS pass
transaction-local pool context pass
password rotation semantics pass
pool restore pass
topology unchanged pass
secret-free evidence pass
但生产仍为 pending:
business direct HBA permits non-TLS
PgBouncer client TLS disabled
CRL absent
client CA distribution/rotation not exercised
pgAudit absent
non-error bind values may be fully logged