使用SSL/TLS配置保护PostgreSQL连接:完整指南

学习如何使用SSL/TLS加密保护PostgreSQL连接。本全面指南涵盖服务器端和客户端配置,包括生成证书、修改`postgresql.conf`和`pg_hba.conf`,以及设置客户端以实现安全的加密通信。保护传输中的敏感数据,确保符合现代安全标准。

使用SSL/TLS配置保护PostgreSQL连接:完整指南

PostgreSQL SSL/TLS配置包含两个独立任务。第一个是加密,防止网络上的其他人读取传输中的凭据或查询结果。第二个是身份验证,确保客户端连接的是真实的数据库服务器,而不是冒充它的机器。许多配置只完成了第一部分,却意外跳过了第二部分。

这种区别在实际部署中至关重要。sslmode=require会加密连接,但本身并不完全验证服务器主机名。sslmode=verify-full则会进行验证。如果你的应用程序通过公共网络、共享企业网络、你无法完全控制的Kubernetes覆盖网络或任何可能被拦截流量的环境进行连接,verify-full应该是目标。

理解PostgreSQL中的SSL/TLS

SSL/TLS(安全套接层/传输层安全)是一种加密协议,旨在为计算机网络上的通信提供安全性。当应用于PostgreSQL时,它会对数据库服务器和客户端之间交换的数据进行加密。这可以防止未经授权的方拦截和读取敏感信息,如凭据、财务数据或个人详细信息。

PostgreSQL客户端支持多种SSL模式:

  • sslmode=disable:仅使用普通未加密连接。
  • sslmode=prefer:首先尝试TLS,如果TLS不可用,则回退到普通TCP。这很方便,但可能隐藏配置错误。
  • sslmode=require:要求TLS加密,但不一定验证服务器主机名。
  • sslmode=verify-ca:要求TLS并验证证书链到受信任的CA。
  • sslmode=verify-full:要求TLS,验证CA,并验证主机名与证书匹配。

在服务器端,ssl = on仅表示PostgreSQL能够接受TLS连接。它并不强制每个客户端都使用TLS。强制执行在pg_hba.conf中通过hostssl规则以及避免使用允许相同用户和网络在不使用TLS的情况下连接的更宽泛的host规则来实现。

SSL/TLS配置的先决条件

在开始配置PostgreSQL的SSL/TLS之前,请确保你具备以下条件:

  1. 已安装OpenSSL:OpenSSL工具包对于生成和管理SSL证书至关重要。它通常预装在Linux和macOS系统上。对于Windows,你可能需要单独下载并安装它。
  2. 访问PostgreSQL配置文件:你需要管理员权限来修改postgresql.confpg_hba.conf文件。
  3. 理解证书颁发机构(CA):虽然你可以为测试创建自签名证书,但生产环境最好使用由受信任的证书颁发机构(CA)或内部企业CA签名的证书。

服务器端SSL/TLS配置

服务器端配置包括启用SSL、指定SSL证书和密钥的位置,以及配置客户端身份验证。

1. 生成或获取SSL证书和密钥

