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