PostgreSQL加密连接SSL配置

PostgreSQL加密连接SSL配置

环境说明

操作系统 主机名 IP 类型 说明
CentOS Linux release 7.6.1810 (Core) centos7.6 192.168.1.150 PostgreSQL Server PostgreSQL 12.17
CentOS Linux release 7.6.1810 (Core) testLinux 192.168.1.200 client psql 13.11
Winserver 2012 192.168.1.99 client navicat

SSL说明

什么是SSL?

SSL的全名叫做secure socket layer(安全套接字层),最开始是由一家叫网景的互联网公司开发出来,主要是防止信息在互联网上传输的时候不被窃听或者篡改,后来网景公司提交SSL给ISOC组织做标准化,改名为TLS。

什么是openssl?

openssl 是目前最流行的 SSL 密码库工具,其提供了一个通用、健壮、功能完备的工具套件,用以支持SSL/TLS 协议的实现。

SSL双向认证和SSL单向认证的区别?

双向认证 SSL 协议要求服务器和用户双方都有证书。

单向认证 SSL 协议不需要客户拥有CA证书,服务器端不会验证客户证书,以及在协商对称密码方案,对称通话密钥时,服务器发送给客户的是没有加过密的(这并不影响 SSL 过程的安全性)密码方案。

一般Web应用都是采用SSL单向认证的,无需在通讯层对用户身份进行验证,一般都在应用逻辑层来保证用户的合法登入。但如果是企业应用对接,可能会要求对客户端(相对而言)做身份验证。这时就需要做SSL双向认证。

单向认证和双向认证的区别仅在于创建连接阶段,数据的传输均为加密的,因此客户端与PG服务端的连接采取SSL单向认证即可,即仅在PG Server端配置SSL证书。

前提条件:必须使用openssl来编译安装pg,否则后期不能启用ssl加密连接

查看postgresql是否使用openssl选项编译安装,没有则需重新编译:

[postgres@centos7 ~]$ pg_config|grep CONFIGURE|grep ssl
CONFIGURE = '--prefix=/postgresql/pg12' '--with-openssl'
[postgres@centos7 ~]$

在启用了--with-openssl这个编译选项的情况下,ssl_library的参数值是OpenSSL,否则为空。

[postgres@centos7 ~]$ psql -c "select name,setting,unit,context from pg_settings where name ~* 'ssl_library';"
    name     | setting | unit | context  
-------------+---------+------+----------
 ssl_library | OpenSSL |      | internal
(1 row)

配置单向SSL认证连接

1、为服务器创建一个有效期为365天的简单自签名证书,创建服务端证书和私钥文件:

su - postgres
mkdir ~/openssl
openssl req -new -x509 -days 365 -nodes -text -subj '/CN=postgres' -out ~/openssl/server.crt -keyout ~/openssl/server.key
chmod 600 ~/openssl/server.key

创建过程

[postgres@centos7 ~]$ mkdir ~/openssl
[postgres@centos7 ~]$ openssl req -new -x509 -days 365 -nodes -text -subj '/CN=postgres' -out ~/openssl/server.crt -keyout ~/openssl/server.key
Generating a 2048 bit RSA private key
.............+++
.+++
writing new private key to '/home/postgres/openssl/server.key'
-----
[postgres@centos7 ~]$ chmod 600 ~/openssl/server.key
[postgres@centos7 ~]$ ll openssl/
total 8
-rw-rw-r-- 1 postgres postgres 4058 Nov 11 08:16 server.crt
-rw------- 1 postgres postgres 1704 Nov 11 08:16 server.key
[postgres@centos7 ~]$

2、修改postgreql.conf配置文件

cat >> /postgresql/pgdata/postgresql.conf << "EOF"
ssl = on
ssl_cert_file = '/home/postgres/openssl/server.crt'
ssl_key_file = '/home/postgres/openssl/server.key'
EOF

参数说明

参数 说明
ssl 支持SSL连接。默认是关闭的。这个参数只能在服务器启动时设置。SSL通信只能通过TCP/IP连接进行。
ssl_cert_file 指定包含SSL服务器证书的文件的名称。默认是server.crt,相对路径相对于数据目录$PGDATA。此参数只能在服务器启动时设置。
ssl_key_file 指定包含SSL服务器私钥的文件的名称。默认是server.key,相对路径相对于数据目录。此参数只能在服务器启动时设置。

要在SSL模式下启动,必须存在包含服务器证书和私钥的文件。默认情况下,这些文件将被命名为server.crt和server.key。但是可以使用配置参数ssl_cert_file和ssl_key_file指定其他名称和位置。

