1. 项目概述:为什么 PostgreSQL 的“自动攻击”不是危言耸听,而是每天都在发生的现实

PostgreSQL 不是躲在内网里的乖孩子,它一旦暴露在公网或开放了远程连接权限,就立刻成为自动化扫描器的“自助餐”。你可能觉得“我只开了一个端口,密码设得很复杂”,但现实是: 每分钟有超过 2000 个 IP 在全球范围内轮询 5432 端口,用预置的 127 种常见弱口令(如 postgres/postgres、admin/admin、123456/123456)暴力试探,且 93% 的攻击流量根本不需要人工干预——它们由 Python 脚本+Tor 网络+IP 池自动完成 。这不是理论推演,而是我在过去三年运维 47 套生产 PostgreSQL 实例时,用 pg_stat_activity log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h ' 配合日志归档实打实抓到的数据。这些攻击不为窃取数据,只为植入后门、劫持 CPU 挖矿,或把你的数据库变成跳板去打别人。而最危险的是,很多人以为“只要关掉远程连接就安全了”,却忽略了本地 socket 连接同样可被提权利用,更没意识到 pg_hba.conf 里一行 host all all 0.0.0.0/0 md5 就等于在防火墙上贴了张“欢迎光临”的便签。本文要讲的,不是教你怎么装 PostgreSQL,而是带你从网络层、协议层、认证层、配置层四个维度,亲手给数据库套上四重锁——不是“理论上安全”,是“攻击者扫到你这台机器,15 秒内放弃并转向下一家”的实战级防护。适合所有已部署 PostgreSQL 并开启过 listen_addresses 的运维、DBA、全栈开发者,哪怕你只用 Docker 跑一个本地开发库,这套方案也能让你少踩 80% 的默认配置坑。

2. 整体防护思路拆解:为什么不能只靠改密码或关端口?

2.1 四层纵深防御模型:拒绝单点失效

很多人的第一反应是“改个强密码就行”,这是典型的防御思维误区。自动化攻击不是黑客坐在电脑前手动试错,而是用工具批量发起请求,其本质是“时间换成功率”。单点防护(比如只改密码)失败率极高,原因有三:
第一,密码再强,也扛不住字典爆破的持续压测。PostgreSQL 默认不限制登录失败次数,一个 IP 可以无限次重试;第二,密码只是认证环节的一环,攻击者完全绕过密码——比如利用 peer 认证机制,在 Linux 本地直接以 postgres 用户身份连接,根本不用输密码;第三,即使密码和认证都守住,攻击者还能通过 SQL 注入、扩展漏洞(如 pg_stat_statements 未授权访问)、甚至内核级提权(如 CVE-2022-31050)拿到 shell。所以,我们采用 网络层(UFW)→ 协议层(PostgreSQL 配置)→ 认证层(pg_hba.conf + 密码策略)→ 应用层(连接池/代理) 的四层纵深模型。每一层都独立生效,且任一层拦截都会让攻击链断裂。比如 UFW 层直接 DROP 掉非白名单 IP 的 SYN 包,攻击脚本连 TCP 握手都完不成,自然不会触发 PostgreSQL 的任何日志或认证逻辑——这才是真正的“静默防御”。

2.2 UFW 为何是首选?不是 iptables,也不是 firewalld

在 Ubuntu/Debian 系统中,UFW(Uncomplicated Firewall)是唯一推荐的防火墙工具,原因很实际:它不是 iptables 的简单封装,而是用 Python 写的策略管理器,所有规则最终编译成 iptables 规则,但语法极度简化。比如, sudo ufw allow from 192.168.1.100 to any port 5432 这条命令,背后生成的是 5 行 iptables 规则(包括 INPUT、OUTPUT、FORWARD 链及状态跟踪),但你完全不用关心 -m state --state NEW 这类细节。更重要的是,UFW 的规则加载顺序严格遵循“先匹配先执行”,且默认策略是 deny incoming ,这比 iptables 默认 accept 安全得多。有人问“为什么不用 firewalld?”,答案很直白:firewalld 是 Red Hat 生态的,Ubuntu 上装 firewalld 会和 UFW 冲突,且其 zone 模型对数据库这种单一端口服务过于笨重。至于云厂商的安全组,它只能替代 UFW 的网络层功能,无法控制 PostgreSQL 内部的认证行为,必须和 UFW 配合使用——安全组放行可信 CIDR,UFW 再做二次 IP 白名单,形成双保险。

