Skip to content

PostgreSQL SSL/TLS 安全连接完全指南

概述

PostgreSQL 原生支持使用 SSL/TLS 连接来加密客户端与服务器之间的通信,这是保障数据库安全的重要手段。在现代企业环境中,数据传输安全已成为合规性要求的重要组成部分。

SSL vs TLS 术语 SSL 和 TLS 通常可以互换使用。实际上,SSL 协议是 TLS 协议的前身,现代系统都使用 TLS 协议,但习惯上仍称为 SSL 连接。PostgreSQL 中 SSL 与 TLS 可互换使用。 :::

SSL/TLS 连接架构图

1. 基本设置与配置

1.1 启用 SSL 支持

业务场景:在生产环境中,金融、医疗等行业必须启用数据传输加密以满足合规要求。

配置步骤

第一步:修改 postgresql.conf

ini
# 启用 SSL 支持
ssl = on

# 指定证书文件路径(可选,默认在数据目录)
ssl_cert_file = '/etc/postgresql/ssl/server.crt'
ssl_key_file = '/etc/postgresql/ssl/server.key'

# 客户端证书验证(可选)
ssl_ca_file = '/etc/postgresql/ssl/ca.crt'
ssl_crl_file = '/etc/postgresql/ssl/server.crl'

# 密码套件配置
ssl_ciphers = 'HIGH:!aNULL:!3DES'
ssl_prefer_server_ciphers = on

第二步:设置证书文件权限

bash
# 确保私钥文件安全权限
chmod 0600 /etc/postgresql/ssl/server.key

# 或者设置为 root 拥有,组可读(适合系统管理证书的环境)
chown root:postgres /etc/postgresql/ssl/server.key
chmod 0640 /etc/postgresql/ssl/server.key

# 证书文件可以设置为较宽松的权限
chmod 0644 /etc/postgresql/ssl/server.crt

安全注意事项

  • 私钥文件权限必须严格控制,禁止任何对世界或组的访问
  • 如果数据目录允许组读取访问权限,证书文件应位于数据目录之外
  • 受密码保护的私钥在 Windows 上无法使用 :::

1.2 连接验证示例

客户端连接测试

bash
# 使用 psql 测试 SSL 连接
psql "host=mydb.example.com port=5432 dbname=myapp user=appuser sslmode=require"

# 验证连接使用的 SSL 信息
\conninfo

预期输出

You are connected to database "myapp" as user "appuser" on host "mydb.example.com" at port "5432".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)

程序化连接示例

python
import psycopg2

# SSL 连接配置
conn_params = {
    'host': 'mydb.example.com',
    'port': 5432,
    'database': 'myapp',
    'user': 'appuser',
    'password': 'mypassword',
    'sslmode': 'require',  # 要求 SSL 连接
    'sslcert': '/path/to/client.crt',  # 客户端证书(可选)
    'sslkey': '/path/to/client.key',   # 客户端私钥(可选)
    'sslrootcert': '/path/to/ca.crt'   # CA 根证书
}

try:
    # 建立 SSL 连接
    conn = psycopg2.connect(**conn_params)
    cursor = conn.cursor()

    # 验证 SSL 连接状态
    cursor.execute("SELECT version(), current_setting('ssl')")
    result = cursor.fetchone()
    print(f"PostgreSQL 版本: {result[0]}")
    print(f"SSL 状态: {result[1]}")

except psycopg2.Error as e:
    print(f"连接失败: {e}")
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.ResultSet;

public class PostgreSQLSSLConnection {
    public static void main(String[] args) {
        // SSL 连接 URL
        String url = "jdbc:postgresql://mydb.example.com:5432/myapp" +
                     "?ssl=true&sslmode=require" +
                     "&sslcert=/path/to/client.crt" +
                     "&sslkey=/path/to/client.key" +
                     "&sslrootcert=/path/to/ca.crt";

        String username = "appuser";
        String password = "mypassword";

        try (Connection conn = DriverManager.getConnection(url, username, password)) {
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(
                "SELECT version(), current_setting('ssl')"
            );

            if (rs.next()) {
                System.out.println("PostgreSQL 版本: " + rs.getString(1));
                System.out.println("SSL 状态: " + rs.getString(2));
            }
        } catch (Exception e) {
            System.err.println("连接失败: " + e.getMessage());
        }
    }
}

2. OpenSSL 配置优化

