Centos7上的PostgreSQL开启SSL配置

Centos7上的PostgreSQL开启SSL配置

PostgreSQL支持使用SSL连接加密Client和Server之间的通信,以提高安全性。要求在Client和Server两端上都安装OpenSSL、并配置启用PostgreSQL中的SSL的支持。

SSL单向认证和双向认证:

SSL单向认证:只有一个对象校验对端的证书合法性,通常都是客户端来校验服务器的合法性。即一般单向认证只要求服务器端部署了ssl证书就行,客户端可以无证书,任何用户都可以去访问,只是服务端提供了身份认证。
client: 不需要证书,也不需要CA根证书
server: server.crt, server.key

SSL双向认证:客户端和服务端相互校验,服务器需要校验每个客户端、每个客户端也需要校验服务器,只要求服务器和用户双方都有证书才能正常通信,因此只能是服务端允许的客户才能访问服务器。
client: root.crt, postgresql.crt, postgresql.key
server: root.crt, server.crt, server.key

环境信息:Centos 7.8 + PostgreSQL 12.9

1. 安装OpenSSL

Centos7默认已安装了OpenSSL、无需单独安装,如果没有则使用yum安装:

yum install -y openssl, openssl-devel

查看openssl版本
openssl version -a

openssl主要参数:
req          大致有3个功能:生成证书请求文件、验证证书请求文件和创建根CA
-x509        说明生成自签名证书
-nodes       openssl req在自动创建私钥时,将总是加密该私钥文件,并提示输入加密的密码。可以使用"-nodes"选项禁止加密私钥文件
-days        指定所颁发的证书有效期(单位:天)
-new         新的请求
-newkey      直接指定私钥的算法和私钥长度,例如:-newkey rsa:2048 生成一个2048长度的RSA私钥文件、用于签发
             实际上,"-x509"选项和"-new"或"-newkey"配合使用时,可以不指定证书请求文件,它在自签署过程中将在内存中自动创建证书请求文件。"-newkey"和"-new"选项类似,只不过"-newkey"选项可以指定私钥的算法和长度
-keyout      指定私钥保存位置(含文件名)
-out         新的证书请求文件位置(含文件名)
-config      指定req的openssl配置文件,指定后将忽略所有的其他配置文件。如果不指定则默认使用 /etc/pki/tls/openssl.cnf 中 req段落的值
-text        text显示格式
-key         用于签名待生成的请求证书的私钥文件
-subj        设置CA证书subject

详细openssl参数请使用命令帮助查看 man openssl


默认情况下PostgreSQL 读取openssl的配置文件openssl.cnf(在openssl version -d 返回的目录 /etc/pki/tls 中)
$ openssl version -d
OPENSSLDIR: "/etc/pki/tls"

2.开启SSL配置

我没有使用第三方权威机构的CA来认证,而是自己充当CA的角色,自己签名证书。虽然可以使用自签名证书进行测试,但如果在生产环境中使用,还是推荐请使用正规的证书颁发机构(CA)(通常是企业范围的根CA)签名的证书。

2.1)自签名的方式创建服务端证书私钥文件(server.key) & 服务端证书(server.crt):
注意:
1)证书和私钥文件一定要放到数据目录 $PGDATA 目录下
2)设置过程会提示设置证书密码,密码必须设置,如果不想设置密码,可以等文件生成以后执行下面命令删除密码:openssl rsa -in server.key -out server.key。
3)创建过程中会同时提示相关信息设置,证书相关信息设置可以根据实际情况设置,全部回车则使用默认值

su - postgres
cd $PGDATA

生成私钥(server.key)
openssl genrsa -des3 -out server.key 2048

删除私钥中的密码:
openssl rsa -in server.key -out server.key
如果不删除server.key的密码,我启动数据库会报这个错:FATAL:  could not load private key file "server.key": problems get... password

创建服务器证书(server.key):
-x509生成一个自签名证书,而不是证书请求csr,提示配置信息根据实际填写,我直接回车未指定
openssl req -new -key server.key -days 3650 -out server.crt -x509 

除了Country Name指定为CN外,其他直接默认回车:
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:
Email Address []:


由于是自签名,我们使用服务器证书作为受信任的根证书(root.crt)
cp server.crt root.crt

查看证书私钥文件中的私钥和公钥信息:
openssl rsa -in server.key -text

查看证书详细信息:
openssl x509 -in server.crt -noout -text

禁止其他用户的访问server.key:
chmod 600 server.key

如果不禁止其他用户访问server.key,启动数据库会报错:
DETAIL:  File must have permissions u=rw (0600) or less if owned b... by root.

2.2)修改PostgreSQL配置开启SSL

修改postgres.conf,配置SSL参数
vi postgres.conf
ssl = on                                 #支持SSL,默认off(关闭)。该参数只能在Server启动时设置。SSL通信只能通过TCP/IP连接进行。
ssl_ca_file = 'root.crt'                 #指定根证书,SSL单项认证时也可以不配置、SSL双向认证必须配置
ssl_cert_file = 'server.crt'             #指定包含SSL服务器证书的文件的名称。默认是server.crt。相对路径相对于数据目录($PGDATA)。该参数只能在Server启动时设置。
ssl_key_file = 'server.key'              #指定包含SSL服务器私钥的文件的名称。默认是server.key。相对路径相对于数据目录($PGDATA)。该参数只能在Server启动时设置。

