PostgreSQL 使用 ---- 配置 SSL/TLS/TLCP 加密传输

SSL 简介

PostgreSQL 有一个对使用 SSL 连接加密客户端/服务器通讯的本地支持,它可以增加安全性。这个特性要求在客户端和服务器端都安装 OpenSSL (如果需要支持 GM 算法,可以安装 GmSSL 或者 TONGSUO 开源密码库)并且在编译 PostgreSQL 的时候打开这个支持(–with-openssl)。

术语 SSL 和 TLS 经常被互换使用,意思是使用 TLS 协议的安全加密连接。 SSL 协议是 TLS 协议的前身,即使 SSL 协议不再受支持,术语 SSL 仍然用于加密连接。 在PostgreSQL中,SSL 与 TLS 可以互换使用。

POSTGRESQL12.3 以上支持的协议版本为 TLSv1, TLSv1.1, TLSv1.2, TLSv1.3。也就是说 PG 已经不支持使用SSL协议了。可以通过配置参数 ssl_min_protocol_version 和 ssl_max_protocol_version 限制数据库可以使用的协议版本,分别是最小版本号和最大版本号。

  • ssl_min_protocol_version 的默认值为:TLSv1
  • ssl_max_protocol_version 的默认值为:TLS_ANY

SSL 是 TCP/IP 连接,如果是同一台 Linux 服务器上运行客户端和服务器,那么是无法建立 SSL 连接的。客户端使用 psql 建立连接时一定要使用 “-h 127.0.0.1” 或 “-h 真实IP地址” ,否则不会启用 SSL 连接。

TLCP 简介

传输层密码协议 Transport Layer Cryptography Protocol (TLCP) 采用 SM 系列密码算法和数字证书等密码技术保障传输层的机密性、完整性、身份认证和抗攻击。TLCP 协议被定义于《GB/T 38636-2020 信息安全技术 传输层密码协议》,于2020年4月发布,在2020年11月实施。

TLCP 参照了 TLSv1.0 规范,整个协议握手和加密国产基本一致,但和 TLSv1.0 并不兼容。主要的不同体现在三个地方:

  1. 协议的版本号不同,握手和加密协议细节不同;
  2. 协议采用的主要是 SM2/SM3/SM4 算法,不同于 TLS 采用的国际密码算法;
  3. 采用的是 SM2 双证书体系(签名证书和加密证书)。

SSL/TLS 单证书逻辑如下
     签名时:用户A(简称A)使用自己的私钥加密信息的摘要,即签名操作;用户 B(简称 B)使用 A 的公钥进行解密,对比该摘要是否正确,若正确,则 B 就确定了 A 的身份,即验签成功。
     加密时:A 用 B 的公钥将信息加密传递给 B,B 使用自己的私钥解密,进而获得信息。

TLCP 双证书:
     签名时:签名证书仅仅用来验证身份使用,其公钥和私钥均由 A 自己产生,并且由自己保管,CA 不负责其保管任务。
     加密时:加密证书在传递加密数据时使用,其私钥和公钥由 CA 产生,并由CA 保管(存根)。

单证书场景:如果私钥丢了,你如何恢复之前得到的信息呢?
双证书的场景:如果签名私钥遗失,用户再产生一对即可,影响不大,因此签名密钥没必要交给 CA;如果加密密钥遗失,别人发过来的信息我就没办法解密了,必须从 CA 那里获取存根。因此从逻辑上,两个密钥具有不同的属性,应该分开处理。

本次 TLCP 示例依赖于"铜锁"项目,支持的密码套件有 TLCP密码套件支持

基本设置

PostgreSQL 数据库通过 SSL 加密传输,使用时在数据库集群目录下存放 CA 证书、服务器证书和服务器私钥文件。同时打开配置文件 postgresql.conf 中 ssl 开关,启动集群后远程传输即可实现加密。

客户端/服务器之间的身份认证依赖于证书。我们首先需要生成自签名的证书,然后分别为服务器和客户端生成各自的证书和私钥。证书相关文件存储位置如下:

服务器端
     CA 的证书 root.crt ,服务器的证书和私钥 server.crt 和 server.key 都应该放在集群目录,例如集群路径是 $HOME/pginstall/bin/data,服务器证书应该存放在同一目录 data 下面。当然也可以放在可以访问的其他路径,通过配置参数(ssl_ca_file、ssl_key_file、ssl_cert_file)进行配置访问。