2.1 系统级 OpenSSL 配置

业务场景:企业环境需要统一的加密策略,通过 OpenSSL 配置文件可以全局控制加密算法和安全策略。

查看当前 OpenSSL 配置

bash
# 查看 OpenSSL 版本和配置目录
openssl version -d

# 查看支持的密码套件
openssl ciphers -v 'HIGH:!aNULL:!3DES' | head -10

输出分析

OPENSSLDIR: "/etc/ssl"

ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD

2.2 PostgreSQL 专用密码配置

ini
# postgresql.conf 中的高级 SSL 配置

# 推荐的生产环境密码套件(优先考虑安全性)
ssl_ciphers = 'ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS'

# 服务器密码优先(让服务器选择最安全的密码)
ssl_prefer_server_ciphers = on

# 启用椭圆曲线加密(提高性能)
ssl_ecdh_curve = 'prime256v1'

# 最低 TLS 版本(安全考虑)
ssl_min_protocol_version = 'TLSv1.2'

密码套件选择建议

  • ECDHE:椭圆曲线迪菲-赫尔曼密钥交换,提供前向安全性
  • AESGCM:AES 加密的 GCM 模式,提供认证加密
  • CHACHA20:现代流密码,在某些硬件上性能更好
  • !aNULL:禁用匿名密码(无认证) :::

3. 客户端证书认证

3.1 双向认证配置

业务场景:高安全要求的环境(如银行核心系统)需要双向认证,不仅验证服务器身份,还要验证客户端身份。

服务器端配置

配置 pg_hba.conf

bash
# pg_hba.conf 配置示例

# 要求客户端证书验证的连接
hostssl  myapp        appuser        192.168.1.0/24    cert

# 验证客户端证书但使用密码认证
hostssl  myapp        appuser        192.168.1.0/24    md5 clientcert=verify-ca

# 完整验证:证书链 + 用户名匹配
hostssl  myapp        appuser        192.168.1.0/24    md5 clientcert=verify-full

配置选项说明

选项说明使用场景
cert仅使用证书认证,不需要密码自动化系统间通信
clientcert=verify-ca验证证书有效性,但不检查用户名一般安全要求
clientcert=verify-full验证证书并检查 CN 与用户名匹配高安全要求

客户端证书生成示例

bash
# 1. 生成客户端私钥
openssl genrsa -out client.key 2048
chmod 0600 client.key

# 2. 创建证书签名请求
openssl req -new -key client.key -out client.csr \
    -subj "/CN=appuser/O=MyCompany/C=CN"

# 3. 使用 CA 签署客户端证书
openssl x509 -req -in client.csr -days 365 \
    -CA ca.crt -CAkey ca.key -CAcreateserial \
    -out client.crt

# 4. 验证证书
openssl verify -CAfile ca.crt client.crt

3.2 证书吊销列表 (CRL) 管理

业务场景:当员工离职或客户端密钥泄露时,需要及时吊销相应证书。

CRL 创建和管理

bash
# 1. 创建空的 CRL 文件
openssl ca -gencrl -out server.crl -crldays 30 \
    -cert ca.crt -keyfile ca.key

# 2. 吊销特定证书
openssl ca -revoke client.crt -cert ca.crt -keyfile ca.key

# 3. 更新 CRL
openssl ca -gencrl -out server.crl -crldays 30 \
    -cert ca.crt -keyfile ca.key

# 4. 查看 CRL 内容
openssl crl -in server.crl -text -noout

PostgreSQL CRL 配置

ini
# postgresql.conf
ssl_crl_file = '/etc/postgresql/ssl/server.crl'

# 或使用目录方式
ssl_crl_dir = '/etc/postgresql/ssl/crl'

4. SSL 服务器文件管理

4.1 文件结构和作用

4.2 文件用途详细说明

文件类型默认位置作用安全要求
server.crt$PGDATA/server.crt服务器证书,向客户端证明服务器身份644 权限,可公开
server.key$PGDATA/server.key服务器私钥,证明证书所有权600 权限,严格保密
ca.crtssl_ca_file 配置受信任的 CA 根证书644 权限,可公开
server.crlssl_crl_file 配置证书吊销列表644 权限,定期更新

4.3 证书链配置示例

业务场景:企业使用多级 CA 结构,需要配置包含中间证书的完整证书链。

创建证书链文件

