文章目录
-
-
- [一、为什么需要 SSL 加密?](#一、为什么需要 SSL 加密?)
- [二、SSL 工作原理简述](#二、SSL 工作原理简述)
- [三、准备工作:生成 SSL 证书](#三、准备工作:生成 SSL 证书)
-
- [方式 1:使用 OpenSSL 生成自签名证书(推荐用于测试/内网)](#方式 1:使用 OpenSSL 生成自签名证书(推荐用于测试/内网))
- [方式 2:使用 Let's Encrypt 或企业 CA(生产环境推荐)](#方式 2:使用 Let's Encrypt 或企业 CA(生产环境推荐))
- [四、服务端配置(postgresql.conf + pg_hba.conf)](#四、服务端配置(postgresql.conf + pg_hba.conf))
-
- [步骤 1:启用 SSL(编辑 `postgresql.conf`)](#步骤 1:启用 SSL(编辑
postgresql.conf)) - [步骤 2:配置客户端认证(编辑 `pg_hba.conf`)](#步骤 2:配置客户端认证(编辑
pg_hba.conf))
- [步骤 1:启用 SSL(编辑 `postgresql.conf`)](#步骤 1:启用 SSL(编辑
- 五、客户端连接方式
-
- [1. psql 命令行连接](#1. psql 命令行连接)
- [2. 应用程序连接(以 Python psycopg2 为例)](#2. 应用程序连接(以 Python psycopg2 为例))
- [3. JDBC 连接(Java)](#3. JDBC 连接(Java))
- [六、SSL 模式(sslmode)详解](#六、SSL 模式(sslmode)详解)
- [七、高级配置:双向 SSL(客户端证书认证)](#七、高级配置:双向 SSL(客户端证书认证))
-
- 服务端配置(postgresql.conf)
- [pg_hba.conf 配置](#pg_hba.conf 配置)
- 客户端准备
- [八、验证 SSL 是否生效](#八、验证 SSL 是否生效)
-
- [方法 1:查看连接状态](#方法 1:查看连接状态)
- [方法 2:抓包验证](#方法 2:抓包验证)
- [方法 3:日志检查](#方法 3:日志检查)
- 九、性能影响与优化
-
- [1. 性能开销](#1. 性能开销)
- [2. 优化建议](#2. 优化建议)
- 十、常见问题排查
-
- [问题 1:`FATAL: no pg_hba.conf entry for host ... SSL off`](#问题 1:
FATAL: no pg_hba.conf entry for host ... SSL off) - [问题 2:`certificate verify failed`](#问题 2:
certificate verify failed) - [问题 3:私钥权限错误](#问题 3:私钥权限错误)
- [问题 4:证书过期](#问题 4:证书过期)
- [问题 1:`FATAL: no pg_hba.conf entry for host ... SSL off`](#问题 1:
- 十一、实践建议
- 十二、自动化脚本示例(生成自签名证书)
-
在 PostgreSQL 中配置传输层加密(SSL/TLS)是保障数据库通信安全的核心措施,可有效防止中间人攻击(MitM)、数据窃听和篡改。本文将系统性地详解 如何从零开始配置 PostgreSQL 的 SSL 加密连接,涵盖证书生成、服务端配置、客户端连接、验证模式、性能影响及最佳实践,适用于 PostgreSQL 10 及以上版本。
一、为什么需要 SSL 加密?
PostgreSQL 默认以明文传输所有数据(包括用户名、密码、SQL 语句、查询结果)。在以下场景中,必须启用 SSL:
- 数据库与应用部署在不同服务器(跨网络);
- 使用公共云或共享网络环境;
- 满足合规要求(如 GDPR、PCI-DSS、等保 2.0)。
注意:SSL 仅加密传输中的数据(in-transit),不加密存储数据(需配合透明数据加密 TDE 或文件系统加密)。
二、SSL 工作原理简述
PostgreSQL 支持标准 TLS 协议(v1.2+ 推荐)。流程如下:
- 客户端发起 SSL 连接请求;
- 服务端返回其 SSL 证书;
- 客户端验证证书(可选);
- 双方协商加密套件,建立加密通道;
- 后续所有通信均通过该通道加密传输。
PostgreSQL 支持两种证书模式:
- 自签名证书:快速部署,适合测试或内网;
- CA 签发证书:由可信第三方(如 Let's Encrypt、企业 CA)签发,适合生产环境。
三、准备工作:生成 SSL 证书
方式 1:使用 OpenSSL 生成自签名证书(推荐用于测试/内网)
bash
# 进入 PostgreSQL 数据目录(通常为 /var/lib/pgsql/data 或 /usr/local/pgsql/data)
cd $PGDATA
# 生成私钥(server.key)
openssl genrsa -out server.key 2048
# 设置私钥权限(仅属主可读)
chmod 600 server.key
chown postgres:postgres server.key # 假设运行用户为 postgres
# 生成证书签名请求(CSR)
openssl req -new -key server.key -out server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=your-db-hostname"
# 生成自签名证书(有效期 3650 天 ≈ 10 年)
openssl x509 -req -in server.csr -signkey server.key -out server.crt -days 3650
# (可选)生成根证书(用于客户端验证)
cp server.crt root.crt
关键参数说明:
CN(Common Name)必须与客户端连接时使用的主机名一致,否则证书验证失败;- 若使用 IP 地址连接,需在 SAN(Subject Alternative Name)中添加 IP,或直接用 IP 作为 CN(不推荐)。
方式 2:使用 Let's Encrypt 或企业 CA(生产环境推荐)
- 申请域名证书(如
db.example.com); - 将私钥保存为
$PGDATA/server.key; - 将证书链保存为
$PGDATA/server.crt; - 将 CA 根证书保存为
$PGDATA/root.crt(用于客户端验证)。
四、服务端配置(postgresql.conf + pg_hba.conf)
步骤 1:启用 SSL(编辑 postgresql.conf)
conf
# 启用 SSL
ssl = on
# 指定证书和私钥路径(默认即为 data 目录下)
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
# (可选)指定 CA 证书(用于客户端证书验证)
# ssl_ca_file = 'root.crt'
# (可选)指定 CRL(证书吊销列表)
# ssl_crl_file = 'root.crl'
# 强制使用 TLS 1.2+(PostgreSQL 12+)
ssl_min_protocol_version = 'TLSv1.2'
ssl_max_protocol_version = 'TLSv1.3'
# 禁用弱加密套件(推荐)
ssl_ciphers = 'HIGH:!aNULL:!MD5'
注意:
- 修改后需重启 PostgreSQL 生效(
pg_ctl restart);- 若私钥有密码,PostgreSQL 启动时会提示输入(生产环境应避免密码,使用文件权限控制)。
步骤 2:配置客户端认证(编辑 pg_hba.conf)
pg_hba.conf 控制哪些连接允许使用 SSL 及验证级别。
示例:强制所有远程连接使用 SSL
conf
# TYPE DATABASE USER ADDRESS METHOD
hostssl all all 0.0.0.0/0 scram-sha-256
host all all 127.0.0.1/32 trust
关键说明:
hostssl:仅接受 SSL 加密连接;host:接受非 SSL 连接(应避免在公网开放);- 若希望"允许 SSL 但不强制",可同时保留
host和hostssl,但客户端需显式请求 SSL。
五、客户端连接方式
1. psql 命令行连接
bash
# 显式启用 SSL(若服务端配置 hostssl,则必须)
psql "host=db.example.com port=5432 dbname=mydb user=myuser sslmode=require"
# 验证服务器证书(需提供 root.crt)
psql "host=db.example.com ... sslmode=verify-full sslrootcert=/path/to/root.crt"
2. 应用程序连接(以 Python psycopg2 为例)
python
import psycopg2
conn = psycopg2.connect(
host="db.example.com",
port=5432,
database="mydb",
user="myuser",
password="xxx",
sslmode="require" # 或 "verify-full"
)
3. JDBC 连接(Java)
java
String url = "jdbc:postgresql://db.example.com:5432/mydb?sslmode=require";
Connection conn = DriverManager.getConnection(url, "myuser", "xxx");
六、SSL 模式(sslmode)详解
| sslmode | 行为 | 安全性 | 适用场景 |
|---|---|---|---|
disable |
不使用 SSL | ❌ 极低 | 本地测试 |
allow |
先尝试非 SSL,失败再试 SSL | ⚠️ 低 | 兼容旧系统 |
prefer |
先尝试 SSL,失败回退非 SSL | ⚠️ 中 | 默认值(不推荐生产) |
require |
强制 SSL,但不验证证书 | ✅ 中 | 内网/自签名证书 |
verify-ca |
SSL + 验证证书由可信 CA 签发 | ✅ 高 | 企业 CA 环境 |
verify-full |
SSL + 验证 CA + 验证主机名匹配 | ✅✅ 最高 | 公网/高安全要求 |
生产环境强烈建议使用
verify-full,并配合有效 CA 证书。
七、高级配置:双向 SSL(客户端证书认证)
除服务端认证外,PostgreSQL 还支持客户端证书认证(mTLS),实现双向身份验证。
服务端配置(postgresql.conf)
conf
ssl_ca_file = 'root.crt' # 包含客户端证书的 CA
pg_hba.conf 配置
conf
hostssl all all 0.0.0.0/0 cert # 使用客户端证书认证
客户端准备
- 为每个客户端生成证书(由同一 CA 签发);
- 连接时提供:
sslcert:客户端证书(client.crt)sslkey:客户端私钥(client.key)sslrootcert:CA 根证书(root.crt)
bash
psql "host=db.example.com ... sslmode=verify-full sslcert=client.crt sslkey=client.key sslrootcert=root.crt"
优势:无需密码,基于证书的身份认证;
缺点:证书管理复杂,适合机器对机器(M2M)场景。
八、验证 SSL 是否生效
方法 1:查看连接状态
sql
SELECT pid, usename, application_name, client_addr, ssl
FROM pg_stat_ssl
JOIN pg_stat_activity ON pg_stat_ssl.pid = pg_stat_activity.pid;
ssl = t表示该连接已启用 SSL。
方法 2:抓包验证
bash
tcpdump -i any -w pg.pcap port 5432
用 Wireshark 打开,若看到 TLSv1.2 或 TLSv1.3 记录,而非明文 SQL,则成功。
方法 3:日志检查
在 postgresql.conf 中启用:
conf
log_connections = on
log_hostname = on
成功 SSL 连接日志示例:
LOG: connection authorized: user=myuser database=mydb SSL enabled (protocol=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384, compression=off)
九、性能影响与优化
1. 性能开销
- CPU 开销:SSL 握手约增加 10--30% CPU 使用率;
- 延迟:首次连接增加 1--2 个 RTT(可通过会话复用缓解)。
2. 优化建议
- 启用 SSL 会话缓存(PostgreSQL 自动支持);
- 使用高效加密套件(如 ECDHE + AES-GCM);
- 避免频繁短连接(使用连接池如 PgBouncer);
- 硬件加速:支持 AES-NI 的 CPU 可显著降低开销。
实测:在现代服务器上,SSL 对 OLTP 查询的吞吐影响通常 < 5%。
十、常见问题排查
问题 1:FATAL: no pg_hba.conf entry for host ... SSL off
- 原因 :客户端未请求 SSL,但
pg_hba.conf只配置了hostssl; - 解决 :客户端使用
sslmode=require,或服务端添加host规则(不推荐)。
问题 2:certificate verify failed
- 原因 :客户端使用
verify-full,但证书 CN/SAN 与主机名不匹配; - 解决 :
- 确保证书包含正确的
CN或subjectAltName; - 或临时使用
sslmode=require(仅测试)。
- 确保证书包含正确的
问题 3:私钥权限错误
-
错误 :
private key file "server.key" has group or world access -
解决 :
bashchmod 600 server.key chown postgres:postgres server.key
问题 4:证书过期
- 现象 :连接突然失败,日志提示
certificate expired; - 解决 :更新证书并重载 PostgreSQL(
pg_ctl reload)。
十一、实践建议
| 项目 | 推荐做法 |
|---|---|
| 证书类型 | 生产环境使用 CA 签发证书(如 Let's Encrypt) |
| SSL 模式 | 客户端使用 sslmode=verify-full |
| 协议版本 | 禁用 TLS 1.0/1.1,仅启用 TLS 1.2+ |
| 加密套件 | 使用 HIGH:!aNULL:!MD5 |
| 连接控制 | pg_hba.conf 仅开放 hostssl |
| 监控 | 定期检查 pg_stat_ssl 和证书有效期 |
| 备份 | 安全备份 server.key(加密存储) |
十二、自动化脚本示例(生成自签名证书)
bash
#!/bin/bash
# generate-ssl.sh
set -e
PGDATA=${PGDATA:-/var/lib/pgsql/data}
HOSTNAME=${1:-$(hostname)}
cd "$PGDATA"
openssl genrsa -out server.key 2048
chmod 600 server.key
openssl req -new -key server.key -out server.csr \
-subj "/C=CN/ST=State/L=City/O=Org/CN=$HOSTNAME"
openssl x509 -req -in server.csr -signkey server.key -out server.crt -days 3650
cp server.crt root.crt
echo "SSL certificates generated in $PGDATA"
echo "Remember to set ssl=on in postgresql.conf and restart PostgreSQL."
总结:配置 PostgreSQL SSL 加密是数据库安全的基石。虽然初期需处理证书管理和配置细节,但一旦正确部署,即可在几乎不影响性能的前提下,大幅提升通信安全性。永远不要在公网或不可信网络中使用未加密的 PostgreSQL 连接。