客户端
     客户端证书 postgresql.crt ,私钥 postgresql.key,以及认证服务器根证书 root.crt 对于 linux 系统应该放在 ~/.postgresql 目录 。
     客户端也可在登录时手动指定。如:./psql "host=127.0.0.1 port=5432 dbname=postgres user=postgres sslrootcert=$HONE/ca.crt sslcert=$HONE/postgresql.crt sslkey=$HONE/postgresql.key"
     参数介绍如下:
     sslcert:此参数指定 Client 端 SSL 证书的文件名,以替换默认的 ~/.postgresql/postgresql.crt。如果未构建 SSL 连接,则忽略此参数。
     sslkey:此参数指定用于 Client 端证书的密钥的位置。它可以指定将使用的文件名代替默认的 ~/.postgresql/postgresql.key,也可以指定从外部“引擎”获得的密钥(引擎是可加载 OpenSSL 的模块)。外部引擎规范应由冒号分隔的引擎名称和特定于引擎的键标识符组成。如果未构建 SSL 连接,则忽略此参数。
     sslrootcert:此参数指定包含 SSL 证书颁发机构(CA)证书的文件的名称。如果该文件存在,则将验证服务器的证书是否已由这些机构之一签名。默认值为 ~/.postgresql/root.crt。

客户端证书使用

要求客户端提供受信任的证书,就需要把认证选项 clientcert=verify-ca 或 clientcert=verify-full 加入到 pg_hba.conf 文件中合适的 hostssl 行上。 然后将在 SSL 连接启动时从客户端请求该证书。

对于具有 clientcert=verify-ca 的 hostssl 条目,服务器将验证客户端的证书是否由一个受信任的证书颁发机构签署的。 如果指定了 clientcert=verify-full,则服务器不仅将验证证书链,还将检查用户名或其映射是否与所提供的证书的 cn(通用名称)相匹配。 请注意,在使用 cert 身份验证方法时,要始终确保证书链验证(即所谓的数据库用户级别证书认证)。

clientcert 认证选项适用于所有的认证方法,但仅适用于 pg_hba.conf 中用 hostssl 指定的行。 当clientcert 没有指定,服务器仅在客户端证书存在并且 CA 被配置的时候,通过客户端的 CA 文件来查证客户端证书。

有两种方法可以强制用户在登录时提供证书。

第一种方法是对 pg_hba.conf 文件中的 hostssl 条目使用 cert 身份验证方法,这样证书本身可以用于身份验证,同时提供 ssl 连接安全性。 在这种情况下,证书中 cn(通用名称)将针对用户名或适用的映射进行检查。

第二种方法是将对 hostssl 条目的任何身份验证方法和客户端证书的验证相结合,通过将 clientcert 身份验证选项设置为 verify-ca 或 verify-full。 前一个选项仅强制证书有效,而后者还确保证书中的 cn(通用名称)匹配用户名或适用的映射。

SSL/TLS 配置方法

RSA 算法(OpenSSL)

# 生成server证书,直接用server.crt做root.crt:
openssl genrsa -passout pass:'1qaz!QAZ' -des3 -out server.key 1024
# 去除key的加密
openssl rsa -passin pass:'1qaz!QAZ' -in server.key -out server.key 
# 或者可以直接生成一个未加密的key,直接使用
# openssl genrsa -out server.key 1024

openssl req -new -key server.key -days 3650 -out server.crt -x509 -subj '/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_server'
chmod 400 server.key
cp server.crt root.crt

# 签发client证书:
openssl genrsa -passout pass:'1qaz!QAZ' -des3 -out postgresql.key 1024
openssl rsa -passin pass:'1qaz!QAZ' -in postgresql.key -out postgresql.key
openssl req -new -key postgresql.key -out postgresql.csr -subj '/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_client'
openssl x509 -req -in postgresql.csr -CA root.crt -CAkey server.key -out postgresql.crt -CAcreateserial

chmod 0400 postgresql.key
cp root.crt ~/.postgresql/
cp postgresql.key ~/.postgresql/
cp postgresql.crt ~/.postgresql/

# 设置集群配置文件
vim $PGDATA/postgresql.conf
ssl = on
ssl_ca_file = 'root.crt'
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
# ssl_passphrase_command = 'echo "$SSL_PASSWORD"'
# ssl_passphrase_command = '/bin/systemd-ask-password "%p"'

vim $PGDATA/pg_hba.conf
#(注释原认证方式,添加新的 ssl 证书认证方式)
#host all all 0.0.0.0/0 md5
hostssl all all 0.0.0.0/0 md5 clientcert=verify-ca

SM2 算法(TONGSUO)

# 生成ca相关:
./tongsuo genpkey -algorithm EC -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out ca.key
./tongsuo req -new -days 365 -sm3 -x509 -key ca.key -text -subj "/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_CA" -out ca.crt

# 生成server相关
./tongsuo genpkey -algorithm EC -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out server.key
./tongsuo req -new -key server.key -out server.req -subj "/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_server" 
./tongsuo x509 -req -in server.req -CA ca.crt -CAkey ca.key -set_serial 111111 -text -days 365 -sm3 -out server.crt