bash
# 1. 合并服务器证书和中间证书
cat server.crt intermediate.crt > server-chain.crt

# 2. 验证证书链
openssl verify -CAfile root.crt -untrusted intermediate.crt server.crt

# 3. 配置 PostgreSQL 使用证书链
echo "ssl_cert_file = '/etc/postgresql/ssl/server-chain.crt'" >> postgresql.conf

证书链验证脚本

bash
#!/bin/bash
# check_ssl_chain.sh - 验证 SSL 证书链

CERT_FILE="/etc/postgresql/ssl/server-chain.crt"
CA_FILE="/etc/postgresql/ssl/ca.crt"

echo "=== 验证证书链 ==="

# 检查证书文件存在性
if [[ ! -f "$CERT_FILE" ]]; then
    echo "错误: 证书文件不存在: $CERT_FILE"
    exit 1
fi

# 检查证书有效期
echo "1. 检查证书有效期:"
openssl x509 -in "$CERT_FILE" -noout -dates

# 检查证书主题和颁发者
echo -e "\n2. 证书主题信息:"
openssl x509 -in "$CERT_FILE" -noout -subject -issuer

# 验证证书链
echo -e "\n3. 验证证书链:"
if openssl verify -CAfile "$CA_FILE" "$CERT_FILE"; then
    echo "✓ 证书链验证成功"
else
    echo "✗ 证书链验证失败"
    exit 1
fi

# 检查私钥匹配
echo -e "\n4. 验证私钥匹配:"
CERT_MODULUS=$(openssl x509 -in "$CERT_FILE" -noout -modulus | md5sum)
KEY_MODULUS=$(openssl rsa -in "/etc/postgresql/ssl/server.key" -noout -modulus | md5sum)

if [[ "$CERT_MODULUS" == "$KEY_MODULUS" ]]; then
    echo "✓ 证书与私钥匹配"
else
    echo "✗ 证书与私钥不匹配"
    exit 1
fi

echo -e "\n=== SSL 配置验证完成 ==="

5. 证书生成与管理

5.1 生产环境证书生成

业务场景:为生产环境创建可信的 SSL 证书体系,包括 CA、中间证书和服务器证书。

完整证书生成流程

第一步:创建根 CA

bash
#!/bin/bash
# create_root_ca.sh - 创建根证书颁发机构

# 1. 生成根 CA 私钥
openssl genrsa -aes256 -out root-ca.key 4096
chmod 0400 root-ca.key

# 2. 创建根 CA 证书
openssl req -new -x509 -days 3650 -key root-ca.key -out root-ca.crt \
    -subj "/CN=MyCompany Root CA/O=MyCompany/C=CN" \
    -extensions v3_ca

# 3. 验证根证书
openssl x509 -in root-ca.crt -text -noout | head -20

第二步:创建中间 CA

bash
#!/bin/bash
# create_intermediate_ca.sh - 创建中间证书颁发机构

# 1. 生成中间 CA 私钥
openssl genrsa -aes256 -out intermediate-ca.key 4096
chmod 0400 intermediate-ca.key

# 2. 创建中间 CA 证书请求
openssl req -new -key intermediate-ca.key -out intermediate-ca.csr \
    -subj "/CN=MyCompany Intermediate CA/O=MyCompany/C=CN"

# 3. 使用根 CA 签署中间证书
openssl x509 -req -in intermediate-ca.csr -days 1825 \
    -CA root-ca.crt -CAkey root-ca.key -CAcreateserial \
    -out intermediate-ca.crt -extensions v3_ca \
    -extfile /etc/ssl/openssl.cnf

# 4. 创建证书链文件
cat intermediate-ca.crt root-ca.crt > ca-chain.crt

第三步:生成服务器证书

bash
#!/bin/bash
# create_server_cert.sh - 创建服务器证书

SERVER_NAME="mydb.example.com"

# 1. 生成服务器私钥
openssl genrsa -out server.key 2048
chmod 0600 server.key

# 2. 创建服务器证书请求
openssl req -new -key server.key -out server.csr \
    -subj "/CN=${SERVER_NAME}/O=MyCompany/C=CN"

# 3. 创建证书扩展文件
cat > server.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = ${SERVER_NAME}
DNS.2 = *.${SERVER_NAME}
IP.1 = 192.168.1.100
EOF