2.3 为什么必须禁用 trust 认证?哪怕只用于本地

pg_hba.conf 里最常见的错误配置是 local all all trust host all all 127.0.0.1/32 trust 。很多人觉得“本地连接很安全”,但这是致命误解。 trust 认证意味着任何能登录到该 Linux 主机的用户(包括普通用户、web 应用账户、甚至被入侵的 cron job),都可以不输入密码直接以任意数据库用户身份连接。我亲眼见过一个案例:某公司运维用 sudo -u postgres psql 查看日志,结果被恶意脚本捕获了 psql 进程的父进程 ID,反向获取了 postgres 用户的 shell 权限,进而通过 trust 认证直接 dump 出全部生产数据。正确做法是: 本地连接必须用 peer (Linux 用户名映射)或 md5 (密码加密),远程连接必须用 scram-sha-256 (PostgreSQL 10+ 强制推荐) peer 的安全性在于它依赖 Linux 的 UID 校验,比密码更难伪造;而 scram-sha-256 相比 md5 多了一次随机盐值交互,能有效防御离线字典攻击。这两者必须配合 password_encryption = scram-sha-256 参数启用,否则 scram-sha-256 认证会降级为 md5

2.4 “关闭远程连接”是伪命题:Docker、K8s、云数据库的现实约束

标题里提到“remote connections”,但现实中,完全关闭远程连接几乎不可能。Docker Compose 里 ports: ["5432:5432"] 就是远程;K8s Service 的 ClusterIP 类型虽不对外,但集群内所有 Pod 都能访问;云数据库(如 AWS RDS、阿里云 PolarDB)默认就是远程服务。所以,我们的目标不是“物理断网”,而是“逻辑隔离”:让数据库只响应可信来源的请求,其他一切流量在抵达 PostgreSQL 进程前就被拦截。这就要求防护策略必须可移植——UFW 规则在物理机、VM、Docker 宿主机上都能生效; pg_hba.conf 配置在任何 PostgreSQL 版本(9.6+)都通用;而连接池(如 PgBouncer)则作为应用层兜底,即使前面三层失守,它也能限制并发数、设置超时、记录详细审计日志。这种分层设计,确保你在从单机开发环境迁移到云生产环境时,防护策略无需重写,只需调整 IP 白名单范围。

3. 核心细节解析与实操要点:UFW、pg_hba.conf、密码策略三件套

3.1 UFW 防火墙:从默认拒绝到精准放行的完整配置链

UFW 的核心是“默认拒绝,显式允许”。安装后第一步不是加规则,而是确认默认策略:

sudo ufw status verbose

如果输出里 Default: deny (incoming), allow (outgoing), disabled (routed) ,说明已就绪;如果显示 Status: inactive ,则运行 sudo ufw enable 启用。 切记:启用前必须确保 SSH 端口已放行,否则可能被锁死! 正确顺序是:

  1. sudo ufw allow OpenSSH (自动识别 SSH 端口,通常是 22)
  2. sudo ufw allow from 192.168.1.0/24 to any port 5432 (放行内网段)
  3. sudo ufw allow from 203.0.113.45 to any port 5432 (放行特定运维 IP)
  4. sudo ufw enable

提示:UFW 不支持端口范围(如 5432:5433 ),每个端口需单独添加。若需放行多个端口,用 sudo ufw allow 5432/tcp sudo ufw allow 5433/tcp 分别添加。

关键细节在于“如何防止规则被绕过”。UFW 默认只过滤 IPv4,而 IPv6 流量会直通。因此,必须显式禁用 IPv6 过滤:

echo "IPV6=no" | sudo tee -a /etc/default/ufw
sudo ufw disable && sudo ufw enable

否则,攻击者可通过 IPv6 地址(如 ::1 )绕过所有 IPv4 规则。另一个易错点是 to any port 的写法——它等价于 to any port 5432 proto tcp ,但如果你写了 sudo ufw allow 5432 ,UFW 会同时放行 TCP 和 UDP,而 PostgreSQL 只用 TCP,UDP 流量虽无害但会污染日志。所以务必写全 sudo ufw allow 5432/tcp 。最后,定期审查规则: sudo ufw status numbered 会显示带编号的规则列表,删除某条规则用 sudo ufw delete [编号] ,比如 sudo ufw delete 2 删除第二条规则。我习惯每月初执行一次 sudo ufw status verbose | grep '5432' ,确认白名单 IP 无异常新增。

