23.6 Pigsty 安全基线
Pigsty 能把角色、HBA、服务入口、证书和日志参数声明化,但“声明化”不等于 “使用默认值即可满足任何生产 threat model”。官方安全说明明确指出,默认 配置面向可信内网中的开发、测试和演示;生产环境要按自身威胁模型配置凭据、 网络边界、认证、证书、备份和审计。
本节建立四层对账:
policy intent
-> Pigsty inventory
-> rendered files and service configuration
-> PostgreSQL/PgBouncer runtime facts
-> end-to-end positive and negative observations只有最下面一层能证明客户端实际经历了什么;只有最上面一层能解释为什么这样 设计。
23.6.1 角色、HBA、证书与服务入口声明
角色声明
Pigsty 用:
pg_default_roles 环境级共享角色
pg_users 集群级业务角色/用户用户按数组顺序创建,因此被引用的 group role 应先定义。下面是结构示例, 不是可直接投产的 secret 文件:
pg_users:
- name: app_owner
login: false
superuser: false
createdb: false
createrole: false
inherit: false
replication: false
bypassrls: false
comment: "NOLOGIN owner for app objects"
- name: app_runtime
login: false
inherit: false
comment: "NOLOGIN normal DML capability"
- name: app_readonly
login: false
inherit: false
comment: "NOLOGIN read-only capability"
- name: app_login
login: true
password: "<SCRAM-VERIFIER-FROM-PRIVATE-SECRET-PIPELINE>"
superuser: false
createdb: false
createrole: false
inherit: false
replication: false
bypassrls: false
connlimit: 100
roles:
- {name: app_runtime, admin: false, inherit: false, set: true}
- {name: app_readonly, admin: false, inherit: false, set: true}
pgbouncer: true
pool_mode: transaction
pool_connlimit: 80
comment: "application authentication identity"示例标记必须由私密交付机制替换,不能把真实明文或 verifier 提交到本书、Git、 工单或聊天记录。Pigsty 支持 plaintext 或 SCRAM verifier,但官方也把 plaintext inventory 标为不推荐。角色字段与 membership object 格式见 Pigsty:User/Role。
这里还要注意:
roles管理是 additive;未声明的历史 membership 不会自动消失;- 要撤销旧 membership,应显式使用
state: absent; - PostgreSQL 16+ 才支持 membership 的
set/inherit细分; pgbouncer: true会把 login 纳入 PgBouncer 认证面;NOLOGINowner/runtime/readonly 不应加入池用户列表;connlimit与 PgBouncer pool limit 是不同层的限制。
已存在集群修改角色:
bin/pgsql-user <cluster> <username>它是声明式、可重复入口。删除角色的 state: absent 会涉及断开连接、转移
ownership 和 DROP ROLE,属于破坏性变更;必须先 dry-run/依赖审查和审批,
不能把“脚本支持安全删除”理解为无需变更控制。官方管理流程见
Pigsty:用户管理。
membership 与对象 ACL 分开
Pigsty pg_users 可声明 cluster role 和 membership;schema/table/function
ACL、owner、default privilege 与 RLS policy 仍应由经过版本控制的 migration
完成:
Pigsty inventory
-> identity attributes, membership, pool admission
application migration
-> schema/table owner, ACL, default privileges, RLS policies不要在多个无序启动脚本里同时管理同一份 grant。平台和应用必须明确各自 owner 和收敛时点。
PostgreSQL 与 PgBouncer HBA
Pigsty 有四组参数:
| 参数 | 范围 | 作用 |
|---|---|---|
pg_default_hba_rules | global | PostgreSQL 环境默认规则 |
pg_hba_rules | global/cluster/instance | PostgreSQL 增量规则 |
pgb_default_hba_rules | global | PgBouncer 环境默认规则 |
pgb_hba_rules | global/cluster/instance | PgBouncer 增量规则 |
规则会按 order 排序,数值越小越靠前。未显式指定通常进入 1000+ 区域;
如果前面已有宽泛默认 allow,它可能永远匹配不到。
生产应用入口的概念示例:
pg_hba_rules:
- title: "app direct path from approved application subnet"
user: app_login
db: appdb
addr: "10.20.10.0/24"
auth: ssl
order: 50
- title: "reject app direct path from all other sources"
user: app_login
db: appdb
addr: world
auth: deny
order: 51
pgb_hba_rules:
- title: "app pooled path from approved application subnet"
user: app_login
db: appdb
addr: "10.20.10.0/24"
auth: ssl
order: 50
- title: "reject app pooled path from all other sources"
user: app_login
db: appdb
addr: world
auth: deny
order: 51auth: ssl 的当前 Pigsty alias 会渲染为 hostssl ... scram-sha-256;生产变更
仍应检查目标版本的渲染结果,不能永久依赖书中的 alias 解释。HBA 字段、
alias、role filter 和 order 规则见
Pigsty:HBA Rules。
若应用只允许经 pool/service 入口访问,可进一步在网络与 PostgreSQL HBA
中拒绝应用子网直连 5432,只允许本机 PgBouncer 或指定代理身份访问后端。
这能避免客户端绕过 PgBouncer 的连接限制、认证面与事务池合同。
intra 只是地址别名
Pigsty 的 intra/intranet 通常展开为 RFC 1918:
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16这些地址不是天然可信。企业办公网、VPN、容器网、其他租户 VPC 和开发环境都 可能落在其中。生产规则应尽量使用准确的应用 subnet/security group,而不是 把整个 RFC 1918 视为一个 trust zone。
刷新而非手工改文件
修改 inventory 后:
bin/pgsql-hba <cluster>会重新渲染并 reload PostgreSQL/PgBouncer 相关 HBA。不要直接编辑:
/pg/data/pg_hba.conf
/etc/pgbouncer/pgb_hba.conf下次 playbook 会覆盖手工改动,而且 inventory 与运行事实从此分叉。紧急手工 变更若无法避免,也必须同步回声明源、记录例外并尽快恢复收敛。
证书声明和使用
Pigsty 默认基础设施 CA 会为 PostgreSQL、Patroni、etcd、MinIO、Nginx 等 内部服务签发证书。生产评审要区分:
CA exists
server has certificate
listener supports TLS
HBA requires TLS
client trusts the intended CA
client verifies the intended DNS/IP name
client certificate, if required, is mapped and revocable这六件事不能合并成“开启 SSL”。
证书 SAN 应覆盖客户端实际使用的:
- HAProxy/VIP DNS name;
- PostgreSQL direct service name;
- 节点名/IP(若允许直连);
- planned disaster-recovery endpoint。
不要让应用用 hostaddr 绕过期望的 DNS name verification,除非同时提供可
校验的 host 语义并完成测试。
CA private key 所在目录是根信任资产。需要离线/受限备份、读取审计和恢复演练。 证书签发与客户端安装流程见 Pigsty:CA and Certificates。
两段 TLS 分别声明
若应用经 PgBouncer:
client ==TLS policy A==> PgBouncer
PgBouncer ==TLS/socket policy B==> PostgreSQLPigsty 的 PostgreSQL server TLS 默认开启,不表示 HBA 默认要求 TLS; PgBouncer client TLS 默认也不是开启状态。当前官方安全说明明确列出:
PostgreSQL TLS supported
default intranet HBA may not require it
PgBouncer TLS controlled separately by pgbouncer_sslmode
Patroni REST TLS controlled separately生产要逐入口决定:
| 链路 | 加密 | 对端验证 | 允许来源 | 认证主体 |
|---|---|---|---|---|
| app → HAProxy/PgBouncer | required | service name | app subnet | app login |
| PgBouncer → PostgreSQL | TLS 或受控 local socket | node/service | proxy nodes | server login |
| admin → PostgreSQL | required | admin endpoint | bastion/infra | named admin |
| replica → primary | required or isolated equivalent | node identity | cluster only | replication role |
service entrypoint
第 22 章区分了 direct PostgreSQL、PgBouncer、HAProxy primary/replica/offline 等入口。安全基线要为每个入口建立:
purpose
listener address/port
source allowlist
HBA/authentication
TLS name and CA
allowed roles/databases
pool mode
rate/connection limit
audit label
owner未使用入口应关闭或从网络上不可达。保留一个“以后可能调试”的公网 5432
会变成长期旁路。
23.6.2 管理面、监控面和数据库面的网络边界
先画流量矩阵
至少区分:
| 平面 | 典型组件 | 主要主体 | 失陷后风险 |
|---|---|---|---|
| 管理面 | meta/Ansible、SSH、sudo、secret store | 平台管理员/自动化 | 改写全部配置与密钥 |
| 控制面 | Patroni REST、etcd | cluster agent | 错误选主、拓扑控制 |
| 数据面 | HAProxy、PgBouncer、PostgreSQL | 应用/分析/迁移 | 数据读写与租户越权 |
| 监控面 | exporter、Prometheus、Grafana、日志 | monitoring identities | 查询/拓扑/日志泄露 |
| 备份面 | pgBackRest repo、WAL/archive | backup identities | 全量数据泄露或恢复破坏 |
“都是内网服务”不是边界设计。每条 flow 应记录:
source identity/network
destination service/address/port
direction
TLS/mTLS
authentication
authorization scope
availability dependency
log/audit
owner and review date管理面
管理节点通常能:
- 通过 SSH 到所有节点;
- sudo/root;
- 读取 inventory 或 vault integration;
- 运行 Ansible/playbook;
- 访问 CA material;
- 变更防火墙、HBA 和 service。
因此它不是普通“运维跳板”。应:
named human identity + MFA
short-lived SSH certificate/key
no shared permanent root password
separate automation service identity
command/change audit
restricted egress and inbound sources
workstation and bastion hardening
backup/recovery for control repository把数据库 superuser 密码从 inventory 移走,却允许所有工程师无审计地
sudo -iu postgres,并没有实现职责分离。
控制面
Patroni REST 和 etcd 决定 cluster topology。它们不应暴露给业务 subnet。 控制面规则通常只允许:
cluster members
approved management/monitoring nodeshealth check 入口与管理 API 要区分。HAProxy 为角色判断访问 Patroni health endpoint,不表示应用客户端也应访问完整 Patroni API。
控制面 TLS、认证和 ACL 必须按目标 Pigsty 版本验证。不能因为 etcd 使用 TLS, 就推断 Patroni REST 也已经使用 TLS。
数据面
建议把应用路径收敛为:
application subnet
-> primary/replica service VIP/DNS
-> HAProxy
-> local/cluster PgBouncer
-> PostgreSQL然后按实际需要开放 direct path:
management/migration subnet -> PostgreSQL direct
replication nodes -> PostgreSQL replication
backup/monitor -> dedicated minimum roles网络控制至少三层:
cloud security group / network ACL
host firewall
listener + HBAHBA 不是防 DDoS 的边界:连接已经到达 PostgreSQL 才会检查。安全组/防火墙也 不理解 database/user。二者互补。
Pigsty node_firewall_mode=zone 的默认可信 intranet 必须与真实边界对账。
官方建议也明确提醒 RFC 1918 范围可能过宽,生产 demo 配置通常应移除无必要
的 5432 暴露。参见
Pigsty:Security Recommendations。
监控面
monitor role 看起来只读,但往往能访问:
pg_stat_activity查询文本;- replication/topology;
- database/object names;
- slow-query samples;
- connection source/user;
- logs and dashboards。
因此:
- monitor 不应使用 superuser;
- exporter query 必须受版本控制;
- dashboard 与 Prometheus API 要认证;
- label 不能含 password/token/高基数 PII;
- 跨环境 metrics/logs 要隔离;
- support snapshot 要脱敏;
- 监控不可用不能使数据库认证 fail-open。
备份面
backup repository 持有跨 RLS、跨租户的完整数据与 WAL。网络上只允许备份 主体和恢复路径;读取、删除、retention 改动应分权。数据库 RLS/HBA 无法保护 一份已被复制到对象存储的备份。
恢复环境同样要隔离:把生产备份恢复到宽松开发网,常比攻击生产数据库更容易 造成泄露。第 21、32、33 章会继续处理备份与恢复的控制。
egress 也要控制
数据库节点出站能力可能被:
- extension/FDW;
COPY PROGRAM高权能力;- untrusted language;
- backup/archive command;
- 运维脚本;
- 被攻陷进程
用于外带数据。仅做 inbound firewall 不够。生产需要明确允许的软件源、 backup endpoint、DNS/NTP/monitoring 等 egress,并监控异常。
23.6.3 从配置渲染到运行事实的差异检查
四份状态
对同一项控制记录:
desired reviewed inventory/policy
rendered node-local file/service config
runtime catalogs, settings, sockets, process state
observed actual client connection or rejected action示例:
| 控制 | desired | rendered | runtime | observed |
|---|---|---|---|---|
| app TLS | auth: ssl | hostssl | pg_hba_file_rules | disable fails, verify-full succeeds |
| role | NOINHERIT | generated SQL | pg_roles | raw login SELECT fails |
| membership | SET true | grant statement | pg_auth_members | SET LOCAL succeeds |
| RLS | migration | table/policy DDL | pg_class/pg_policies | cross-tenant writes fail |
| pool identity | pgbouncer: true | userlist presence | SHOW USERS projection | pool auth succeeds |
任何一列缺失都不能宣告闭环。
inventory 评审
先检查:
- 是否把 secret 直接写入 Git;
- role 高权 flag;
inherit和 membershipadmin/set/inherit;pgbouncer暴露范围;- HBA order 与宽规则;
host/hostssl;world/intraalias 展开;- primary/replica/offline role filter;
- listener/service port;
- PostgreSQL/PgBouncer/Patroni TLS;
- logging/audit;
- firewall intranet。
inventory diff 应显示结构,不应把 password/verifier 展开到 PR。
渲染检查
变更前先生成/审查候选,应用后检查节点:
/pg/data/pg_hba.conf
/etc/pgbouncer/pgb_hba.conf
PgBouncer main/user options, secret-redacted projection only
PostgreSQL include/config source
HAProxy listener/backend projection
certificate public metadata
host firewall projection不要把 /etc/pgbouncer/userlist.txt 原文放进证据;它可能含 verifier。
HBA 应验证:
SELECT *
FROM pg_hba_file_rules
ORDER BY rule_number;并确认 error IS NULL、顺序与 inventory 相符。
runtime 检查
角色:
SELECT
rolname, rolcanlogin, rolsuper, rolcreatedb, rolcreaterole,
rolreplication, rolbypassrls, rolconnlimit, rolvaliduntil
FROM pg_roles
ORDER BY rolname;membership:
SELECT
granted.rolname AS granted_role,
member.rolname AS member,
m.admin_option,
m.inherit_option,
m.set_option
FROM pg_auth_members AS m
JOIN pg_roles AS granted ON granted.oid = m.roleid
JOIN pg_roles AS member ON member.oid = m.member;对象与 RLS:
owners
schema/table/function/sequence ACL
default ACL
relrowsecurity / relforcerowsecurity
pg_policies
view/function security attributesTLS:
SHOW ssl / ssl_min_protocol_version
certificate SAN, issuer, validity, fingerprint
private key owner/mode, never contents
pg_stat_ssl on real sessions
verify-full positive and wrong-name negative日志:
log_statement
duration/sample thresholds
parameter logging limits
connection/disconnection settings
shared_preload_libraries
pg_extension
collector/forwarder healthobserved 检查
必须从与真实 workload 相同的网络和 driver 路径执行:
allowed source + right login + right DB succeeds
wrong source rejected
wrong password rejected
sslmode=disable where TLS required rejected
verify-full matching name succeeds
verify-full wrong name rejected
pool login independently succeeds
direct bypass where prohibited rejected
raw login object access rejected
approved SET LOCAL role succeeds
cross-tenant action rejected只在 database node 上用 Unix socket psql,无法验收远程网络、proxy HBA、
TLS name 或客户端 CA distribution。
本章沙箱差异
在 Pigsty v4.4.0 nonproduction sandbox 中,正式捕获发现:
| 控制 | 期望/能力 | 运行事实 | 判定 |
|---|---|---|---|
| PostgreSQL TLS | server 可加密 | on,最低 TLS 1.2 | 能力通过 |
| cert identity | SAN + verify-full | 正例成功,错名失败 | 通过 |
| direct channel binding | SCRAM over TLS | 成功 | 通过 |
| business direct HBA | 生产应强制 TLS | +dbrole_readonly 内网为 host | 待整改 |
| PgBouncer client TLS | 生产应按 threat model 启用 | disabled | 待整改 |
| pool → PostgreSQL | 受控链路 | local Unix socket | 事实符合设计 |
| CRL | 撤销路径 | file/dir unset | 待整改 |
| pgAudit | 目标要求对象审计 | absent/not preloaded | 待整改 |
| bind logging | secret-minimized | 非错误参数上限 -1 | 待整改 |
它诚实地同时证明:
TLS mechanism works
AND
non-TLS direct business connection is still admitted第一条不能抵消第二条。
分阶段整改
安全变更也会造成可用性风险,应分阶段:
Phase 1 inventory
client versions, source networks, DNS names, CA trust, pool entrypoints
Phase 2 prepare
issue correct SAN certs
distribute CA
make clients use verify-full
establish metrics and rollback
Phase 3 enforce client-to-pool TLS
enable PgBouncer TLS
positive/negative tests
roll clients
Phase 4 enforce PostgreSQL HBA
add high-priority hostssl allow
retain controlled exception if necessary
prove non-TLS rejection
Phase 5 audit and secret minimization
install matching pgAudit if required
select classes/objects
reduce bind parameter logging
validate volume and redaction
Phase 6 remove exceptions
expire compatibility HBA/CA/password
terminate old sessions
update runbook and evidence一次性把所有 host 改成 hostssl,如果客户端尚未安装 CA 或仍用 IP 不匹配
证书,会把安全整改变成全站故障。
例外不是口头备注
暂时保留非 TLS/旧客户端时,例外记录至少包含:
exact source/destination/role/database
business reason
threat and data classification
compensating network control
owner and approver
created/expires
remediation milestone
monitoring
tested rollback无到期日的例外就是新默认。
生产判定
最终报告只允许:
| 判定 | 含义 |
|---|---|
pass | 所有必须项有运行与负例证据 |
pass-with-exception | 明确、批准、限时、受补偿控制的差距 |
pending | 机制可用,但必需控制尚未实施/证明 |
reject | 存在不可接受暴露或证据冲突 |
本章沙箱是 pending。它完全适合教学实验,却不能被包装成生产审批。这种区分
正是安全工程成熟度的一部分。Pigsty 当前生产注意事项见
Pigsty:Security Considerations。
上一节:密钥、审计与敏感信息 · 返回本章目录 · 下一节:实战:隔离两个租户 · 查看全书目录 · 查看索引中心