↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1
预发布版本文档。 PostgreSQL 19beta4 为测试版本,最终发布内容可能有所不同。

18.9. 使用 SSL 的安全 TCP/IP 连接 #

PostgreSQL 原生支持使用 SSL 连接来加密客户端与服务器之间的通信,以提高安全性。这要求客户端和服务器系统上都安装了 OpenSSL,并且在构建 PostgreSQL 时启用了该支持(见第 17 章)。

术语 SSL 和 TLS 经常被交替使用,用来表示基于 TLS 协议的安全加密连接。SSL 协议是 TLS 协议的前身,尽管 SSL 协议本身已不再受支持,SSL 一词仍常被用来泛指这类加密连接。在 PostgreSQL 中,SSL 与 TLS 也是这样互换使用的。

18.9.1. 基本设置 #

如果构建时启用了 SSL 支持,那么只需在 postgresql.conf 中把参数 ssl 设为 on,PostgreSQL 服务器就可以启用基于 TLS 协议的加密连接支持。服务器会在同一个 TCP 端口上同时监听普通连接和 SSL 连接,并与每个连接进来的客户端协商是否使用 SSL。默认情况下,这由客户端决定;关于如何把服务器配置为要求某些或全部连接必须使用 SSL,请参阅第 20.1 节。

要以 SSL 模式启动服务器,必须存在包含服务器证书和私钥的文件。默认情况下,这些文件应分别命名为 server.crt 和 server.key,并放在服务器的数据目录中;不过也可以通过配置参数 ssl_cert_file 和 ssl_key_file 指定其他名称和位置。

在 Unix 系统上,server.key 的权限必须禁止组和其他用户访问;可以通过 chmod 0600 server.key 实现。或者,该文件也可以由 root 拥有并授予组读权限(即 0640)。这种设置适用于证书和密钥文件由操作系统统一管理的安装方式。运行 PostgreSQL 服务器的用户此时应属于有权访问这些证书和密钥文件的组。

如果数据目录允许组读取访问,那么为了满足上面概述的安全要求,证书文件可能需要放在数据目录之外。通常,启用组访问权限是为了允许非特权用户备份数据库;在这种情况下,备份软件将无法读取证书文件,并且很可能会报错。

如果私钥受口令保护,服务器会提示输入该口令,并在输入之前不会启动。默认情况下,使用口令会禁用无需重启服务器即可更改 SSL 配置的能力,不过参见 ssl_passphrase_command_supports_reload。此外,在 Windows 上完全无法使用带口令保护的私钥。

server.crt 中的第一个证书必须是服务器证书,因为它必须与服务器私钥相匹配。“中间”证书颁发机构的证书也可以追加到该文件中。假设根证书和中间证书是使用 v3_ca 扩展创建的(这会将证书的 CA 基本约束设为 true),这样做就可以避免在客户端上存储中间证书。同时,这也使中间证书更容易单独过期。

无需把根证书加入 server.crt。相反,客户端必须持有服务器证书链对应的根证书。

18.9.2. OpenSSL 配置 #

PostgreSQL 会读取系统范围的 OpenSSL 配置文件。默认情况下,该文件名为 openssl.cnf,位于 openssl version -d 报告的目录中。可以通过把环境变量 OPENSSL_CONF 设为所需配置文件的名称,覆盖这一默认值。

OpenSSL 支持种类繁多、强度不一的密码套件和认证算法。虽然可以在 OpenSSL 配置文件中指定密码套件列表,但你也可以通过修改 postgresql.conf 中的 ssl_ciphers,专门为数据库服务器指定要使用的密码套件。

注意

使用 NULL-SHA 或 NULL-MD5 密码套件,可以在没有加密开销的情况下完成认证。不过,中间人仍然能够读取并转发客户端与服务器之间的通信。此外,与认证的开销相比,加密开销本身很小。基于这些原因,不建议使用 NULL 密码套件。

18.9.3. 使用客户端证书 #

若要要求客户端提供受信任的证书,请把你信任的根证书颁发机构(CA)证书放入数据目录中的某个文件里,在 postgresql.conf 中把参数 ssl_ca_file 设为该文件名,并在 pg_hba.conf 中相应的 hostssl 行上添加认证选项 clientcert=verify-ca 或 clientcert=verify-full。这样,SSL 连接启动时就会向客户端请求证书。(关于如何在客户端设置证书,请见第 32.19 节。)