# 4. 使用中间 CA 签署服务器证书
openssl x509 -req -in server.csr -days 365 \
    -CA intermediate-ca.crt -CAkey intermediate-ca.key -CAcreateserial \
    -out server.crt -extensions v3_req -extfile server.ext

# 5. 创建完整的服务器证书链
cat server.crt intermediate-ca.crt > server-chain.crt

5.2 证书自动化管理

业务场景:大型环境中需要自动化证书更新,避免证书过期导致的服务中断。

证书监控脚本

bash
#!/bin/bash
# monitor_certificates.sh - 监控证书有效期

CERT_DIR="/etc/postgresql/ssl"
WARNING_DAYS=30
CRITICAL_DAYS=7

check_certificate() {
    local cert_file="$1"
    local cert_name="$2"

    if [[ ! -f "$cert_file" ]]; then
        echo "ERROR: 证书文件不存在: $cert_file"
        return 1
    fi

    # 获取证书过期时间
    expire_date=$(openssl x509 -in "$cert_file" -noout -enddate | cut -d= -f2)
    expire_timestamp=$(date -d "$expire_date" +%s)
    current_timestamp=$(date +%s)
    days_left=$(( (expire_timestamp - current_timestamp) / 86400 ))

    echo "证书: $cert_name"
    echo "文件: $cert_file"
    echo "过期时间: $expire_date"
    echo "剩余天数: $days_left 天"

    if [[ $days_left -le $CRITICAL_DAYS ]]; then
        echo "🔴 紧急:证书即将过期!"
        return 2
    elif [[ $days_left -le $WARNING_DAYS ]]; then
        echo "🟡 警告:证书即将过期"
        return 1
    else
        echo "✅ 证书状态正常"
        return 0
    fi
    echo "---"
}

# 检查所有证书
check_certificate "$CERT_DIR/server.crt" "服务器证书"
check_certificate "$CERT_DIR/ca.crt" "CA 根证书"
check_certificate "$CERT_DIR/intermediate.crt" "中间证书"

自动更新证书脚本

bash
#!/bin/bash
# auto_renew_certificate.sh - 自动更新证书

CERT_DIR="/etc/postgresql/ssl"
BACKUP_DIR="/etc/postgresql/ssl/backup"
SERVER_NAME="mydb.example.com"

# 创建备份目录
mkdir -p "$BACKUP_DIR"

# 备份当前证书
backup_certificates() {
    local timestamp=$(date +%Y%m%d_%H%M%S)
    cp "$CERT_DIR/server.crt" "$BACKUP_DIR/server.crt.$timestamp"
    cp "$CERT_DIR/server.key" "$BACKUP_DIR/server.key.$timestamp"
    echo "证书备份完成: $timestamp"
}

# 生成新证书
generate_new_certificate() {
    echo "生成新的服务器证书..."

    # 生成新私钥
    openssl genrsa -out "$CERT_DIR/server.key.new" 2048
    chmod 0600 "$CERT_DIR/server.key.new"

    # 创建证书请求
    openssl req -new -key "$CERT_DIR/server.key.new" -out "$CERT_DIR/server.csr" \
        -subj "/CN=${SERVER_NAME}/O=MyCompany/C=CN"

    # 使用 CA 签署新证书
    openssl x509 -req -in "$CERT_DIR/server.csr" -days 365 \
        -CA "$CERT_DIR/intermediate-ca.crt" \
        -CAkey "$CERT_DIR/intermediate-ca.key" \
        -CAcreateserial \
        -out "$CERT_DIR/server.crt.new"

    # 验证新证书
    if openssl verify -CAfile "$CERT_DIR/ca-chain.crt" "$CERT_DIR/server.crt.new"; then
        echo "新证书验证成功"
        return 0
    else
        echo "新证书验证失败"
        return 1
    fi
}

# 应用新证书
apply_new_certificate() {
    echo "应用新证书..."

    # 原子性替换证书文件
    mv "$CERT_DIR/server.crt.new" "$CERT_DIR/server.crt"
    mv "$CERT_DIR/server.key.new" "$CERT_DIR/server.key"

    # 重新加载 PostgreSQL 配置
    sudo systemctl reload postgresql

    echo "证书更新完成"
}

# 主流程
main() {
    echo "开始证书更新流程..."

    backup_certificates

    if generate_new_certificate; then
        apply_new_certificate
        echo "证书更新成功"
    else
        echo "证书更新失败,请检查日志"
        exit 1
    fi
}