在linux系统中,server.key必须禁止其他用户的访问权限。我们需要通过chown命令将server.key的访问权限设置成600。

SSL打开后,此时服务器将侦听同一TCP端口上的正常连接和SSL连接,并与任何连接客户机协商是否使用SSL。

3、重启数据库,创建sslinfo扩展,验证ssl连接

pg_ctl start
psql -c "create extension sslinfo;"


[postgres@centos7 ~]$ psql -c "\dx"
                    List of installed extensions
  Name   | Version |   Schema   |            Description             
---------+---------+------------+------------------------------------
 plpgsql | 1.0     | pg_catalog | PL/pgSQL procedural language
 sslinfo | 1.2     | public     | information about SSL certificates
(2 rows)

连接的时候需要加上-h参数,否则不是以ssl连接的

[postgres@centos7 ~]$ psql -U postgres
psql (12.17)
Type "help" for help.

postgres=# select ssl_is_used();
 ssl_is_used 
-------------
 f
(1 row)
postgres=# exit
[postgres@centos7 ~]$
[postgres@centos7 ~]$ psql -h localhost
psql (12.17)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# select ssl_is_used();
 ssl_is_used 
-------------
 t
(1 row)

postgres=# exit
[postgres@centos7 ~]$
[postgres@centos7 ~]$ psql 'host=localhost user=postgres dbname=postgres password=postgres sslmode=require'
psql (12.17)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# select ssl_is_used();
 ssl_is_used 
-------------
 t
(1 row)

postgres=# exit
[postgres@centos7 ~]$ 

配置双向SSL认证连接

双向SSL认证配置我们需要根服务器来为客户端、数据库颁发证书。

服务器端证书配置

服务器端需生成三个文件: root.crt(根证书)server.crt(服务器证书)server.key(服务器私钥)

1、生成服务器私钥server.key

服务器私钥生成后,需移除密码,否则数据库重启时会出现异常。

cd $PGDATA
openssl genrsa -des3 -out server.key 2048
openssl rsa -in server.key -out server.key  #去掉证书的密码保护
chmod 400 server.key

目录根据实际情况,openssl 就是 openssl.org,genrsa 是 generate an RSA private key,des3 是加密算法。意思是生成一个 RSA 密钥,再将密钥用 des3 算法加密。

2、生成服务器证书server.crt

openssl req -new -key server.key -days 3650 -out server.crt -x509 -subj '/C=CN/ST=Shaanxi/L=Xian/O=163.com/CN=test/emailAddress=test@163.com'

subj参数说明:用于指定证书请求或自签名证书的主题信息。主题信息包括一系列的字段,如国家、组织、单位、通用名称等。下面是openssl -subj参数的可用选项:

全称 缩写 说明
Country Name (2 letter code) [AU] /C 指定证书的国家代码,例如/C=CN表示中国
State or Province Name (full name) [Some-State] /ST 指定证书的省/州
Locality Name (eg, city) [] /L 指定证书的城市/地区
Organization Name (eg, company) [Internet Widgits Pty Ltd] /O 指定证书的组织名称
Organizational Unit Name (eg, section) [] /OU 指定证书的组织单位
Common Name (e.g. server FQDN or YOUR name) [] /CN 指定证书的通用名称,即证书的域名
Email Address [] /emailAddress 指定证书的电子邮件地址
/serialNumber= 指定证书的序列号
/DN 指定证书的颁发者名称
/DC 指定证书的颁发者组织单位
/DNQ 指定证书的颁发者通用名称
/DNQUALIFIER 指定证书的颁发者限定符
/DNEMAIL 指定证书的颁发者电子邮件地址

3、根证书root.crt

由于没有公证机构提供,只能使用自签名证书,因此可以将服务器证书作为根证书

cp server.crt root.crt

服务器端数据库配置

1、postgresql.conf

cat >> /postgresql/pgdata/postgresql.conf << "EOF"
ssl=on
ssl_ca_file='root.crt'
ssl_key_file='server.key'
ssl_cert_file='server.crt'
EOF

2、pg_hba.conf

更改服务器的pg_hba.conf文件禁止用户以非SSL连接数据库。

PostgreSQL的客户机身份验证由一个配置文件控制,该配置文件通常名为pg_hba.conf,存储在数据库的数据目录中。(HBA代表基于主机的身份验证。),当initdb初始化数据目录时,将安装一个默认的pg_hba.conf文件。

pg_hba.conf文件的一般格式是一组记录,每行一个。其中每个记录指定连接类型、客户机IP地址范围(连接类型相关)、数据库名、用户名和用于匹配这些参数的连接的身份验证方法。具有匹配的连接类型、客户端地址、请求的数据库和用户名的第一条记录用于执行身份验证。不存在“穿透”或“备份”, 如果选择了一条记录,身份验证失败,则不考虑后续记录。如果没有记录匹配,则拒绝访问。