如果强制SSL连接(仅允许SSL连接)、不允许普通连接,则修改pg_hba.conf,配置SSL连接认证规则:
vi pg_hba.conf
hostssl      all    all    0.0.0.0/0    md5

如果要同时支持客户端以SSL和普通的非SSL方式连接,则配置成host类型,优先使用SSL,如果客户端强制指定sslmode=disable,则最终以非SSL方式连接:
host      all    all    0.0.0.0/0    md5

如果同时允许SSL但不强制,同时也允许普通非SSL连接,则可以继续使用host类型,无需修改pg_hba.conf规则。


注意:
1)要在SSL模式下启动数据库服务,必须存在包含服务器证书和私钥的文件。默认情况下,这些文件将被命名为server.crt和server.key。但是可以使用配置参数ssl_cert_file和ssl_key_file指定其他名称和位置。
2)在Linux系统中,server.key必须禁止其他用户的访问权限:chown 600 server.key。否则会报错:
    FATAL:  private key file "server.key" has group or world access
    DETAIL:  File must have permissions u=rw (0600) or less if owned b... by root.
3)Server SSL开启后,Server会监听同一TCP端口上的正常连接(host)和SSL连接(hostssl)两种类型的连接,并与任意Client协商是否使用SSL。
4)pg_hba.conf中的Client连接认证规则配置的几种类型:local、host、hostssl、hostnossl
   local:    此记录匹配通过 Unix 域套接字进行的联接企图,没有这种类型的记录,就不允许 Unix 域套接字的联接。
   host:     此记录匹配使用TCP/IP进行的连接尝试,他既匹配通过ssl方式的连接,也匹配通过非ssl方式的连接,会优先使用ssl认证。
   hostssl:  此记录匹配使用TCP/IP进行的连接尝试,但仅在使用SSL加密进行连接时才匹配。hostssl表示强制使用ssl。
   hostnossl:此记录类型具有与hostssl相反的行为:它仅匹配不使用SSL的TCP/IP上的连接尝试。hostnossl表示前置不使用ssl。


2.3)重启数据库生效配置
systemctl restart postgresql-12


2.4)测试SSL

服务端ssl配置好之后,使用客户端连接数据库,创建sslinfo extension, 可以查看一些ssl相关的连接信息:

su - postgresql
psql -U postgres -h 192.168.1.201
create extension sslinfo;
select ssl_is_used();
select ssl_cipher();
select ssl_version();

结果如下:
postgres=# select ssl_is_used();
 ssl_is_used 
-------------
 t
(1 row)

postgres=# select ssl_cipher();
         ssl_cipher          
-----------------------------
 ECDHE-RSA-AES256-GCM-SHA384
(1 row)

postgres=# select ssl_version();
 ssl_version 
-------------
 TLSv1.2
(1 row)


强制使用SSL连接登录:
$ psql "sslmode=require host=192.168.1.201 dbname=postgres"
postgres=# select ssl_is_used();
 ssl_is_used 
-------------
 t

使用非SSL普通登录(需要pg_hba.conf配置host类型的支持):
$ psql "sslmode=disable host=192.168.1.201 dbname=postgres"
postgres=# select ssl_is_used();
 ssl_is_used 
-------------
 f

如果不强制指定sslmode,默认是SSL登录:
$ psql "host=192.168.1.201 dbname=postgres"
postgres=# select ssl_is_used();
 ssl_is_used 
-------------
 t

PostgreSQL 的几种SSL连接模式:
disable:     只尝试非SSL连接。
allow:      首先尝试非SSL连接,若失败再尝试SSL连接。
prefer:     默认模式,首先尝试SSL连接,若失败再尝试非SSL连接。
require:    只尝试SSL连接,若有根证书存在,等同于verify-ca。
verify-ca:  只尝试SSL连接,并用根证书验证服务器证书是不是根CA签发的。
verify-full:只尝试SSL连接,并用根证书验证服务器证书是不是根CA签发的,且主题必须匹配连接域名或IP地址。


我测试时遇到了一个奇怪的问题:
1)我用DBeaver 7.2.0, jdbc驱动:maven:/org.postgresql:postgresql:RELEASE 42.2.5,指定了根证书,但以require模式连接时,根证书存在、但并没有检测根证书,只有verify-ca才会检测根证书。因为我故意防止了一个错误的根证书root.key,居然连接成功了,但以verify-ca模式连接就会报错证书错误。
猜测可能是和驱动版本、或者是DBeaver软件有关。
2)我用另外一个Centos7客户端通过psql连接服务器, /var/lib/pgsql/.postgresql目录下防止了一个错误的根证书root.crt,使用require模式连接时会检查根证书报错:
psql "sslmode=require host=192.168.1.201 dbname=postgres"
psql: error: SSL error: certificate verify failed