main

6. 最佳实践与故障排除

6.1 安全最佳实践

生产环境 SSL 配置清单 ✅ 证书管理
  • 使用商业 CA 或企业内部 CA 签发证书
  • 证书有效期不超过 2 年
  • 配置证书自动更新机制
  • 定期备份证书和私钥

权限控制

  • 私钥文件权限设置为 600
  • 证书文件归属正确用户
  • 使用专用证书目录

网络安全

  • 禁用不安全的密码套件
  • 强制使用 TLS 1.2 以上版本
  • 启用服务器密码优先级

监控告警

  • 配置证书过期监控
  • 监控 SSL 连接失败率
  • 记录 SSL 握手错误 :::

6.2 常见问题排除

问题 1:SSL 连接失败

错误信息

psql: error: could not connect to server: SSL error: certificate verify failed

排查步骤

bash
# 1. 检查服务器证书有效性
openssl x509 -in /etc/postgresql/ssl/server.crt -noout -dates

# 2. 验证证书主机名
openssl x509 -in /etc/postgresql/ssl/server.crt -noout -subject

# 3. 测试 SSL 连接
openssl s_client -connect mydb.example.com:5432 -servername mydb.example.com

# 4. 检查客户端信任存储
psql "host=mydb.example.com sslmode=require sslrootcert=/path/to/ca.crt" -c "SELECT version();"

问题 2:私钥权限错误

错误信息

FATAL: private key file "/etc/postgresql/ssl/server.key" has group or world access

解决方案

bash
# 修正私钥权限
chmod 0600 /etc/postgresql/ssl/server.key
chown postgres:postgres /etc/postgresql/ssl/server.key

# 验证权限
ls -la /etc/postgresql/ssl/server.key
# 输出应为:-rw------- 1 postgres postgres ... server.key

问题 3:证书链不完整

错误信息

SSL error: certificate chain verification failed

解决方案

bash
# 1. 检查证书链完整性
openssl crl2pkcs7 -nocrl -certfile server-chain.crt | \
    openssl pkcs7 -print_certs -noout

# 2. 重建完整证书链
cat server.crt intermediate.crt root.crt > server-complete.crt

# 3. 验证新证书链
openssl verify -CAfile root.crt -untrusted intermediate.crt server.crt

6.3 性能优化建议

SSL 连接性能调优

ini
# postgresql.conf 性能优化配置

# SSL 会话重用(减少握手开销)
ssl_renegotiation_limit = 0

# 启用 SSL 压缩(谨慎使用,可能有安全风险)
# ssl_compression = off  # 推荐关闭

# 选择高性能密码套件
ssl_ciphers = 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256'

# 启用椭圆曲线加密
ssl_ecdh_curve = 'prime256v1'

连接池 SSL 配置

python
# PgBouncer SSL 配置示例
import psycopg2.pool

# 创建支持 SSL 的连接池
ssl_pool = psycopg2.pool.ThreadedConnectionPool(
    1, 20,  # 最小和最大连接数
    host='mydb.example.com',
    database='myapp',
    user='appuser',
    password='mypassword',
    sslmode='require',
    sslcert='/path/to/client.crt',
    sslkey='/path/to/client.key',
    sslrootcert='/path/to/ca.crt'
)

# 使用连接池
def execute_query(query):
    conn = ssl_pool.getconn()
    try:
        with conn.cursor() as cursor:
            cursor.execute(query)
            return cursor.fetchall()
    finally:
        ssl_pool.putconn(conn)

总结

PostgreSQL SSL/TLS 配置是数据库安全的重要组成部分。通过本指南,您应该能够:

  1. 理解 SSL/TLS 的工作原理:掌握加密连接的基本概念和架构
  2. 配置安全的 SSL 连接:从基本设置到高级双向认证
  3. 管理证书生命周期:包括生成、更新、监控和故障排除
  4. 优化 SSL 性能:在安全性和性能之间找到平衡
  5. 解决常见问题:快速诊断和修复 SSL 相关问题

持续改进建议

  • 定期审查和更新 SSL 配置
  • 关注最新的安全漏洞和补丁
  • 建立完善的证书管理流程
  • 监控 SSL 连接的性能指标 :::

在生产环境中实施这些配置时,请务必先在测试环境中验证,并制定详细的回滚计划。SSL 配置错误可能导致服务不可用,因此谨慎操作是关键。