pg_hba.conf与ssl相关的配置有两个。

  • hostssl: 此记录匹配使用TCP/IP进行的连接尝试,但仅在使用SSL加密进行连接时才匹配。要使用此选项,必须使用SSL支持构建服务器。此外,必须通过设置SSL配置参数在服务器启动时启用SSL。
  • hostnossl:此记录类型具有与hostssl相反的行为;它只匹配不使用SSL的TCP/IP上的连接尝试。

使用使用host的话优先使用ssl认证,hostssl表示强制使用ssl, hostnossl 强制不使用ssl。

cat >> /postgresql/pgdata/pg_hba.conf <<"EOF"
hostssl all all 0.0.0.0/0 cert
EOF


[postgres@centos7 ~]$ egrep -v "^$|^#" /postgresql/pgdata/pg_hba.conf
local   all             all                                     trust
host    all             all             127.0.0.1/32            trust
host    all             all             ::1/128                 trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust
hostssl all all 0.0.0.0/0 cert

重启数据库

pg_ctl restart

普通连接会报错

[postgres@testLinux ~]$ psql -U postgres -h 192.168.1.150
psql: error: FATAL:  connection requires a valid client certificate
FATAL:  no pg_hba.conf entry for host "192.168.1.200", user "postgres", database "postgres", SSL off
[postgres@testLinux ~]$

客户端配置SSL证书

开启客户端SSL连接也需要三个文件

  • root.crt (trusted root certificate) :root.crt(根证书)
  • postgresql.crt (client certificate) :postgresql.crt(客户端证书)
  • postgresql.key (private key) :postgresql.key(客户端私钥)

生成postgresql.key

cd $PGDATA
openssl genrsa -des3 -out postgresql.key 2048
openssl rsa -in postgresql.key -out postgresql.key  #去掉证书的密码保护

生成postgresql.crt

openssl req -new -key postgresql.key -out postgresql.csr -subj '/C=CN/ST=Shaanxi/L=Xian/O=163.com/CN=postgres/emailAddress=test@163.com'

openssl x509 -req -in postgresql.csr -CA root.crt -CAkey server.key -out postgresql.crt -CAcreateserial

说明:Common Name (e.g. server FQDN or YOUR name) []:postgres该项必须设置为要连接postgresql数据库的用户名,否则会默认使用当前计算机的用户名,导致证书使用时,认证失败。

测试连接

Linux

psql客户端使用ssl连接。

可以配置环境变量PGSSLCERT和PGSSLKEY来指定密钥和证书的位置。

在Linux环境中,需要将证书放在当前用户的.postgresql目录下

#将证书拷贝到客户端
su - postgres
mkdir .postgresql
scp 192.168.1.150:/postgresql/pgdata/root.crt /home/postgres/.postgresql/
scp 192.168.1.150:/postgresql/pgdata/postgresql.key /home/postgres/.postgresql/
scp 192.168.1.150:/postgresql/pgdata/postgresql.crt /home/postgres/.postgresql/
chmod 600 root.crt postgresql.key postgresql.crt

#使用以下命令测试连接(正常连接)
psql 'host=192.168.1.150 user=postgres dbname=postgres password=postgres sslmode=require'
psql "postgresql://postgres@192.168.1.150:5432/postgres?sslmode=require"
psql "postgresql://postgres@192.168.1.150:5432/postgres?ssl=true"
psql postgresql://postgres@192.168.1.150:5432/postgres?ssl=true

执行过程

[postgres@testLinux ~]$ pwd
/home/postgres
[postgres@testLinux ~]$ ll /home/postgres/.postgresql
total 12
-rw-------. 1 postgres postgres 1220 Nov 12 08:08 postgresql.crt
-rw-------. 1 postgres postgres 1675 Nov 12 08:07 postgresql.key
-rw-------. 1 postgres postgres 1334 Nov 12 08:06 root.crt
[postgres@testLinux ~]$
[postgres@testLinux ~]$ psql 'host=192.168.1.150 user=postgres dbname=postgres password=postgres sslmode=require'
psql (13.11, server 12.17)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# exit
[postgres@testLinux ~]$ psql "postgresql://postgres@192.168.1.150:5432/postgres?sslmode=require"
psql (13.11, server 12.17)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# exit
[postgres@testLinux ~]$ psql "postgresql://postgres@192.168.1.150:5432/postgres?ssl=true"
psql (13.11, server 12.17)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# exit
[postgres@testLinux ~]$ psql postgresql://postgres@192.168.1.150:5432/postgres?ssl=true
psql (13.11, server 12.17)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# exit
[postgres@testLinux ~]$

