使用 SSL/TLS 和 PostgreSQL 通讯

Spring Boot 下如何和 PostgreSQL 建立 SSL/TLS 连接

Posted by mingfer on April 20, 2019

出于安全方面的考虑,有时候可能我们会希望应用和数据库之间的数据传输是通过 SSL/TLS 协议进行的。本文的主要内容在于介绍一个基于 Spring Boot 的应用如何和 PostgreSQL 数据库进行 SSL/TLS 通讯。

在 PostgreSQL 数据库的 JDBC 官方文档中,对如何配置 SSL 进行了说明。但是根据官方文档的解释,PostgreSQL 的 JDBC 只是实现一个加密的 SSL 链路,但是并不会验证证书的有效性。这不一定能够满足某些情况下要求的安全强度,所以下文会介绍如何通过扩展 JDBC 实现和 PostgreSQL 建立双向验证的 SSL/TLS 链路。

参考文档:

环境准备

为了快速的进行 PostgreSQL 的使用,这里使用 Docker 启动 PostgreSQL,关于 PostgreSQL 在 docker 下的使用参见这里

启动一个 PostgreSQL 实例:

1
$ docker run --name postgres -d -v ~/tmp/config:/config -e POSTGRES_PASSWORD=123456 postgres
  • -d 是指后台启动
  • -v ~/tmp/config:/config 是将宿主机的 ~/tmp/config 目录映射到容器的 /config 目录,方面之后配置证书
  • -e POSTGRES_PASSWORD=123456 指定 PostgreSQL 的登录密码,默认的用户是 postgres,可以通过 POSTGRES_USER 变量更改用户名。

重新启动一个实例,登录到上一个实例中进行数据库操作:

1
2
3
4
5
6
7
8
9
10
11
# 启动一个临时的 posetgres 实例当做客户端
$ docker run --rm -ti postgres /bin/bash

# 登录到 posetgres 数据库实例中,密码是 123456
$ psql -U postgres -W

# 创建一个 test 数据库
postgres=# create database test;

# 切换到 test 数据库下面, 用户密码是 123456
postgres=# \c test

使用 demo-posetgresql 中的 cn.mingfer.demo.postgresql.UserRepositoryTest#insert 方法插入一条数据到 user 数据表(这里使用 JPA 自动执行了数据表的创建,也可以自己手动创建数据表)。

PostgreSQL 单向 SSL

PosetgreSQL 提供的 JDBC 中支持的 SSL 是纯 SSL 链路,也就是不对双方的身份证书进行校验,而只是建立一个加密信道。下面是配置 PostgreSQL SSL 链路的过程:

启动一个 PostgreSQL 容器:

1
$ docker run --name postgres -d -v ~/tmp/config:/config -e POSTGRES_PASSWORD=123456 postgres

注意:这里需要将配置目录 /config 和宿主机的一个目录进行映射,方便我们后面进行证书文件的复制和修改配置文件。PostgreSQL 提供的镜像里面没有包含 vi 等编辑工具,我们需要借助宿主机编辑之后复制到对应的目录。

签发出标识 PostgreSQL 的证书文件 postgres.crt 和私钥文件 postgres.key,具体的证书文件生成过程参见生成数字证书。将这两个文件放到宿主机的 ~/tmp/config 目录下,将权限配置为 600 (chmod 600 postgres.*) ,也就是当前用户可读写。

通过下面的命令进入到 PostgreSQL 容器中:

1
$ docker exec -ti postgres bash

/var/lib/postgresql/data/ 目录下面的 pg_hba.confpostgresql.conf 这两个配置文件复制到 /config 目录下面:

1
2
$ cd /var/lib/postgresql/data/
$ cp pg_hba.conf postgresql.conf /config

在宿主机的 ~/tmp/config 目录下编辑 postgresql.conf 文件,将 ssl 配置为 onssl_cert_file 是 PostgreSQL 的身份证书,ssl_key_file 是 PostgreSQL 的私钥文件。ssl_ca_file 是 PostgreSQL 信任的证书,PostgreSQL 通过这些证书去认证客户端,这里我们只是配置单纯的 SSL 用不着验证客户端身份,所以略过此项:

1
2
3
4
5
6
7
8
9
10
11
12
13
# - SSL -

ssl = on
#ssl_ca_file = '/config/root.crt'
ssl_cert_file = '/config/postgres.crt'
#ssl_crl_file = ''
ssl_key_file = '/config/postgres.key'
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off

pg_hba.conf 文件中配置一条 hostssl 规则:

1
2
# IPv4 local connections:
hostssl    all             all             0.0.0.0/0            md5

使用配置好的文件覆盖 /var/lib/postgresql/data/ 下面的同名文件,请注意备份原文件

1
2
$ cd /var/lib/postgresql/data/
$ cp /config/postgresql.conf /config/pg_hba.conf ./

退出容器,重启容器:

1
2
$ exit
$ docker restart postgres

启动一个临时的容器对 PostgreSQL 进行访问:

1
2
3
4
5
6
7
8
$ docker run --rm -ti 30bf4f039abe /bin/bash
root@6460748d933b:/# psql -U postgres -h 192.168.10.102 
Password for user postgres: 
psql (11.2 (Debian 11.2-1.pgdg90+1))
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# 

可以发现连接到数据库的连接是 TLSv1.2 的 SSL 连接。

需要注意,如果是从 PostgreSQL 数据库容器本地用户去访问,这里是不会使用 SSL 的:

1
2
3
4
5
6
7
$ docker exec -ti postgres bash
root@7554074b9f97:/# su postgres
postgres@7554074b9f97:/$ psql
psql (11.2 (Debian 11.2-1.pgdg90+1))
Type "help" for help.

postgres=# 

因为此时的连接是使用 pg_hba.conf 中的 local 规则建立连接的:

1
2
# "local" is for Unix domain socket connections only
local   all             all                                     trust