使用 PgBouncer

PgBouncer 这是一个用于 PostgreSQL 的轻量级连接池。 它维持少量数据库连接,并让多个应用程序客户端共享这些连接,这有助于您的部署在 连接限制 范围内运行,并避免了为每个客户端打开新连接所产生的开销。

IBM Cloud® Databases for PostgreSQL 部署中内置了对 PgBouncer's auth_query 身份验证。 每次部署都会提供一个 public.pgbouncer_lookup 函数和一个 pgbouncer_auth 角色,因此您运行的 PgBouncer 实例可以直接根据您的部署对数据库用户进行验证。 您无需为数据库用户维护本地密码列表,且密码更改会立即生效,无需重启或重新加载 PgBouncer。

Databases for PostgreSQL PgBouncer 并非由其托管或运营。 您需要在自己的基础设施(例如虚拟服务器、Kubernetes 集群或应用程序侧车)上安装、运行、保障安全并更新 PgBouncer。 有关连接管理的更多信息,请参阅 “管理连接”。

准备工作

您需要:

  • 一个 已设置管理员密码 的 Databases for PostgreSQL 部署环境。
  • 您应用程序的数据库用户,可在用户界面(UI)、命令行界面(CLI)或 API 中创建。
  • PgBouncer 1.11.0 或更高版本,且支持 SCRAM 身份验证,并安装在您控制的基础设施上。 如果您计划在事务池模式下使用协议级预编译语句,请使用 PgBouncer 1.21.0 或更高版本。
  • 一个 psql 客户端。
  • 您的部署连接信息:
    • 主机名和端口,来自[连接]字符串……
    • CA证书,通过 ibmcloud cdb deployment-cacert 获取。

对 pgbouncer_lookup 的支持正在各部署环境中逐步推出。 要确认您的部署中是否包含该功能,请以 admin 身份登录 psql,并运行 \df public.pgbouncer_lookup。 如果结果为空,则您的部署将在即将进行的维护更新中收到该函数。

创建专用认证用户

PgBouncer 以一个指定的登录角色运行 auth_query 时,其 auth_user。 创建一个仅用于此目的的角色。 一个不拥有任何数据、也不运行其他任何程序的专用角色,是遵循最小权限原则的最佳选择。

以 admin 用户身份连接到 psql,然后创建该角色并授予其 pgbouncer_auth 权限:

CREATE ROLE pool_auth WITH LOGIN PASSWORD '<POOL_AUTH_PASSWORD>';
GRANT pgbouncer_auth TO pool_auth;

pgbouncer_auth 角色仅包含一项权限:运行 pgbouncer_lookup 函数的权限。 admin 用户拥有 pgbouncer_auth 账户并具备管理员权限,因此您可以自行授予和撤销成员资格。 如果你希望该用户出现在服务凭据中,也可以使用 ibmcloud cdb user-create 创建用户,但 通过这种方式创建的用户属于 ibm-cloud-base-user 组,并具有创建用户和数据库的权限,这超出了认证用户所需的权限范围。

仅向指定的认证用户授予 pgbouncer_auth 权限。 该角色的任何成员都可以读取您其他数据库用户的存储密码验证器,因此每增加一名成员,都会扩大凭据泄露的影响范围。

要停用某个认证用户,请撤销其成员资格:

REVOKE pgbouncer_auth FROM pool_auth;

配置 PgBouncer

连接到 Databases for PostgreSQL 部署时,需要配置以下 PgBouncer 设置:

  • auth_type = scram-sha-256,因为部署中存储了 SCRAM-SHA-256 的密码验证器。
  • auth_query = SELECT * FROM public.pgbouncer_lookup($1),因为 PgBouncer's 默认情况下,auth_query 会直接读取 pg_authid,而您的数据库用户无法读取该文件。
  • 服务器端 TLS,因为部署环境仅接受 TLS 连接。 将 server_tls_sslmode = verify-full 设置为指定值,并将通过 ibmcloud cdb deployment-cacert 获取的证书保存到 server_tls_ca_file 中设置的路径下。

PgBouncer 它会从 auth_file 读取其自身 auth_user 的凭据,因此在此配置下,userlist.txt 中仅包含 auth_user 的凭据。 每隔一位用户都会通过 auth_query 进行解析。 将文件的权限限制为 PgBouncer 进程的所有者,例如设置为 0600 模式。

"pool_auth" "<POOL_AUTH_PASSWORD>"