对于带有 clientcert=verify-ca 的 hostssl 条目,服务器会验证客户端证书是否由某个受信任的证书颁发机构签署。如果指定的是 clientcert=verify-full,服务器不仅会验证证书链,还会检查用户名或其映射是否与所提供证书的 cn(通用名称)相匹配。请注意,在使用 cert 认证方法时,总会确保进行证书链验证(见第 20.11 节)。

如果你希望避免在客户端上存储中间证书,那么与现有根证书构成链的中间证书也可以出现在 ssl_ca_file 文件中(前提是根证书和中间证书是使用 v3_ca 扩展创建的)。如果设置了参数 ssl_crl_file 或 ssl_crl_dir,还会检查证书吊销列表(CRL)条目。

clientcert 认证选项适用于所有认证方法,但只适用于 pg_hba.conf 中指定为 hostssl 的行。未指定 clientcert 时,只有在客户端实际提供了证书且配置了 CA 的情况下,服务器才会依据其 CA 文件验证客户端证书。

有两种方法可以强制用户在登录时提供证书。

第一种方法是在 pg_hba.conf 的 hostssl 条目上使用 cert 认证方法,这样证书本身就会用于认证,同时也提供 SSL 连接安全性。详见第 20.11 节。(使用 cert 认证方法时,无需显式指定任何 clientcert 选项。)在这种情况下,会检查证书中的 cn(通用名称)是否与用户名或适用映射相匹配。

第二种方法是在 hostssl 条目上结合任意认证方法和客户端证书验证,即把 clientcert 认证选项设为 verify-ca 或 verify-full。前者只强制证书有效,后者则还会确保证书中的 cn(通用名称)与用户名或适用映射相匹配。

18.9.4. SSL 服务器文件用法 #

表 18.2 概括了服务器端 SSL 设置相关的文件。(表中显示的是默认文件名;本地实际配置的名称可能不同。)

表 18.2. SSL 服务器文件用法

文件内容效果
ssl_cert_file ($PGDATA/server.crt)服务器证书发送给客户端,用于表明服务器身份
ssl_key_file ($PGDATA/server.key)服务器私钥证明服务器证书是其所有者发送的,并不说明证书所有者是值得信任的
ssl_ca_file受信任的证书颁发机构检查客户端证书是由一个受信任的证书颁发机构签名的
ssl_crl_file被证书颁发机构吊销的证书客户端证书不能出现在这个列表上
$PGDATA/pg_hosts.confSNI 配置定义针对哪些服务器主机名使用哪些证书

服务器会在启动时以及每次重新加载配置时读取这些文件。在 Windows 系统上,每当为新的客户端连接生成新的后端进程时,也会重新读取它们。

如果在服务器启动时检测到这些文件有错误,服务器将拒绝启动。但如果在重新加载配置时检测到错误,这些文件会被忽略,旧的 SSL 配置将继续使用。在 Windows 系统上,如果在后端启动时检测到这些文件有错误,该后端将无法建立 SSL 连接。在所有这些情况下,错误都会记录到服务器日志中。

18.9.5. 创建证书 #

要为服务器创建一个有效期为 365 天的简单自签名证书,可以使用下面的 OpenSSL 命令,并将 dbhost.yourdomain.com 替换为服务器主机名:

openssl req -new -x509 -days 365 -nodes -text -out server.crt \
  -keyout server.key -subj "/CN=dbhost.yourdomain.com"

然后执行:

chmod og-rwx server.key

如果该文件权限比这更宽松,服务器会拒绝使用它。关于如何创建服务器私钥和证书的更多细节,请参阅 OpenSSL 文档。

虽然自签名证书可用于测试,但在生产环境中应使用由证书颁发机构(CA,通常是企业范围内的根 CA)签名的证书。

要创建一个可由客户端验证其身份的服务器证书,首先要创建证书签名请求(CSR)以及公钥/私钥文件:

openssl req -new -nodes -text -out root.csr \
  -keyout root.key -subj "/CN=root.yourdomain.com"
chmod og-rwx root.key

然后,使用该密钥对请求进行签名,以创建一个根证书颁发机构(这里使用的是 Linux 上 OpenSSL 配置文件的默认位置):

openssl x509 -req -in root.csr -text -days 3650 \
  -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
  -signkey root.key -out root.crt