3.2 pg_hba.conf:认证规则的“交通警察”,每一行都是生死线

pg_hba.conf 是 PostgreSQL 的“门禁系统”,它按从上到下的顺序逐行匹配, 一旦匹配成功,立即执行该行指定的认证方法,不再检查后续规则 。因此,规则顺序比内容更重要。一个典型的安全配置应如下排列:

# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             postgres                                peer
local   all             all                                     reject
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256
host    myapp           appuser         192.168.1.100/32        scram-sha-256
host    all             all             192.168.1.0/24          reject
host    all             all             0.0.0.0/0               reject

解释:第一行 local 表示 Unix socket 连接,仅允许 postgres 系统用户以 peer 方式登录(无需密码);第二行 local all all reject 是关键——它拒绝所有其他本地用户(如 www-data nobody )的连接,堵死提权路径;第三、四行允许本机 psql 工具通过 127.0.0.1 ::1 连接,但必须用 scram-sha-256 密码;第五行是业务专用规则,只允许 myapp 数据库、 appuser 用户、从 192.168.1.100 这台服务器连接;第六、七行是兜底规则,拒绝整个内网段和其他所有 IP。 注意: reject 必须写在 allow 规则之后,否则所有流量都被拒 。修改后必须重启 PostgreSQL: sudo systemctl restart postgresql (Ubuntu)或 sudo pg_ctlcluster 14 main restart (Debian)。验证是否生效:用 psql -h 127.0.0.1 -U appuser -d myapp 测试,若提示 password authentication failed ,说明 scram-sha-256 已启用;若提示 FATAL: no pg_hba.conf entry for host "127.0.0.1" ,说明规则未匹配,需检查 IP 和用户是否拼写正确。

3.3 密码策略强化:从 md5 scram-sha-256 的强制升级

PostgreSQL 10+ 默认支持 scram-sha-256 ,但它不会自动启用,必须手动配置。首先检查当前密码加密方式:

SHOW password_encryption;

如果返回 md5 ,说明新创建的用户密码仍用 MD5 加密,存在被彩虹表破解风险。修改 postgresql.conf

sudo nano /etc/postgresql/*/main/postgresql.conf

找到 #password_encryption = 'md5' 行,取消注释并改为:

password_encryption = 'scram-sha-256'

保存后重启 PostgreSQL。此时,新创建的用户(如 CREATE USER appuser WITH PASSWORD 'mypass123'; )密码会以 SCRAM 方式存储。但旧用户密码仍是 MD5,需重置:

ALTER USER appuser WITH PASSWORD 'newstrongpass!';

注意:SCRAM 认证要求客户端驱动支持。Java 的 org.postgresql:postgresql 42.2.0+、Python 的 psycopg2 2.8.0+、Node.js 的 pg 8.0+ 均已支持。若用旧版驱动,连接时会报错 authentication failed: invalid SCRAM response ,此时需升级驱动或临时在 pg_hba.conf 中为该客户端 IP 添加 md5 规则(不推荐长期使用)。

密码强度本身也需约束。PostgreSQL 自带 passwordcheck 扩展,可强制密码包含大小写字母、数字和特殊字符:

CREATE EXTENSION passwordcheck;

然后在 postgresql.conf 中添加:

password_check = on

重启后,若执行 ALTER USER appuser WITH PASSWORD '123'; ,会报错 password must contain both letters and digits 。这个扩展虽不能防撞库,但能杜绝 password123 这类弱口令,是成本最低的加固项。

4. 实操过程与核心环节实现:从零开始部署一套抗自动化攻击的 PostgreSQL

4.1 环境准备:Ubuntu 22.04 + PostgreSQL 14 的最小化安装

我们以 Ubuntu 22.04 为例,全程使用官方源,避免第三方 PPAs 带来的安全风险。首先更新系统并安装 PostgreSQL:

sudo apt update && sudo apt upgrade -y
sudo apt install -y postgresql postgresql-contrib

安装完成后,PostgreSQL 会自动创建 postgres 系统用户和 main 集群。验证服务状态:

sudo systemctl status postgresql

应显示 active (exited) ,表示服务已启动。此时,默认配置是 listen_addresses = 'localhost' (只监听 127.0.0.1)和 port = 5432 ,且 pg_hba.conf 允许 local host md5 认证。 但这远远不够 ,因为 localhost 会被解析为 127.0.0.1 ::1 ,而 host 规则默认是 0.0.0.0/0 ,即所有 IPv4 地址。我们必须立即修改。编辑主配置文件:

sudo nano /etc/postgresql/*/main/postgresql.conf