psql "sslmode=require host=192.168.1.201 dbname=postgres"
$ psql "sslmode=verify-ca host=192.168.1.201 dbname=postgres"          
psql: error: SSL error: certificate verify failed

$ psql "sslmode=prefer host=192.168.1.201 dbname=postgres"
Password for user postgres: 

Centos7上psql的测试结果是符合预期的,DBeaver的测试结果 require & verify-ca 在指定了 root.crt 的情况下的效果并不等同。

到这里,如果只是配置SSL单行认证,那么到此结束了。无需后续生成客户端SSL证书和配置等操作。
如果配置SSL双向认证,则需要继续生成客户端SSL的相关配置。

2.5)生成客户端SSL配置:
服务端启用SSL后,客户端即使不开启SSL配置进行连接时,默认也是SSL连接。客户端无需任何配置,也能SSL连接,详细原因待后续研究。

既然服务器启用了SSL,建议还是为客户端生成客户后端证书,提供给客户端连接时使用。客户端开启SSL配置连接服务器,需要三个文件:root.key(根证书)、postgresql.crt(客户端证书)、postgresql.key(客户端私钥)。

在服务器端操作、生成客户端需要的文件,即客户后端私钥(postgresql.key)和客户端证书(postgresql.crt):
生成客户端私钥(postgresql.key)
openssl genrsa -des3 -out postgresql.key 2048

删除私钥中的密码:
openssl rsa -in postgresql.key -out postgresql.key

创建客户端证书请求(postgresql.csr) & 签名生成客户后端证书(postgresql.crt):
它必须由我们受信任的根(正在使用服务器端的机器上的服务私钥文件)进行签名。 此外, 证书通用名(CN)必须设置为要连接的数据库用户名
openssl req -new -key postgresql.key -out postgresql.csr

$ openssl req -new -key postgresql.key -out postgresql.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:


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

$ openssl x509 -req -in postgresql.csr -CA root.crt -CAkey server.key -out postgresql.crt -CAcreateserial 
Signature ok
subject=/C=CN/L=Default City/O=Default Company Ltd
Getting CA Private Key


修改文件权限(postgresql.key)
chmod 600 postgresql.key

查看证书私钥文件中的私钥和公钥信息:
openssl rsa -in postgresql.key -text

2.6)拷贝客户端SSL配置文件到客户端机器&客户端远程连接测试

把客户端证书放到客户端的postgres用户的.postgresql目录下
Windows:%appdata%\postgresql\
Linux:~/.postgresql/

客户端远程连接测试:

psql postgresql://[email protected]:5432/postgres?ssl=true
select ssl_is_used();

psql "sslmode=require host=192.168.1.201 dbname=postgres"
select ssl_is_used();

psql "sslmode=disable host=192.168.1.201 dbname=postgres"
select ssl_is_used();

注意:
1)拷贝后需要确认postgresql.key文件权限 & 3个文件的用户组设置()
2)如果客户端的对应目录下放置了CA根证书(root.crt)但根证书正确,此时会导致证书校验失败,psql连接报错:psql: error: SSL error: certificate verify failed

2.7)关于SSL的PostgreSQL官方介绍
不同的PostgreSQL版本,配置上会有一些区别。PostgreSQL 12的SSL配置,官方有关于 Client Verification of Server Certificates & Client Certificates 等详细介绍,请参考:PostgreSQL: Documentation: 12: 33.18. SSL Support:https://www.postgresql.org/docs/12/libpq-ssl.html

关于ON和CN字段:
ON:单位名称 (Organization Name),简称ON字段。对于SSL证书,一般为将要申请SSL证书的网站域名(Domain)或子域名(Subdomain);而对于代码签名证书则为申请单位名称;而对于客户端单位证书则为证书申请者所在单位名称。
CN: 公用名称 (Common Name),简称CN字段,只能是国家字母缩写,如中国:CN。对于SSL证书,一般为将要申请SSL证书的网站域名(Domain)或子域名(Subdomain);而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名。

Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:

如果是浏览器通过HTTPS连接到您的服务器时,他们会检查以确保您的SSL证书与地址栏中的主机名称匹配,有三种匹配方式:
1.The host name (in the address bar) exactly matches the Common Name in the certificate's Subject.   //CN 精确匹配
2.The host name matches a Wildcard Common Name. For example, www.example.com matches the common name *.example.com.  //CN通配符匹配,例如:*.test.com
3.The host name is listed in the Subject Alternative Name field. //主题备用名称(SAN: Subject Alternative Name)字段中列出

如果是在实际生产环境中使用,则根据自己的实际情况填写。


3.其他-证书相关操作命令

查看证书私钥文件中的私钥和公钥信息:
openssl rsa -in server.key -text

查看证书详细信息:
openssl x509 -in server.crt -noout -text

本地使用自签名CA根证书来分别校验由自己颁发的服务器证书server.crt和客户端证书postgresql.crt
openssl verify -CAfile root.crt server.crt
openssl verify -CAfile root.crt postgresql.crt

你可能感兴趣的:(数据库,postgresql,ssl,数据库)