亚马逊AWS官方博客

解决 RDS PostgreSQL 10 升级到 14 后,JDBC 应用程序连接失败的问题

背景

根据 Amazon RDS for PostgreSQL 发布说明,RDS PostgreSQL 10/11 标准支持终止日期到 2023 年,建议升级到合适的新版本。

客户在验证 PostgreSQL 10 升级到 14 过程中,发现第三方应用程序无法正常连接 RDS PostgreSQL 14 数据库。

PG10 升级到 14 后,应用程序无法连接数据库

Metabase 是一个开源的数据分析工具,客户基于 Metabase 构建了很多报表。将 PG 升级到 14 后,发现当前版本 Metabase(v0.29.3)无法连接 PG14,具体错误如下:

Metabase 日志错误堆栈:

SEVERE: Connection error:
org.postgresql.util.PSQLException: The authentication type 10 is not supported. Check that you have configured the pg_hba.conf file to include the client's IP address or subnet, and that it is using an authentication scheme supported by the driver.
    at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:614)
    at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:222)
    at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
    at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:194)
    at org.postgresql.Driver.makeConnection(Driver.java:450)
    at org.postgresql.Driver.connect(Driver.java:252)
    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:681)
    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:190)
    ...
06-30 06:46:00 ERROR metabase.driver :: Failed to connect to database: org.postgresql.util.PSQLException: The authentication type 10 is not supported. Check that you have configured the pg_hba.conf file to include the client's IP address or subnet, and that it is using an authentication scheme supported by the driver.
06-30 06:46:00 DEBUG metabase.middleware :: POST /api/setup/validate 400 (445 ms) (0 DB calls).

问题分析

通过错误关键信息:The authentication type 10 is not supported,结合查看 pgjdbc 的源码,确认这是一个当前版本的 JDBC 驱动无法支持服务器要求的密码认证方式而产生的问题。

在 Server 端,通过 psql 工具连入,结合 rds_tools 扩展去查看当前用户的 encryption_type:

注意:rds_tools 在 RDS PostgreSQL 13 以及后续版本可用。

postgres=> CREATE EXTENSION rds_tools;
CREATE EXTENSION
postgres=> select * from rds_tools.role_password_encryption_type();
       rolname        | encryption_type
----------------------+-----------------
 pg_database_owner    |
 pg_read_all_data     |
 pg_write_all_data    |
 pg_monitor           |
 pg_read_all_settings |
 pg_read_all_stats    |
 pg_stat_scan_tables  |
 pg_signal_backend    |
 postgres             | scram-sha-256
(9 rows)

可以看到用户的默认加密类型是:scram-sha-256。

从客户端测,我们分析 JDBC 驱动,可以看到针对 SASL 的支持在 46.2.1 版本开始加入。

驱动版本 42.1.0

驱动版本 46.2.1

private static final int AUTH_REQ_SASL = 10;

关于 SASL 的说明详见:https://www.postgresql.org/docs/14/sasl-authentication.html

SASL is a framework for authentication in connection-oriented protocols. At the moment, PostgreSQL implements two SASL authentication mechanisms, SCRAM-SHA-256 and SCRAM-SHA-256-PLUS. More might be added in the future. The below steps illustrate how SASL authentication is performed in general, while the next subsection gives more details on SCRAM-SHA-256 and SCRAM-SHA-256-PLUS.

以上分析可见,服务器端默认使用 scram-sha-256,但是当前版本的 JDBC 驱动不支持这种加密方式,导致问题产生。

如果你的应用程序可控,可以通过升级 JDBC 驱动来解决这个问题。

参考驱动的 release 版本说明,从版本 42.6.1 起增加了对 SASL 的支持。

经实测,升级 JDBC 驱动到 42.6.1 及后续版本可以正常连接 PG14。

如果无法升级应用程序和驱动,  可以通过修改 RDS 实例的配置来解决。

修改 RDS 实例配置以支持旧版本驱动的方法

要点:

  1. 配置 RDS PostgreSQL 14 支持 MD5 的密码认证方式
  2. 将具体用户的密码认证方式修改为 MD5

第一步:新建一个自定义参数组

第二步:修改 RDS parameters 参数的 password_encryption

将 password_encryption 的值修改为 md5

第三步:修改 RDS 实例使用自定义参数组并重启

修改参数组需要重启 RDS 服务,重启完成后可以继续下一步。

重启完成后,通过 psql 工具等连接进入目标数据库实例查看 password_encryption,确认值为 md5。

psql (15.0, server 14.7)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, compression: off)
Type "help" for help.

postgres=> show password_encryption;
password_encryption
md5
(1 row)

第四步:通过修改现有用户的密码的方式将用户密码加密方式修改为 MD5

根据 postgresql 的文档 https://www.postgresql.org/docs/current/auth-password.html

The availability of the different password-based authentication methods depends on how a user’s password on the server is encrypted (or hashed, more accurately). This is controlled by the configuration parameter password_encryption at the time the password is set.

设置密码的时候会根据 password_encryption 指定的加密方式来存储密码。修改 password_encryption 变量以后, 可以通过重设密码操作来确保密码采用修改后的方式加密。

应用该修改后,数据库进入以下状态 Resetting-master-credentials

完成后,再次通过 rds_tools 扩展查看当前用户的加密类型,确认已经是 MD5。

postgres=> select * from rds_tools.role_password_encryption_type();
       rolname        | encryption_type
----------------------+-----------------
 pg_database_owner    |
 pg_read_all_data     |
 pg_write_all_data    |
 pg_monitor           |
 pg_read_all_settings |
 pg_read_all_stats    |
 pg_stat_scan_tables  |
 pg_signal_backend    |
 postgres             | md5
(9 rows)

再次使用以上用户名和密码连接数据库,可以正常连接。

总结

出于安全等角度, 我们总是需要将数据库升级到新的版本。AWS 提供了很多方法和实践去帮助我们的升级数据库,但是在升级过程中我们总是会遇到各种各样的挑战。本文中使用到的一些工具和方法希望可以帮助大家定位问题。

祝大家升级顺利。

参考链接

  1. Best practices for upgrading Amazon RDS to major and minor versions of PostgreSQL https://aws.amazon.com/cn/blogs/database/best-practices-for-upgrading-amazon-rds-to-major-and-minor-versions-of-postgresql/
  2. SCRAM Authentication in RDS https://aws.amazon.com/blogs/database/scram-authentication-in-rds-for-postgresql-13/
  3. 了解 PostgreSQL 角色和权限 https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.Roles.html
  4. Release calendar for Amazon RDS for PostgreSQL https://docs.aws.amazon.com/AmazonRDS/latest/PostgreSQLReleaseNotes/postgresql-release-calendar.html

本篇作者

叶小微

亚马逊云科技解决方案架构师,曾就职于 IBM,后从事电商相关和企业数字化转型工作,拥有多年架构设计、研发、项目管理经验。在工作流、微服务、系统集成等方向有丰富的解决实际问题的经验。