# 生成client相关
./tongsuo genpkey -algorithm EC -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out postgresql.key
./tongsuo req -new -key postgresql.key -out postgresql.req -subj "/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_client" 
./tongsuo x509 -req -in postgresql.req -CA ca.crt -CAkey ca.key -set_serial 111111 -text -days 365 -sm3 -out postgresql.crt

# 修改权限
chmod 0400 server.key
chmod 0400 postgresql.key

cp ca.crt ~/.postgresql/root.crt
cp postgresql.key ~/.postgresql/
cp postgresql.crt ~/.postgresql/

cp ca.crt $PGDATA/
cp server.key $PGDATA/
cp server.crt $PGDATA/

# 修改集群配置文件
vim $PGDATA/postgresql.conf
ssl = 1
ssl_ciphers = 'TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3'
ssl_ecdh_curve = 'SM2'
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ca_file = 'ca.crt'

vim $PGDATA/pg_hba.conf
hostssl all all 0.0.0.0/0 md5 00:00-24:00 clientcert=verify-ca
#host all all 0.0.0.0/0 md5 00:00-24:00

用户级别证书认证

以上配置都是用于通用的 SSL 连接配置。所有的数据库用户都可以使用该证书进行认证,建立 SSL 加密链接。若证书文件与数据库用户一一绑定,则该证书只能该数据库用户进行使用,其他用户使用报错。

用户级别认证方式的配置步骤大致相同。不同点如下:

  1. 生成客户端证书时,命令行参数 ‘/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_client’ 中 CN 对应的字段修改为 数据库用户名 (如:‘/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres’)。
  2. 修改集群目录下客户端认证方式配置文件 pg_hba.conf 中的用户认证方式为 证书认证,即 cert 。如:hostssl all all 0.0.0.0/0 cert clientcert=verify-full

如下命令所示:

# 生成ca相关:
./tongsuo genpkey -algorithm EC -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out ca.key
./tongsuo req -new -days 365 -sm3 -x509 -key ca.key -text -subj "/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_CA" -out ca.crt

# 生成server相关
./tongsuo genpkey -algorithm EC -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out server.key
./tongsuo req -new -key server.key -out server.req -subj "/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres.com_server" 
./tongsuo x509 -req -in server.req -CA ca.crt -CAkey ca.key -set_serial 111111 -text -days 365 -sm3 -out server.crt

# 生成client相关
./tongsuo genpkey -algorithm EC -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out postgresql.key
./tongsuo req -new -key postgresql.key -out postgresql.req -subj "/C=CN/ST=China/L=XIAN/O=postgresql/OU=postgres/CN=postgres" 
./tongsuo x509 -req -in postgresql.req -CA ca.crt -CAkey ca.key -set_serial 111111 -text -days 365 -sm3 -out postgresql.crt

# 修改权限
chmod 0400 server.key
chmod 0400 postgresql.key

cp ca.crt ~/.postgresql/root.crt
cp postgresql.key ~/.postgresql/
cp postgresql.crt ~/.postgresql/

cp ca.crt $PGDATA/
cp server.key $PGDATA/
cp server.crt $PGDATA/

# 修改集群配置文件
vim $PGDATA/postgresql.conf
ssl = 1
ssl_ciphers = 'TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3'
ssl_ecdh_curve = 'SM2'
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ca_file = 'ca.crt'

vim $PGDATA/pg_hba.conf
hostssl all all 0.0.0.0/0 cert 00:00-24:00 clientcert=verify-full
#host all all 0.0.0.0/0 md5 00:00-24:00

TLCP 配置

PostgreSQL 本身是不支持 TLCP 的,若想要其支持需进行简单的改造。相关适配逻辑可以参考 适配tassl,使opengauss支持TLCP

PostgreSQL 铜锁适配方式

参考链接:国密TLCP使用手册

内核代码改造在此处不进行详细描述,只是提供修改大纲。如下:

  1. 首先是双证书生成:参考 tongsuo/tlcp_certs/mkcert.sh 脚本,其中未带 _enc 后缀的即为签名证书、私钥,对应 SSL/TLS 时需要的单证书场景,带 _enc 后缀的即为加密证书、私钥。

  2. postgresql.conf 配置:新增 ssl_use_tlcp(TLCP开关)、ssl_enc_cert_file(加密证书路径)、ssl_enc_key_file(加密私钥路径)参数。之所以新增 TLCP 开关,是因为 TLS 与 TLCP 存在单证书/双证书的区别,区分加载逻辑及 context 结构。

  3. 客户端配置:SSL/TLS 场景下客户端证书默认路径、名称为 ~/.postgresql/root.crt、 ~/.postgresql/postgresql.crt、 ~/.postgresql/postgresql.key。TLCP 场景下加密证书及私钥默认为 ~/.postgresql/postgresql_enc.crt、 ~/.postgresql/postgresql_enc.key。也可通过 psql 连接参数去指定,如:./psql " host=127.0.0.1 dbname=postgres user=postgres port=5432 sslkey=$HOME/tlcp_certs/postgresql.key sslcert=$HOME/tlcp_certs/postgresql.crt sslenckey=$HOME/tlcp_certs/postgresql_enc.key sslenccert=$HOME/tlcp_certs/postgresql_enc.crt sslrootcert=$HOME/tlcp_certs/root.crt"