为PostgreSQL服务器获取SSL证书主要有两种方式:

  • 自签名证书(用于测试/开发):这些证书使用OpenSSL创建,默认情况下不被外部客户端信任。它们适用于初始设置和内部测试。
  • 来自证书颁发机构(CA)的证书(用于生产):从受信任的公共CA(例如Let's Encrypt、DigiCert)或内部企业CA获取证书。这确保客户端可以验证服务器的身份。

使用OpenSSL创建自签名证书:

这是开发和内部环境的常见方法。在PostgreSQL服务器或安装了OpenSSL的机器上执行以下命令:

  1. 创建证书目录:保持证书组织有序是一个好习惯。

    sudo mkdir -p /etc/postgresql/ssl
    sudo chown postgres:postgres /etc/postgresql/ssl
    cd /etc/postgresql/ssl
    
  2. 生成服务器私钥:此密钥应保密。

    sudo openssl genrsa -out server.key 2048
    
  3. 创建服务器证书签名请求(CSR):其中包含有关服务器的信息。

    sudo openssl req -new -key server.key -out server.csr
    

    使用客户端将连接的主机名,例如db01.internal.example.com。现代客户端通常会检查主题备用名称(SAN),因此当你的CA流程支持时,请在证书请求中包含DNS名称。

  4. 使用你自己的CA签署证书(用于内部使用)

    • 创建根CA私钥和证书(如果你还没有)
      # 生成CA私钥
      sudo openssl genrsa -des3 -out root.key 2048
      # 创建CA证书(有效期为3650天)
      sudo openssl req -new -x509 -days 3650 -key root.key -out root.crt
      
    • 使用CA签署服务器CSR:这将创建受信任的服务器证书。
      sudo openssl x509 -req -days 365 -in server.csr -CA root.crt -CAkey root.key -set_serial 01 -out server.crt
      
  5. 设置权限:确保PostgreSQL用户可以读取这些文件。

    sudo chown postgres:postgres server.key server.crt root.crt
    sudo chmod 600 server.key
    sudo chmod 644 server.crt root.crt
    

使用来自公共/企业CA的证书:

如果你从CA获取证书,通常会收到:

  • server.crt:你的服务器的公共证书。
  • server.key:你的服务器的私钥。
  • root.crt(或类似):CA的根证书(以及可能的中间证书)。

将这些文件放在安全目录中(例如/etc/postgresql/ssl/),并确保PostgreSQL用户具有读取权限。

2. 配置postgresql.conf

编辑你的postgresql.conf文件(通常位于PostgreSQL数据目录中)以启用SSL并指定证书文件。

#------------------------------------------------------------------------------
# SSL
#------------------------------------------------------------------------------

ssl = on

# 这些文件都是PEM格式,如果未配置服务器密钥/证书,则忽略它们。
# 默认情况下,这些文件应位于服务器的数据目录中。
# 或者,它们可以指定为完整路径。
ssl_cert_file = '/etc/postgresql/ssl/server.crt'     # (如果需要,更改文件名)
ssl_key_file = '/etc/postgresql/ssl/server.key'      # (如果需要,更改文件名)
ssl_ca_file = '/etc/postgresql/ssl/root.crt'         # (可选,用于客户端证书验证)

# 可选:如果需要,指定密码套件
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'

# 可选:启用客户端证书验证
#ssl_ca_file必须设置为包含要信任的CA证书的文件
#ssl_crl_file = ''
#ssl_crl_dir = ''
  • ssl = on:在服务器上启用SSL支持。
  • ssl_cert_file:服务器公共证书的路径。
  • ssl_key_file:服务器私钥的路径。
  • ssl_ca_file:PostgreSQL在验证客户端证书时应信任的CA证书的路径。客户端使用自己的CA文件(例如sslrootcert)来验证服务器。

3. 配置pg_hba.conf以强制执行SSL

pg_hba.conf文件控制客户端身份验证。你需要修改条目以强制执行SSL连接。

默认情况下,pg_hba.conf中的条目如下所示:

# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             all                                     peer
# IPv4本地连接:
host    all             all             127.0.0.1/32            scram-sha-256
# IPv6本地连接:
host    all             all             ::1/128                 scram-sha-256

要强制执行SSL,请将host条目更改为hostssl

# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             all                                     peer
# IPv4本地连接:
hostssl all             all             127.0.0.1/32            scram-sha-256
# IPv6本地连接:
hostssl all             all             ::1/128                 scram-sha-256

# 外部网络访问示例 - 需要SSL
hostssl all             app_user        10.20.0.0/16            scram-sha-256
hostssl all             app_user        2001:db8:20::/48        scram-sha-256
  • hostssl:此记录类型需要SSL连接。任何不使用SSL的连接尝试都将被拒绝。
  • hostnossl:此记录类型明确禁止SSL连接。
  • host:允许SSL和非SSL连接。如果在hostssl规则之前或代替它存在匹配的host规则,客户端可能仍然可以在没有TLS的情况下连接。

除非有充分理由并且有其他控制措施,否则避免发布0.0.0.0/0访问权限。大多数生产数据库应仅接受来自应用程序子网、堡垒主机、连接池或私有网络范围的连接。

4. 重启PostgreSQL服务器

修改postgresql.confpg_hba.conf后,必须重启PostgreSQL服务才能使更改生效。

# 对于使用systemd的系统(大多数现代Linux发行版)
sudo systemctl restart postgresql

# 对于使用init.d的系统
sudo service postgresql restart

客户端SSL/TLS配置

客户端也需要配置为安全连接。这涉及指定连接参数,可能提供客户端证书,以及验证服务器的证书。

1. 连接字符串参数

通过psql或任何PostgreSQL客户端库连接时,可以在连接字符串中或作为单独选项指定SSL参数。

基本SSL连接(仅服务器身份验证)

psql "sslmode=require host=your_server_hostname dbname=your_db user=your_user"
  • sslmode:控制客户端的SSL行为。
    • disable:仅允许非SSL连接。
    • allow:允许非SSL,但如果服务器支持,则首选SSL。
    • prefer(默认):首选SSL,但如果SSL失败,则允许非SSL。
    • require:仅允许SSL连接。如果服务器不支持SSL,则连接失败。
    • verify-ca:仅允许SSL连接,并验证服务器证书是否由受信任的CA签名。必须设置sslrootcert参数。
    • verify-full:仅允许SSL连接,验证服务器证书是否来自受信任的CA,并验证服务器主机名是否与证书的通用名称(CN)或主题备用名称(SAN)匹配。

验证服务器证书(verify-caverify-full

为了增强安全性,客户端应验证服务器的身份。这要求客户端信任签署服务器证书的CA。

  1. 获取CA证书:获取用于签署服务器证书的root.crt文件(或适当的CA证书)。
  2. 指定sslrootcert:告诉客户端在哪里找到此CA证书。
psql "sslmode=verify-full host=your_server_hostname dbname=your_db user=your_user sslrootcert=/path/to/your/root.crt"

这是你应该从运行应用程序的同一主机或容器测试的连接字符串。一个常见的失败是psql在管理员笔记本电脑上工作,因为那里存在CA文件,而应用程序容器失败,因为从未挂载CA包。

2. 客户端证书(双向身份验证)

为了获得更高级别的安全性,你可以实现双向身份验证,其中服务器也使用客户端证书验证客户端的身份。

生成客户端证书:

与服务器证书类似,你需要一个客户端私钥和一个由服务器信任的CA(通常与服务器证书使用相同的CA)签署的客户端证书。

  1. 生成客户端私钥

    openssl genrsa -des3 -out client.key 2048
    
  2. 创建客户端CSR

    openssl req -new -key client.key -out client.csr
    

    提供详细信息,确保通用名称对客户端是唯一的。

  3. 使用CA签署客户端CSR

    sudo openssl x509 -req -days 365 -in client.csr -CA root.crt -CAkey root.key -set_serial <unique_serial> -out client.crt
    
  4. 设置权限

    chmod 600 client.key
    chmod 644 client.crt
    

配置pg_hba.conf以进行客户端证书身份验证:

在服务器上,你需要配置pg_hba.conf以接受客户端证书身份验证。这通常使用cert身份验证方法。

# TYPE  DATABASE        USER            ADDRESS                 METHOD
# 要求SSL和客户端证书身份验证用于特定用户/数据库
hostssl all             your_user       your_client_ip/32       cert map=your_cert_map

如果你希望将特定的客户端证书详细信息(如SubjectSubjectAltName)映射到PostgreSQL用户,则可能还需要定义证书映射文件(cert_map选项)。有关详细的cert身份验证和证书映射设置,请参阅PostgreSQL文档。

配置客户端使用证书:

更新客户端的连接字符串以包含其证书和密钥的路径:

psql "sslmode=verify-full host=your_server_hostname dbname=your_db user=your_user \
sslrootcert=/path/to/your/root.crt sslcert=/path/to/your/client.crt sslkey=/path/to/your/client.key"

最佳实践和提示

  • 使用verify-full sslmode:在客户端使用verify-full以减少中间人攻击风险。
  • 保护私钥:确保私钥(.key文件)具有严格的文件权限(例如chmod 600),并且仅服务器上的PostgreSQL用户和客户端上的连接用户可以读取。
  • 定期续订证书:证书有到期日期。实施一个流程在它们到期前续订,以避免连接中断。
  • 集中证书管理:对于较大规模的部署,考虑使用证书管理系统或自动化证书颁发和续订。
  • 监控日志:检查PostgreSQL日志中启动或连接尝试期间的任何SSL相关错误。
  • 文档:参考官方PostgreSQL文档,获取针对你的PostgreSQL版本的最新参数和高级配置选项。

快速验证清单

重启PostgreSQL后,检查服务器是否已启用TLS监听:

SHOW ssl;
SHOW ssl_cert_file;
SHOW ssl_key_file;

然后从客户端主机测试:

psql "host=your_server_hostname dbname=your_db user=your_user sslmode=verify-full sslrootcert=/path/to/root.crt"

在会话内部,确认连接已加密:

SELECT ssl, version, cipher
FROM pg_stat_ssl
WHERE pid = pg_backend_pid();

如果ssl为false,则你的pg_hba.conf规则并未按你预期的方式强制执行。如果verify-full失败但require有效,则证书可能缺少正确的主机名,客户端不信任CA,或者应用程序通过IP地址连接而证书是为DNS名称颁发的。

良好的PostgreSQL TLS设置不仅仅是ssl = on。它是一个链条:包含正确名称的证书、具有严格权限的私钥、实际强制执行TLS的hostssl规则,以及验证服务器而不仅仅是加密套接字的客户端。