PostgreSQL:如何配置数据库的传输层加密(SSL加密连接)

文章目录

      • [一、为什么需要 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. psql 命令行连接](#1. psql 命令行连接)
        • [2. 应用程序连接(以 Python psycopg2 为例)](#2. 应用程序连接(以 Python psycopg2 为例))
        • [3. JDBC 连接(Java)](#3. JDBC 连接(Java))
      • [六、SSL 模式(sslmode)详解](#六、SSL 模式(sslmode)详解)
      • [七、高级配置:双向 SSL(客户端证书认证)](#七、高级配置:双向 SSL(客户端证书认证))
      • [八、验证 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:证书过期)
      • 十一、实践建议
      • 十二、自动化脚本示例(生成自签名证书)

在 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+ 推荐)。流程如下:

  1. 客户端发起 SSL 连接请求;
  2. 服务端返回其 SSL 证书;
  3. 客户端验证证书(可选);
  4. 双方协商加密套件,建立加密通道;
  5. 后续所有通信均通过该通道加密传输。

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(生产环境推荐)
  1. 申请域名证书(如 db.example.com);
  2. 将私钥保存为 $PGDATA/server.key
  3. 将证书链保存为 $PGDATA/server.crt
  4. 将 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 但不强制",可同时保留 hosthostssl,但客户端需显式请求 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  # 使用客户端证书认证
客户端准备
  1. 为每个客户端生成证书(由同一 CA 签发);
  2. 连接时提供:
    • 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.2TLSv1.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 与主机名不匹配;
  • 解决
    • 确保证书包含正确的 CNsubjectAltName
    • 或临时使用 sslmode=require(仅测试)。
问题 3:私钥权限错误
  • 错误private key file "server.key" has group or world access

  • 解决

    bash 复制代码
    chmod 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 连接

相关推荐
山岚的运维笔记2 小时前
SQL Server笔记 -- 第55章:外键
数据库·笔记·sql·microsoft·sqlserver
wsfk12342 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
unirst19850072 小时前
Mysql官网下载Windows、Linux各个版本
linux·数据库·mysql
Elastic 中国社区官方博客2 小时前
Elasticsearch:创建 tavily 网页搜索 workflow 及在 agent 中使用它
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
源力祁老师2 小时前
Odoo ORM 将 Python 查询意图编译为 SQL 的逐函数讲解(Odoo 19)
java·服务器·数据库
那我掉的头发算什么2 小时前
【图书管理系统】基于Spring全家桶的图书管理系统(上)
java·服务器·数据库·spring boot·后端·spring·mybatis
shalou29012 小时前
MySQL数据库的数据文件保存在哪?MySQL数据存在哪里
数据库·mysql
byte轻骑兵2 小时前
大数据场景时序数据库选型指南——Apache IoTDB实践与解析
大数据·数据库·apache·时序数据库·iotdb
数据与人2 小时前
MySQL int(10) 与 int(11) 的区别
数据库·mysql