基于铜锁 TLCP 配置

参考链接:使用 Tongsuo 签发 SM2 双证书

# 生成双证书
sh  mkcert.sh 
# 证书生成命令参考 mkcert.sh,配置文件参考 ca.cnf 和 subca.cnf(cnf配置同openssl)。
	
# 拷贝客户端证书到默认路径
cp root.crt ~/.postgresql/
cp postgresql.crt ~/.postgresql/
cp postgresql.key ~/.postgresql/
cp postgresql_enc.crt ~/.postgresql/
cp postgresql_enc.key ~/.postgresql/
	
# 拷贝服务端证书到bin目录
cp root.crt $PGDATA/
cp server.key $PGDATA/
cp server.crt $PGDATA/
cp server_enc.key $PGDATA/
cp server_enc.crt $PGDATA/

# 修改集群配置文件
vim $PGDATA/postgresql.conf
ssl = on
ssl_use_tlcp = on
ssl_ciphers = 'ECC-SM2-SM4-CBC-SM3:ECDHE-SM2-SM4-CBC-SM3:ECC-SM2-SM4-GCM-SM3:ECDHE-SM2-SM4-GCM-SM3'
ssl_ecdh_curve = 'SM2'
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_enc_cert_file = 'server_enc.crt'
ssl_enc_key_file = 'server_enc.key'
ssl_ca_file = 'root.crt'

vim $PADATA/pg_hba.conf
hostssl all all 0.0.0.0/0 md5 00:00-24:00 clientcert=verify-ca
#host all all 0.0.0.0/0 md5 00:00-24:00

其中 mksert.sh 脚本内容参考如下:

#!/bin/bash
rm -f *.key *.csr *.crt
rm -rf {newcerts,db,private,crl}
mkdir {newcerts,db,private,crl}
touch db/{index,serial}
echo 00 > db/serial

# sm2 ca
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out ca.key
tongsuo req -config ca.cnf -new -key ca.key -out ca.csr -sm3 -nodes -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=root ca"
tongsuo ca -selfsign -config ca.cnf -in ca.csr -keyfile ca.key -extensions v3_ca -days 3650 -notext -out ca.crt -md sm3 -batch

# sm2 middle ca
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out subca.key
tongsuo req -config ca.cnf -new -key subca.key -out subca.csr -sm3 -nodes -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=sub ca"
tongsuo ca -config ca.cnf -extensions v3_intermediate_ca -days 3650 -in subca.csr -notext -out subca.crt -md sm3 -batch
cat ca.crt subca.crt > root.crt

# server sm2 double certs
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out server.key
tongsuo req -config subca.cnf -key server.key -new -out server.csr -sm3 -nodes -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=server"
tongsuo ca -config subca.cnf -extensions server_sign_req -days 3650 -in server.csr -notext -out server.crt -md sm3 -batch
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out server_enc.key
tongsuo req -config subca.cnf -key server_enc.key -new -out server_enc.csr -sm3 -nodes -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=server enc"
tongsuo ca -config subca.cnf -extensions server_enc_req -days 3650 -in server_enc.csr -notext -out server_enc.crt -md sm3 -batch

# client sm2 double certs
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out postgresql.key
tongsuo req -config subca.cnf -key postgresql.key -new -out postgresql.csr -sm3 -nodes -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=postgresql"
tongsuo ca -config subca.cnf -extensions client_sign_req -days 3650 -in postgresql.csr -notext -out postgresql.crt -md sm3 -batch
tongsuo genpkey -algorithm ec -pkeyopt ec_paramgen_curve:sm2 -out postgresql_enc.key
tongsuo req -config subca.cnf -key postgresql_enc.key -new -out postgresql_enc.csr -sm3 -nodes -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=postgresql enc"
tongsuo ca -config subca.cnf -extensions client_enc_req -days 3650 -in postgresql_enc.csr -notext -out postgresql_enc.crt -md sm3 -batch

rm -rf {newcerts,db,private,crl}

抓包验证

在 Linux 环境下可以使用 tcpdump 工具,对建立连接后的数据传输进行抓包验证,默认情况下执行sql 命令,通过抓包可以看到完整的语句。开启 SSL 加密传输之后,抓取数据为密文(乱码)。

抓包命令为:tcpdump -i lo tcp and host 127.0.0.1 and port 5432 -XX -vv

在这里插入图片描述

在这里插入图片描述

  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aSimpleSheep

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值