最后,创建一个由新根证书颁发机构签名的服务器证书:

openssl req -new -nodes -text -out server.csr \
  -keyout server.key -subj "/CN=dbhost.yourdomain.com"
chmod og-rwx server.key

openssl x509 -req -in server.csr -text -days 365 \
  -CA root.crt -CAkey root.key -CAcreateserial \
  -out server.crt

server.crt 和 server.key 应存放在服务器上,而 root.crt 应存放在客户端上,以便客户端能够验证服务器的叶子证书是否由其受信任的根证书签名。root.key 应离线保存,以供将来签发证书时使用。

也可以创建包含中间证书的信任链:

# root
openssl req -new -nodes -text -out root.csr \
  -keyout root.key -subj "/CN=root.yourdomain.com"
chmod og-rwx root.key
openssl x509 -req -in root.csr -text -days 3650 \
  -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
  -signkey root.key -out root.crt

# intermediate
openssl req -new -nodes -text -out intermediate.csr \
  -keyout intermediate.key -subj "/CN=intermediate.yourdomain.com"
chmod og-rwx intermediate.key
openssl x509 -req -in intermediate.csr -text -days 1825 \
  -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
  -CA root.crt -CAkey root.key -CAcreateserial \
  -out intermediate.crt

# leaf
openssl req -new -nodes -text -out server.csr \
  -keyout server.key -subj "/CN=dbhost.yourdomain.com"
chmod og-rwx server.key
openssl x509 -req -in server.csr -text -days 365 \
  -CA intermediate.crt -CAkey intermediate.key -CAcreateserial \
  -out server.crt

server.crt 和 intermediate.crt 应拼接为一个证书文件包并存放在服务器上。server.key 也应存放在服务器上。root.crt 应存放在客户端上,以便客户端验证服务器的叶子证书是否由一条可追溯到其受信任根证书的证书链签名。root.key 和 intermediate.key 应离线保存,以供将来签发证书时使用。

18.9.6. SNI 配置 #

PostgreSQL 可以通过 ssl_sni 配置参数启用服务器名称指示(Server Name Indication,SNI)。PostgreSQL 会检查 SSL 连接握手中的 TLS 主机名扩展,并根据 hosts_file 配置文件中的条目,为连接选择正确的证书、密钥和 CA 证书。

hosts_file 配置文件包含如下通用格式的行:

hostname SSL_certificate SSL_key [ SSL_CA_certificate [ SSL_passphrase_cmd [ SSL_passphrase_cmd_reload ] ] ]
include file
include_if_exists file
include_dir directory

注释、空白、行续接和包含指令的处理方式与 hba_file 相同。hostname 会与 SSL 握手中的 hostname TLS 扩展进行不区分大小写的匹配。SSL_certificate、SSL_key、SSL_CA_certificate、SSL_passphrase_cmd 以及 SSL_passphrase_cmd_reload 分别按 ssl_cert_file、ssl_key_file、ssl_ca_file、ssl_passphrase_command 和 ssl_passphrase_command_supports_reload 处理。除 SSL_CA_certificate、SSL_passphrase_cmd 和 SSL_passphrase_cmd_reload 外,其他字段都是必需的。如果提供了 SSL_passphrase_cmd 但未提供 SSL_passphrase_cmd_reload,则 SSL_passphrase_cmd_reload 的默认值为 off。

hostname 可以是连接的字面主机名、/no_sni/ 或 *。表 18.3 说明了这些取值的用法。

表 18.3. 主机名字段取值

主机项主机名扩展说明
hostname必需 用于连接到连接中指定主机的证书和密钥。可以使用逗号分隔列表定义多个主机名。该证书和密钥将用于连接列表中的所有主机。
/no_sni/不允许 用于未定义 sslsni 的连接的证书和密钥。
*非必需 默认主机,匹配所有连接。


如果 hosts_file 为空或缺失,则会对所有连接使用 postgresql.conf 中的 SSL 配置。如果 hosts_file 非空,则它优先于 postgresql.conf 中的证书和密钥设置。

目前无法为不同证书设置不同的 clientname 值。hba_file 中的任何 clientname 设置都会在认证期间应用,而不管通过启用 SNI 的连接加载了哪组证书。

postgresql.conf 中的 CRL 配置适用于所有连接,无论它们是否使用 SNI。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.