在 PostgreSQL 中启用 SSL 非常简单。只需三个步骤,我们就可以确保与它的连接更加安全,使用通过 SSL/TLS 的传输中加密:
- 确保我们有可用的服务器证书和密钥文件
- 启用 SSL 配置 (ssl = on)
- 确保 pg_hba.conf 文件规则相应更新
在这篇博文中,我们将完成这些步骤,我们还将了解如何检查和验证连接是否确实使用了更安全的 SSL 协议。
什么是 SSL/TLS?
SSL(安全套接字层)是一种加密协议,旨在使两个节点之间的网络通信安全。如果没有某种形式的网络加密,任何可以检查网络数据包的第三方都可以访问客户端和服务器之间发送的数据(在这种情况下,是 PostgreSQL 数据,即用户、密码,甚至 SQL 语句)。TLS(传输层安全性)是它的更现代的定义,即使 SSL 已被弃用,仍将其用于命名目的。出于所有意图和目的,我们在此博客中将它们用作别名。
PostgreSQL 文档页面为我们提供了这方面的更多见解。如果需要,请参阅使用 SSL和SSL 支持的安全 TCP/IP 连接条目以获取更多信息。
尝试在没有证书/密钥文件的情况下启用 SSL
现在让我们看看当我们在没有所需证书和密钥文件的情况下尝试启用 SSL 时会发生什么:
Shell
1
2
3
4
5
6
7
postgres=# alter system set ssl=on;
ALTER SYSTEM
postgres=# select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
我们没有看到任何错误,但我们真的在使用 SSL 吗?如果我们检查错误日志,我们确实会看到错误:
Shell
1
2
3
4
2022-06-23 20:43:54.713 UTC [5284] LOG: received SIGHUP, reloading configuration files
2022-06-23 20:43:54.714 UTC [5284] LOG: parameter "ssl" changed to "on"
2022-06-23 20:43:54.715 UTC [5284] LOG: could not load server certificate file "server.crt": No such file or directory
2022-06-23 20:43:54.715 UTC [5284] LOG: SSL configuration was not reloaded
创建证书
因此,我们首先需要创建上述文件。如果您还没有有效的证书和密钥文件,则可以使用以下 openssl 命令快速解决此问题(这里不是重点深入研究这部分过程):
Shell
1
2
3
4
5
6
7
[root@node0 ~]# cd /var/lib/pgsql/14/data
[root@node0 data]# openssl req -nodes -new -x509 -keyout server.key -out server.crt -subj '/C=US/L=NYC/O=Percona/CN=postgres'
Generating a 2048 bit RSA private key
....+++
.........................+++
writing new private key to 'server.key'
-----
自从我们处于基于 RHEL 的系统中以来,我们已将当前工作目录更改为 PostgreSQL 数据目录。如果您使用的是基于 Debian 的,您应该将文件存储在/etc/ssl/certs/和/etc/ssl/private/或分别定义/检查ssl_cert_file和ssl_key_file PostgreSQL 配置变量。另外,确保 postgres 用户拥有它们,并且它们只对其可读:
Shell
1
2
3
4
5
[root@node0 data]# chmod 400 server.{crt,key}
[root@node0 data]# chown postgres:postgres server.{crt,key}
[root@node0 data]# ll server.{crt,key}
-r--------. 1 postgres postgres 1212 Jun 23 20:49 server.crt
-r--------. 1 postgres postgres 1704 Jun 23 20:49 server.key
启用 SSL/TLS
现在我们可以启用 SSL 并再次重新加载配置;这次没有显示错误:
Shell
1
2
3
4
5
6
7
postgres=# alter system set ssl=on;
ALTER SYSTEM
postgres=# select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
Shell
1
2
2022-06-23 20:52:05.823 UTC [5284] LOG: received SIGHUP, reloading configuration files
2022-06-23 20:52:05.823 UTC [5284] LOG: parameter "ssl" changed to "on"
到目前为止,我们已经启用了 SSL,但除非我们修改 pg_hba.conf 文件,否则这些设置不会应用于任何用户(至少不是以强制方式)。这是可以给我们一种错误的安全感的第一步,所以让我们继续看看如何解决它。
强制执行 SSL/TLS
如前所述,我们可以在 pg_hba.conf 文件中调整使用 SSL 所需的连接。我们可以通过使用“ hostssl ”关键字而不是普通的“ host ”关键字来指示 PostgreSQL 强制执行此操作。请注意,此时您可以看到一些连接开始使用 SSL,因为普通的“ host ”关键字将允许想要使用 SSL 的连接使用它。但是,这并不强制使用 SSL(即:如果客户端不想使用 SSL,PostgreSQL 将不会拒绝连接)。
让我们假设这是我们迄今为止一直在使用的pg_hba.conf文件:
Shell
1
2
3
4
5
6
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
host all all 0.0.0.0/0 md5
host replication all 10.124.33.113/24 md5
我们希望强制所有远程用户的 SSL 连接(还包括远程复制连接):
Shell
1
2
3
4
5
6
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
hostssl all all 0.0.0.0/0 md5
hostssl replication all 10.124.33.113/24 md5
同样,如果我们坚持真正强制连接以使用 SSL,这还不够。我们必须再次调用 pg_reload_conf() 以确保它们被加载到 PostgreSQL 本身:
Shell
1
2
3
4
5
postgres=# select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
此时,新的远程非 SSL 连接将被拒绝:
Shell
1
2
[root@node1 ~]# psql "host=10.124.33.132 sslmode=disable"
psql: error: connection to server at "10.124.33.132", port 5432 failed: FATAL: no pg_hba.conf entry for host "10.124.33.113", user "postgres", database "postgres", no encryption
那么,我们现在可以说我们现在完全安全了吗?还没有!已经建立的连接在重新连接之前不会强制使用 SSL。
使用 SSL/TLS 检查连接
我们可以使用以下查询检查使用 SSL 的连接:
Shell
1
2
3
4
5
6
7
8
9
10
11
postgres=# select pg_ssl.pid, pg_ssl.ssl, pg_ssl.version,
pg_sa.backend_type, pg_sa.usename, pg_sa.client_addr
from pg_stat_ssl pg_ssl
join pg_stat_activity pg_sa
on pg_ssl.pid = pg_sa.pid;
pid | ssl | version | backend_type | usename | client_addr
------+-----+---------+----------------+----------+---------------
5547 | f | | walsender | postgres | 10.124.33.113
5549 | f | | client backend | postgres | 10.124.33.132
5556 | f | | client backend | postgres | 10.124.33.113
(3 rows)
在这种情况下,复制连接(walsender )尚未使用 SSL,其他两个客户端也未连接,因此如果我们希望它们重新连接,我们需要强制重启。与往常一样,我们建议您首先在测试环境中尝试所有这些步骤,并且当需要在生产环境中执行这些步骤时,您可以在适当建立的维护窗口中执行它们(无论这些步骤看起来多么微不足道)。
要强制复制连接使用 SSL,可以重新启动副本中的服务或使用pg_terminate_backend (它将向进程发送SIGTERM信号并且在这种情况下可以安全使用)。在这种情况下,我们在主节点本身中使用pg_terminate_backend ,但它也可以在副本中使用,前提是我们使用了正确的 PID 号。
Shell
1
2
3
4
5
postgres=# select pg_terminate_backend(5547);
pg_terminate_backend
----------------------
t
(1 row)
之后,我们应该可以看到正确使用 SSL/TLS 协议的新副本连接:
Shell
1
2
3
4
5
6
7
8
9
10
11
postgres=# select pg_ssl.pid, pg_ssl.ssl, pg_ssl.version,
pg_sa.backend_type, pg_sa.usename, pg_sa.client_addr
from pg_stat_ssl pg_ssl
join pg_stat_activity pg_sa
on pg_ssl.pid = pg_sa.pid;
pid | ssl | version | backend_type | usename | client_addr
------+-----+---------+----------------+----------+---------------
5557 | t | TLSv1.2 | walsender | postgres | 10.124.33.113
5549 | f | | client backend | postgres | 10.124.33.132
5556 | f | | client backend | postgres | 10.124.33.113
(3 rows)
PID 5549 是我们自己的连接,所以这是一个简单的修复:
Shell
1
2
3
4
5
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
5549
(1 row)
来自 5556 的连接将是我们检查是否需要对所有人强制执行 SSL 的剩余连接。在客户端,我们可以使用 \conninfo 检查当前连接的信息:
Shell
1
2
3
postgres=# \conninfo
You are connected to database "postgres" as user "postgres" on host "10.124.33.132" at port "5432".
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
禁用 SSL/TLS
如果您想禁用 SSL,请确保在设置 ssl=off 并更改 pg_hba.conf 文件后不要丢失客户端连接,否则如果您没有任何使用“主机”的帐户,您可能会被锁定唯一的访问方法,您唯一的出路是重新启动服务。为了安全起见,首先,编辑并重新加载 pg_hba.conf 文件以包含带有“host”的条目,然后才完全禁用 SSL (ssl=off)。
结论
为传输中的连接加密启用 SSL/TLS 很容易,但是在强制使用它时需要注意一些陷阱。仅仅启用它的配置是不够的,即使默认情况下某些连接可能更喜欢在 SSL 可用时使用它。如果您需要确保所有连接都使用 SSL,请相应地编辑 pg_hba.conf 文件并确保它已加载。请记住,“hostssl”条目是强制此行为的条目。
我们可以使用 tcpdump 和 wireshark 来检查连接是否确实被加密了。但是,这是另一个博客的主题......
原文标题:Enabling and Enforcing SSL/TLS for PostgreSQL Connections
原文作者: Agustín
原文地址:https://www.percona.com/blog/enabling-and-enforcing-ssl-tls-for-postgresql-connections/