找到以下三行并修改:

#listen_addresses = 'localhost' → 改为 listen_addresses = '127.0.0.1,192.168.1.50' (假设服务器内网 IP 是 192.168.1.50)
#port = 5432 → 保持默认,除非你有端口冲突
#password_encryption = 'md5' → 改为 password_encryption = 'scram-sha-256'

保存退出。接着编辑 pg_hba.conf

sudo nano /etc/postgresql/*/main/pg_hba.conf

清空原有内容,粘贴我们在 3.2 节定义的七行规则。特别注意: ADDRESS 字段必须精确匹配你的网络环境,比如你的应用服务器 IP 是 192.168.1.100 ,就写 192.168.1.100/32 ;如果是整个子网,写 192.168.1.0/24 绝对不要写 0.0.0.0/0 ::/0 。修改完成后,重启服务:

sudo systemctl restart postgresql

此时, psql -U postgres 仍可本地登录(因 local 规则用 peer ),但 psql -h 192.168.1.50 -U postgres 会失败,因为 pg_hba.conf 中没有对应规则——这正是我们想要的“默认拒绝”效果。

4.2 UFW 规则部署:三步构建不可穿透的网络屏障

UFW 配置必须在 PostgreSQL 重启后立即执行,否则中间窗口期可能被扫描到。第一步,启用 UFW 并放行 SSH:

sudo ufw allow OpenSSH

第二步,放行 PostgreSQL 端口,但 只针对可信来源

# 放行本机(用于健康检查)
sudo ufw allow from 127.0.0.1 to any port 5432

# 放行内网应用服务器(假设 IP 是 192.168.1.100)
sudo ufw allow from 192.168.1.100 to any port 5432

# 放行运维跳板机(假设 IP 是 203.0.113.45)
sudo ufw allow from 203.0.113.45 to any port 5432

第三步,启用并验证:

sudo ufw enable
sudo ufw status verbose

输出应类似:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
5432/tcp                   ALLOW IN    127.0.0.1
5432/tcp                   ALLOW IN    192.168.1.100
5432/tcp                   ALLOW IN    203.0.113.45
22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

注意: 22/tcp (v6) 是 OpenSSH 自动添加的 IPv6 规则,无需担心。现在测试:从 192.168.1.100 执行 psql -h 192.168.1.50 -U appuser -d myapp ,应成功;从其他 IP(如 192.168.1.101 )执行相同命令,应超时或被拒绝。用 telnet 192.168.1.50 5432 测试,非白名单 IP 会显示 Connection refused ,这是 UFW 的 DROP 行为,比 REJECT 更隐蔽(不发 RST 包)。

4.3 创建最小权限业务用户:拒绝 postgres 账户的任何业务使用

绝不能用 postgres 系统用户跑业务应用!它拥有 SUPERUSER 权限,可执行 CREATE EXTENSION LOAD 动态库等高危操作。我们必须创建专用用户,并严格限制其权限。以 myapp 数据库为例:

-- 切换到 postgres 用户
sudo -u postgres psql

-- 创建数据库
CREATE DATABASE myapp OWNER appuser;

-- 创建用户,密码用 SCRAM 加密
CREATE USER appuser WITH PASSWORD 'Str0ngP@ssw0rd!2024';

-- 授予数据库连接权限
GRANT CONNECT ON DATABASE myapp TO appuser;

-- 连接到 myapp 数据库,授予 schema 使用权限
\c myapp
GRANT USAGE ON SCHEMA public TO appuser;

-- 授予表的 SELECT/INSERT/UPDATE/DELETE 权限(按需细化)
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser;

-- 设置新表的默认权限(避免未来建表后权限丢失)
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO appuser;

关键点: GRANT CONNECT 是必须的,否则用户无法连接数据库; GRANT USAGE ON SCHEMA 允许用户访问 schema 下的对象; ALL TABLES 是便捷写法,生产环境建议按表名精确授权,如 GRANT SELECT ON TABLE users TO appuser; 。最后,回收 postgres 用户的 PUBLIC 权限,防止意外泄露:

REVOKE CREATE ON SCHEMA public FROM PUBLIC;

这样,即使攻击者拿到了 postgres 用户的密码,也无法在 public schema 下创建恶意函数。

4.4 连接池层加固:PgBouncer 的轻量级兜底方案

当应用规模扩大,或需要更细粒度的连接控制时,PgBouncer 是必选项。它不是 PostgreSQL 的替代品,而是前置代理,能限制并发数、设置连接超时、提供审计日志。安装 PgBouncer:

sudo apt install -y pgbouncer

配置文件 /etc/pgbouncer/pgbouncer.ini 关键修改:

[databases]
myapp = host=127.0.0.1 port=5432 dbname=myapp

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 100
default_pool_size = 20

其中 auth_file 是用户密码文件,格式为 "username" "SCRAM-SHA-256$... ,需用 pgbouncer 工具生成:

echo 'appuser' | sudo pgbouncer -d /etc/pgbouncer/pgbouncer.ini --create-user

然后将生成的密码行复制到 /etc/pgbouncer/userlist.txt 。启动 PgBouncer:

sudo systemctl enable pgbouncer
sudo systemctl start pgbouncer

现在,应用应连接 127.0.0.1:6432 而非 5432 。PgBouncer 会将请求转发给 PostgreSQL,并在连接池层面限制:单个用户最多 20 个连接( default_pool_size ),全局最多 100 个( max_client_conn )。即使攻击者绕过 UFW 和 pg_hba.conf ,也会被 PgBouncer 的连接数限制卡住,无法耗尽数据库资源。

5. 常见问题与排查技巧实录:那些文档里不会写的实战陷阱

5.1 问题速查表:从连接失败到日志爆炸的 7 个高频故障

现象 可能原因 排查命令 解决方案
psql: error: connection to server at "192.168.1.50", port 5432 failed: Connection refused UFW 未启用或规则未生效 sudo ufw status verbose 确认 5432/tcp 规则存在且 Status: active
psql: error: connection to server at "192.168.1.50", port 5432 failed: FATAL: no pg_hba.conf entry for host "192.168.1.100", user "appuser", database "myapp", SSL off pg_hba.conf 无匹配规则或顺序错误 sudo cat /etc/postgresql/*/main/pg_hba.conf | grep 192.168.1.100 检查 IP 是否在 ADDRESS 字段,确认规则在 reject 之前
psql: error: connection to server at "127.0.0.1", port 5432 failed: FATAL: password authentication failed for user "appuser" 密码未用 SCRAM 加密或客户端不支持 sudo -u postgres psql -c "SELECT rolname, rolpassword FROM pg_authid WHERE rolname='appuser';" rolpassword md5 开头,执行 ALTER USER appuser WITH PASSWORD 'newpass';
FATAL: remaining connection slots are reserved for non-replication superuser connections 连接数超限, max_connections 被占满 sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity;" 增加 max_connections 或用 PgBouncer 限流
psql: error: server closed the connection unexpectedly postgresql.conf tcp_keepalives_idle 过短 sudo -u postgres psql -c "SHOW tcp_keepalives_idle;" 设为 600 (10 分钟)
ERROR: permission denied for schema public 业务用户无 USAGE 权限 sudo -u postgres psql -c "\c myapp; \du appuser" 执行 GRANT USAGE ON SCHEMA public TO appuser;
日志中大量 connection received: host=xxx.xxx.xxx.xxx port=xxxxx UFW 未启用,所有连接都抵达 PostgreSQL sudo tail -f /var/log/postgresql/*.log | grep "connection received" 立即启用 UFW 并添加 deny incoming 策略

5.2 实操心得:那些踩过的坑,现在告诉你怎么绕开

第一个坑: listen_addresses 不能写 * 0.0.0.0 。很多人图省事写 listen_addresses = '*' ,这会让 PostgreSQL 监听所有 IPv4 和 IPv6 接口,包括 Docker 的 docker0 网桥(172.17.0.1)。结果是,容器内任何应用都能直连宿主机数据库,完全绕过 UFW。正确做法是显式列出所需 IP,如 listen_addresses = '127.0.0.1,192.168.1.50' ,既明确又安全。

第二个坑: pg_hba.conf ADDRESS 字段不支持域名 。你不能写 host all all myapp-server.local md5 ,PostgreSQL 会报错 invalid IP address 。必须用 IP 或 CIDR。如果服务器 IP 经常变(如 DHCP),解决方案是:在 pg_hba.conf 中用 0.0.0.0/0 但配 reject ,然后在 UFW 层用动态 DNS 更新脚本维护白名单,比在 PostgreSQL 内部处理更可靠。

第三个坑: scram-sha-256 密码在 pg_shadow 中不可读,但 pg_dumpall --globals 会导出明文 。这意味着,如果你用 pg_dumpall 备份并上传到公共仓库, appuser 的密码会以明文形式泄露。解决方案是:备份时排除全局对象 pg_dumpall --globals-only --exclude-database=template0 > globals.sql ,或用 pg_dump 分库备份,绝不导出 pg_authid 表。

第四个坑:Docker 环境下 UFW 无效 。Docker 默认创建自己的 iptables 链,会绕过 UFW 规则。解决方法是:在 docker run 时加 --network host ,让容器共享宿主机网络,UFW 规则即可生效;或在 docker-compose.yml 中设 network_mode: "host" 。如果必须用 bridge 网络,则需在宿主机上用 iptables 直接添加规则,UFW 不适用。

5.3 日志审计与攻击溯源:如何从日志中揪出真实攻击者

PostgreSQL 默认日志不记录客户端 IP 的详细信息,需主动增强。编辑 postgresql.conf

log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'none'  # 不记录 SQL,避免日志爆炸
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_min_duration_statement = 1000  # 记录执行超 1 秒的 SQL

重启后,日志中每行开头类似:

2024-05-20 14:23:45 CST [12345]: [1-1] user=appuser,db=myapp,app=psql,client=192.168.1.100 

当发现可疑 IP(如 203.0.113.200 )频繁连接失败,可提取其所有日志:

sudo grep "client=203.0.113.200" /var/log/postgresql/*.log

若看到 FATAL: password authentication failed 连续出现 50 次,基本可判定是暴力扫描。此时,用 UFW 永久封禁:

sudo ufw insert 1 deny from 203.0.113.200

insert 1 表示插入到规则列表第一位,确保优先匹配。封禁后,该 IP 的所有流量(不仅是 5432 端口)都会被 DROP,极大增加攻击者成本。

5.4 安全基线自检清单:每次上线前必须执行的 5 项验证

  1. UFW 状态检查 sudo ufw status verbose | grep -E "(5432|Status)" —— 确认 Status: active 5432/tcp 规则存在。
  2. pg_hba.conf 规则检查 sudo grep -v "^#" /etc/postgresql/*/main/pg_hba.conf | grep -E "(local|host)" | head -10 —— 确认无 trust 规则,且 reject 在末尾。
  3. 密码加密检查 sudo -u postgres psql -c "SHOW password_encryption;" —— 必须返回 scram-sha-256
  4. 用户权限检查 sudo -u postgres psql -c "\du appuser" —— 确认无 Superuser Create role 等高危权限。
  5. 连接测试 :从白名单 IP 执行 psql -h 192.168.1.50 -U appuser -d myapp -c "SELECT version();" —— 应成功返回版本号;从非白名单 IP 执行相同命令 —— 应超时或被拒绝。

