PG网络传输安全SSL介绍及使用示例
SSL 概念介绍
1.1 SSL介绍
用 SSL 进行安全的 TCP/IP 连接,对网络传输的数据进行加密,保证网络传输中数据的安全。
PG有一个对使用 SSL 连接加密客户端/服务器通讯的本地支持,它可以增加安全性。这个特性要求在客户端和服务器端都安装 OpenSSL 并且在编译的时候打开这个支持。
编译配置时增加--with-openssl参数,让postgresql支持ssl认证方式,即
./configure ... --with-openssl
1.2 openssl 的req 参数说明
openssl 的req 参数说明:
-new :创建一个证书请求文件,会交互式提醒输入一些信息,这些交互选项以及交互选项信息的长度值以及其他一些扩展属性在配置文件(默认为
:openssl.cnf,还有些辅助配置文件)中指定了默认值。如果没有指定"-key"选项,则会自动生成一个RSA私钥,该私钥的生成位置
:也在openssl.cnf中指定了。如果指定了-x509选项,则表示创建的是自签署证书文件,而非证书请求文件
-x509 :指定该选项时,将生成一个自签署证书,而不是创建证书请求。一般用于测试或者为根CA创建自签名证书
-days n :指定自签名证书的有效期限,默认30天,需要和"-x509"一起使用。
-nodes :默认情况下,openssl req自动创建私钥时都要求加密并提示输入加密密码,指定该选项后则禁止对私钥文件加密
-text :以文本格式打印证书请求
-out filename :证书请求或自签署证书的输出文件,也可以是其他内容的输出文件,不指定时默认stdout
-keyout filename :指定自动创建私钥时私钥的存放位置,若未指定该选项,则使用配置文件中default_keyfile指定的值,默认该值为privkey.pem
-subj args :替换或自定义证书请求时需要输入的信息,并输出修改后的请求信息。args的格式为"/type0=value0/type1=value1...",
:如果value为空,则表示使用配置文件中指定的默认值,如果value值为".",则表示该项留空。其中可识别type(man req)有:
:C是Country、ST是state、L是localcity、O是Organization、OU是Organization Unit、CN是common name等
1.3 SSL 请求模式说明
sslmode参数的不同值提供了不同的保护级别。具体参数值如下:
verify-ca和verify-full之间的不同是根据root CA的政策。 如果使用的是一个公用CA,verify-ca允许那些带有CA 注册的客户端对服务器进行连接访问。在这种情况下,应该使用verify-full。 如果使用的是一个本地CA,甚至是一个自签名证书, 使用verify-ca通常会提供充分的保护。
sslmode缺省值是prefer。如在表中说明的那样, 从安全角度来看这样做是没有意义的,并且如果可能的话,它只承诺性能的开销。 它仅提供了缺省向后兼容性,在安全部署中不建议使用。
1.4密码套件类别
PostgreSQL 配置参数 ssl_ciphers 指定 SSL 连接允许的密码套件的类别。
指定允许 SSL 连接使用的SSL密码套件列表。 。只有使用 TLS 1.2 及更低版本的连接会受到影响。当前没有控制 TLS 1.3 版连接使用的密码选择的设置。默认值为"HIGH:MEDIUM:+3DES:!aNULL"。除非您有特定的安全要求,否则默认值通常是一个合理的选择。
此参数只能在postgresql.conf文件中或服务器命令行中设置。
默认值说明:
HIGH : 使用HIGH组密码的密码套件(例如,AES、Camellia、3DES)
MEDIUM : 使用MEDIUM组密码的密码套件(例如,RC4、SEED)
+3DES : OpenSSL的默认顺序是有问题的,因为它对 3DES的排序HIGH高于 AES128。这是错误的,因为 3DES 提供的安全性低于 AES128,而且速度也慢得多。在所有其他和密码+3DES之后重新排序。HIGHMEDIUM
!aNULL : 禁用不进行身份验证的匿名密码套件。此类密码套件易受MITM攻击,因此不应使用。
可用的密码套件详细信息因OpenSSL版本而异。使用该命令openssl ciphers -v 'HIGH:MEDIUM:+3DES:!aNULL'查看当前安装的OpenSSL版本的实际详细信息。请注意,此列表在运行时根据服务器密钥类型进行过滤。
SSL 类型介绍
2.1自签名私有证书
自证身份不可信,只有加密功能,用于无需身份证明的场合。
证书颁发者和主题是一样,自己给自己颁发,此类证书不需要根证书认证,所以是不安全的,是无法防止他人伪装成服务器,但如果服务器是按IP地址连接的,也许对这项要求不是很注重,所以自签名证书就够了。
openssl req -new -x509 -days 365 -nodes -text -out server.crt -keyout server.key -subj "/CN=xx.xx.xx"
CN=后面是 证书主题如域名或IP地址,此命令生成证书文件server.crt 和私钥文件 server.key ,请将它们部署在数据库服务器上,并设好ssl_cert_file、ssl_key_file参数。
2.2 自签名CA证书
CA签名的证书兼具“身份证明”和“加密”双重功能 ,支持吊销列表 。
建立本地根CA,会生成两个有用的文件 root.key (根CA的私钥,请离线保存), root.crt(根CA证书)。
openssl req -new -nodes -text -out root.csr -keyout root.key -subj "/CN=XX.XX.XX"
openssl x509 -req -in root.csr -text -days 3650 -extfile openssl.cfg -extensions v3_ca -signkey root.key -out root.crt
由根CA颁发服务器证书, 生成两个有用的文件 server.key(服务器私钥),server.crt(服务器证书)
openssl req -new -nodes -text -out server.csr -keyout server.key -subj "/CN=XX.XX.XX"
openssl x509 -req -in server.csr -text -days 365 -CA root.crt -CAkey root.key -CAcreateserial -out server.crt
请将server.key和server.crt部署在服务器,本地根证书root.crt部署在客户端,PostgreSQL客户端不依赖操作系统的证书,需要自行制定根证书的存放地方,所以即使是自行发行的根证书,也认为是合法的。在windows下根证书默认存放目录是appdata/postgresql目录下。
2.3 权威机构的CA证书
使用机构签发的证书时候需要联网签发 , 大部分要收使用费,一般用在有域名的服务器上。申请后一般会得三个文件服务器私钥server.key、服务器证书server.crt、颁布者CA自己的证书。
请将server.key和server.crt部署在服务器。
客户端的公共root.crt可从 libcurl网站获取最新的pem文件,此文件含有目前世界已知所有CA的证书,将此文件改名为root.crt,并部署在客户端的$appdata/postgresql目录下,若在sslmode=verify-ca模式登陆失败,可能是颁布者CA的证书不含在root.crt里,可将颁布者CA的证书连接到root.crt(文本格式可编辑),也可连接到部署在服务器的服务器证书server.crt,建议后者。
测试验证
3.1 使用自签名私有证书
3.1.1 证书准备
要为服务器创建一个有效期为365天的简单自签名证书,可以使用下面的OpenSSL命令,将dbhost.yourdomain.com替换为服务器的主机名:
openssl req -new -x509 -days 365 -nodes -text -out server.crt \
-keyout server.key -subj "/CN=dbhost.yourdomain.com"
# 然后执行:
chmod og-rwx server.key
如果文件的权限比这个更自由,服务器将拒绝该文件。尽管可以使用自签名证书进行测试,但是在生产中应该使用由证书颁发机构(CA)(通常是企业范围的根CA)签名的证书。
3.1.2 配置ssl
#1. 将server.crt和server.key移动到$PGDATA
mv server.crt server.key $PGDATA
#2. 配置postgresql.conf. 打开ssl
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'
#3. 配置pg_hba.conf, 强制让客户端使用ssl连接数据库
hostssl all all 0.0.0.0/0 md5
#4. 重新加载数据库
pg_ctl reload
3.1.3 客户端连接
#客户端连接
psql -h192.168.146.133
Password for user postgres:
psql (12.3)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=#
# 通过指定sslmode参数, 强制使用或不使用ssl的方法。 或者通过export PGSSLMODE=disable 设置环境变量方式实现
psql "sslmode=require" -h 192.168.146.133
psql "sslmode=disable" -h 192.168.146.133
# 查看客户端调用的libssl库
[root@localhost ~]# lsof|grep psql|grep ssl
psql 27420 pg12 memREG 8,3 470376107550 /usr/lib64/libssl.so.1.0.2k
3.2 使用自签名CA证书配置ssl
3.2.1 证书准备
- 1.要创建其身份可以被客户端验证的服务器证书,首先创建一个证书签名请求(CSR)和一个公共/专用密钥文件:
openssl req -new -nodes -text -out root.csr -keyout root.key -subj "/CN=root.yourdomain.com"
chmod og-rwx root.key
- 2.使用密钥对请求进行签名以创建根证书颁发机构(使用Linux上的默认OpenSSL配置文件位置,可以通过openssl version -d查询cnf所在目录):
openssl x509 -req -in root.csr -text -days 3650 \
-extfile /etc/pki/tls/openssl.cnf -extensions v3_ca \
-signkey root.key -out root.crt
- 3.创建由新的根证书颁发机构签名的服务器证书:
openssl req -new -nodes -text -out server.csr \
-keyout server.key -subj "/CN=dbhost.yourdomain.com"
chmod og-rwx server.key
openssl x509 -req -in server.csr -text -days 365 \
-CA root.crt -CAkey root.key -CAcreateserial \
-out server.crt
- 4.前三步会生成下面几个文件,server.crt和server.key应该存储在服务器上,并且root.crt应该存储在客户端上,以便客户端可以验证服务器的叶证书已由其受信任的根证书签名。root.key应该离线存储以用于创建将来的证书。
-rw-------. 1 pg12 pg12 1704 Sep 13 22:09 root.key
-rw-rw-r--. 1 pg12 pg12 3338 Sep 13 22:09 root.csr
-rw-rw-r--. 1 pg12 pg12 1123 Sep 13 22:12 root.crt
-rw-------. 1 pg12 pg12 1704 Sep 13 22:12 server.key
-rw-rw-r--. 1 pg12 pg12 3344 Sep 13 22:12 server.csr
-rw-rw-r--. 1 pg12 pg12 17 Sep 13 22:13 root.srl
-rw-rw-r--. 1 pg12 pg12 1005 Sep 13 22:13 server.crt
3.2.2 配置ssl
#1. 将server.crt和server.key移动到$PGDATA
mv server.crt server.key $PGDATA
# 将root.crt应该存储在客户端上,注意默认目录, 在windows下根证书默认存放目录是appdata/postgresql/目录下, 或通过 export PGSSLROOTCERT 设定
mv root.crt /home/pg12/.postgresql/
#2. 配置postgresql.conf. 打开ssl,如果需要验证客户端是否可信,需要设置ssl_ca_file。
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'
# 3. 配置pg_hba.conf, 强制让客户端使用ssl连接数据库
hostssl all all 0.0.0.0/0 md5
# 4. 重新加载数据库
pg_ctl reload
3.2.3 客户端连接
verify-ca 方式
export PGSSLMODE="verify-ca"
export PGSSLROOTCERT="/home/pg12/.postgresql/root.crt"
psql -h192.168.146.133
Password for user postgres:
psql (12.3)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=#
补充:
export PGSSLMODE="verify-ca"
export PGSSLROOTCERT="/home/pg12/.postgresql/root.crt"
客户端连接时,需要将颁证机构证书root.crt放入数据库安装用户~/.postgresql/目录下(需手工创建)。若使用非缺省目录,可以设置PGSSLROOTCERT环境变量。连接时需要设置PGSSLMODE变量为verify-ca或verify-full。
java程序连接数据库时,可以在jdbc-url连接串中指定sslmode、sslrootcert参数。
verify-full 方式
verify-full会验证证书中的CN,证书中为dbhost.yourdomain.com,因此访问ip地址不让连接。
export PGSSLMODE="verify-full"
export PGSSLROOTCERT="/home/pg12/.postgresql/root.crt"
psql -h192.168.146.133
psql: error: could not connect to server: server certificate for "dbhost.yourdomain.com" does not match host name "192.168.146.133"
# 编辑客户端服务器/etc/hosts,将192.168.146.133和dbhost.yourdomain.com绑定
export PGSSLMODE="verify-full"
export PGSSLROOTCERT="/home/pg12/.postgresql/root.crt"
psql -hdbhost.yourdomain.com
Password for user postgres:
psql (12.3)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=#
补充知识
4.1 SSL网络查看
我们可以使用 sslinfo插件查看ssl网络的传输情况
# 1. 安装sslinfo插件
postgres=> create extension sslinfo;
# 2. 查询ssl相关函数
postgres=> select pg_backend_pid(), ssl_is_used(), ssl_version(), ssl_cipher();
pg_backend_pid | ssl_is_used | ssl_version | ssl_cipher
----------------+-------------+-------------+-----------------------------
30147 | t | TLSv1.2 | ECDHE-RSA-AES256-GCM-SHA384
(1 row)
# 3. 查询ssl状态视图
postgres=> select * from pg_stat_ssl;
pid | ssl | version | cipher | bits | compression | client_dn | client_serial | issuer_dn
-------+-----+---------+-----------------------------+------+-------------+-----------+---------------+-----------
26155 | | | | | | | |
26158 | | | | | | | |
26159 | | | | | | | |
26164 | | | | | | | |
29501 | | | | | | | |
30147 | t | TLSv1.2 | ECDHE-RSA-AES256-GCM-SHA384 | 256 | f | | |
26153 | | | | | | | |
26152 | | | | | | | |
26154 |
4.2 流复制采有SSL
在主从环境中, 主库使用SSL后,也需要将server.crt ,server.key文件 复制到备库的$PGDATA目录,同时需要手动更改SSL参数(参数不会同步) ,这样避免在切换的时候,应用连接异常.
一般情况,流复制连接建议就采用普通的host 访问方式
# 主库的 pg_hba.conf 配置
host replication repluser 192.168.1.0/24 md5
# 从库的连接配置
primary_conninfo='host=192.168.1.71 port=5432 user=repluser password=repluser keepalives_idle=60'
如果我们把它改成SSL的方式,需要更改
# 主库的 pg_hba.conf 配置
hostssl replication repluser 192.168.1.0/24 md5
# 重新加载数据库
pg_ctl reload
# 从库的连接配置增加 sslmode 参数, 如果要压缩可以增加 sslcompression=1 参数
primary_conninfo='host=192.168.1.71 port=5432 user=repluser password=repluser keepalives_idle=60 sslmode=require '
# 重新加载数据库
pg_ctl reload
# 查ssl状态
postgres=# select * from pg_stat_ssl;
pid | ssl | version | cipher | bits | compression | client_dn | client_serial | issuer_dn
------+-----+---------+-----------------------------+------+-------------+-----------+---------------+-----------
3395 | f | | | | | | |
3398 | f | | | | | | |
3399 | f | | | | | | |
3955 | t | TLSv1.2 | ECDHE-RSA-AES256-GCM-SHA384 | 256 | f | | |
#postgres@s2ahumysqlpg01-> ps -ef | grep 3955
postgres 3955 3382 0 13:30 ? 00:00:00 postgres: walsender repluser 192.168.1.56(33244) streaming 1/990004B0
参考文档:
http://www.postgres.cn/docs/13/ssl-tcp.html
http://www.postgres.cn/docs/13/libpq-ssl.html
https://www.modb.pro/db/111308