一个完整的最小配置,其中 <HOSTNAME> 和 <PORT> 来自您的 连接字符串:

[databases]
ibmclouddb = host=<HOSTNAME> port=<PORT> dbname=ibmclouddb
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
auth_user = pool_auth
auth_query = SELECT * FROM public.pgbouncer_lookup($1)
server_tls_sslmode = verify-full
server_tls_ca_file = /etc/pgbouncer/ca-certificate.crt
pool_mode = session
max_client_conn = 200
default_pool_size = 20

请勿设置 auth_dbname = postgres。 postgres 数据库中未安装查找函数。 请勿设置 auth_dbname,以便身份验证查询在客户端连接的数据库中执行。 您以后创建的数据库将自动包含该功能。

有关每个设置的说明,请参阅《 PgBouncer 配置参考 》。

验证安装

  1. 请确认该查询能匹配到您的数据库用户之一。 以 admin 的身份连接到 psql,然后运行:

    SELECT usename, passwd IS NOT NULL AS can_authenticate
      FROM public.pgbouncer_lookup('<APP_USERNAME>');
    

    结果是一行,内容为 can_authenticate = t。 该查询的编写方式确保不会显示密码验证框。 服务保留用户(包括 admin )不会返回任何行。

  2. 以数据库用户 PgBouncer 的身份连接:

    psql "host=127.0.0.1 port=6432 dbname=ibmclouddb user=<APP_USERNAME>"
    

    如果连接成功,PgBouncer 将通过 auth_query 正确地对用户进行身份验证。

  3. 轮换数据库用户的密码,然后使用新密码通过 PgBouncer 重新连接:

    ibmcloud cdb user-password <DEPLOYMENT_NAME_OR_CRN> <APP_USERNAME> <NEW_PASSWORD>
    

    新密码立即生效。 无需重启 PgBouncer、重新加载或修改 userlist.txt。

安全模型的工作原理

pgbouncer_lookup 函数仅公开 PgBouncer 进行身份验证所需的信息。

  • 该函数使用 SECURITY DEFINER 并固定了 search_path,同时会为您读取 pg_catalog.pg_authid。 直接访问 pg_authid 和 pg_shadow 仍被屏蔽。
  • 它返回的是经过哈希处理的SCRAM验证码,绝不会返回明文密码。
  • 只有被允许登录的角色才能解析。 服务保留用户(例如 admin 以及内部复制和运维用户)永远不会解析。
  • 如果某个角色的 VALID UNTIL 时间戳处于过去,则该函数将返回一个 NULL 密码,因此身份验证失败,且密码过期规则仍将得到执行。
  • PUBLIC 被撤销了运行该函数的权限,该权限仅授予 admin 以及 pgbouncer_auth 的成员。

PgBouncer's 默认情况下,auth_query 会直接读取 pg_authid,而您的数据库用户无法执行此操作。 针对这种情况,PgBouncer 文档建议改由非超级用户调用 SECURITY DEFINER 函数。 pgbouncer_lookup 函数即为该函数,其中排除了该服务预留的用户。

局限性与注意事项

  • PgBouncer 不会触发您部署的 max_connections。 请调整 default_pool_size 的值,以确保所有 PgBouncer 实例的服务器连接总数保持在 连接限制 范围内。 如果达到限制,请参阅“增加最大连接数”。
  • 用户 admin 无法通过 auth_query 进行身份验证。 进行管理操作时,请直接以 admin 的身份连接到部署环境,不要通过 PgBouncer 进行连接。
  • postgres 数据库不支持LOOKUP函数,因此切勿设置 auth_dbname = postgres。
  • 在事务池化模式(pool_mode = transaction )下,诸如 SET 变量、临时表、建议锁以及 LISTEN 通道等会话状态不会在事务之间传递。 协议级预编译语句需要 max_prepared_statements 以及 PgBouncer 1.21.0 或更高版本。 如需了解更多信息,请参阅 PgBouncer features。
  • 在进行 就地主要版本升级 期间,您的部署会经历短暂的停机,且所有打开的连接都会被终止(SQLSTATE 57P01 )。 PgBouncer 会自动重新建立服务器连接,但您的应用程序必须重试中断的事务并重新准备语句。
  • 只读用户无法连接到主端点。 为了合并这些连接,请添加一个单独的 [databases] 条目,将其指向您的只读副本。

后续步骤