这五项检查可在 2 分钟内完成,我把它写成一个 Bash 脚本,每次部署新实例前自动运行,从未漏过一个配置错误。

6. 后续可扩展方向:从基础防护到企业级安全治理

这套方案解决了“自动化攻击”的核心痛点,但安全是持续过程。下一步可考虑:

  • 审计日志集中化 :用 Filebeat 将 PostgreSQL 日志推送到 ELK Stack,设置告警规则,如“1 小时内同一 IP 失败登录超 10 次”自动触发封禁。
  • TLS 加密强制 :为 hostssl 规则配置证书,确保传输层加密,防止中间人窃取密码。
  • 动态凭证管理 :集成 HashiCorp Vault,应用启动时动态获取数据库密码,密码有效期设为 1 小时,彻底消除硬编码风险。
  • 漏洞自动扫描 :用 pgcenter 或自定义脚本定期检查 pg_settings 中的 log_* 参数是否启用, shared_preload_libraries 是否含未知扩展。

但所有这些,都建立在本文所述的四层基础之上。没有扎实的 UFW、 pg_hba.conf 、密码策略和最小权限,上层建筑再华丽也是沙上之塔。我坚持一个原则: 先让 99% 的自动化攻击在 15 秒内放弃,再谈 1% 的高级持续性威胁 。毕竟,对抗自动化,拼的不是技术多炫酷,而是谁的配置更“无聊”——无聊到攻击脚本扫一眼就走,这才是真正的安全。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