证书不在当前用户的.postgresql目录下,例如在/tmp目录下

cd /home/postgres/.postgresql
cp postgresql.crt postgresql.key root.crt /tmp/
rm -rf /home/postgres/.postgresql

psql postgresql://postgres@192.168.1.150:5432/postgres?ssl=true\&sslrootcert=/tmp/root.crt\&sslkey=/tmp/postgresql.key\&sslcert=/tmp/postgresql.crt

执行过程

[postgres@testLinux ~]$ ll /tmp/
total 12
-rw-------. 1 postgres postgres 1220 Nov 12 08:13 postgresql.crt
-rw-------. 1 postgres postgres 1675 Nov 12 08:13 postgresql.key
-rw-------. 1 postgres postgres 1334 Nov 12 08:13 root.crt
[postgres@testLinux ~]$ 
[postgres@testLinux ~]$ rm -rf /home/postgres/.postgresql
#.postgresql目录已经删除,所以以下连接会报错
[postgres@testLinux ~]$ psql 'host=192.168.1.150 user=postgres dbname=postgres password=postgres sslmode=require'
psql: error: FATAL:  connection requires a valid client certificate
[postgres@testLinux ~]$ 
#指定证书位置连接
[postgres@testLinux ~]$ psql postgresql://postgres@192.168.1.150:5432/postgres?ssl=true\&sslrootcert=/tmp/root.crt\&sslkey=/tmp/postgresql.key\&sslcert=/tmp/postgresql.crt
psql (13.11, server 12.17)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# exit
[postgres@testLinux ~]$

Windows

在Windows环境中,可以将证书和密钥文件放在%appdata%\postgresql这个文件夹下

C:\Users\Administrator>echo %appdata%
C:\Users\Administrator\AppData\Roaming

C:\Users\Administrator>cd C:\Users\Administrator\AppData\Roaming

C:\Users\Administrator\AppData\Roaming>mkdir postgresql

C:\Users\Administrator\AppData\Roaming>cd postgresql

C:\Users\Administrator\AppData\Roaming\postgresql>dir/b
postgresql.crt
postgresql.key
root.crt

navicat连接测试:

root.crtpostgresql.crtpostgresql.key拷贝到%appdata%\postgresql这个文件夹后,不用输入密码就能连接

image-20241112195810883

删除%appdata%\postgresql这个文件夹后,指定证书位置:

使用普通连接方式将无法连接:

image-20241112194356222

需要使用SSL连接:

image-20241112194504242

image-20241112194730407

SSL加密等级说明

加密等级 说明
disable 只尝试非SSL连接
allow 首先尝试非SSL连接,若失败再尝试SSL连接
prefer (default) 首先尝试SSL连接,若失败再尝试非SSL连接
require 只尝试SSL连接,若有根证书存在,等同于verify-ca
verify-ca 只尝试SSL连接,并用根证书验证服务器证书是不是根CA签发的
verify-full 只尝试SSL连接,并用根证书验证服务器证书是不是根CA签发的,且主题必须匹配连接域名或IP地址

SSL 模式描述

sslmode 窃听保护 MITM保护 声明
disable No No 我不关心安全性,并且我不想为加密增加负荷。
allow 可能 No 我不关心安全性,但如果服务器坚持,我将承担加密带来的负荷。
prefer 可能 No 我不关心安全性,但如果服务器支持,我希望承担加密带来的负荷。
require Yes No 我想要对数据加密,并且我接受因此带来的负荷。我信任该网络会保证我总是连接到想要连接的服务器。
verify-ca Yes Depends on CA policy 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器。
verify-full Yes Yes 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器,并且就是我指定的那一个。

verify-caverify-full之间的区别取决于根CA的策略。如果使用了一个公共CAverify-ca允许连接到那些可能已经被其他人注册到该CA的服务器。在这种情况下,总是应该使用verify-full。如果使用了一个本地CA或者甚至是一个自签名的证书,使用verify-ca常常就可以提供足够的保护。

sslmode的默认值是prefer。如表中所示,这在安全性的角度来说没有意义,并且它只承诺可能的性能负荷。提供它作为默认值只是为了向后兼容,并且我们不推荐在安全部署中使用它。

参考资料

https://cloud.tencent.com/developer/information/openssl -subj参数有哪些可用选项-album

上一篇:2025年最佳内网企业办公平台与工具


下一篇:【C语言】js写一个冒泡顺序