第 21 章 客户端认证
目录
当客户端应用连接到数据库服务器时,它会指定要以哪个 PostgreSQL 数据库用户名连接,这很像以某个特定用户身份登录 Unix 计算机。在 SQL 环境中,当前活动的数据库用户名决定了对数据库对象的访问权限 — 详见第 22 章。因此,必须限制哪些数据库用户能够连接。
注意
如第 22 章中所述,PostgreSQL 实际上是以“角色”为单位进行权限管理的。在本章中,我们统一使用数据库用户来表示“拥有 LOGIN 权限的角色”。
认证是数据库服务器确认客户端身份的过程,并据此决定是否允许该客户端应用(或者运行该客户端应用的用户)以请求的数据库用户名进行连接。
PostgreSQL 提供了多种不同的客户端认证方法。用于认证特定客户端连接的方法可以根据(客户端)主机地址、数据库和用户来选择。
PostgreSQL 数据库用户名在逻辑上独立于服务器所在操作系统中的用户名。如果某台服务器的所有用户在该机器上也都有账号,那么为他们分配与操作系统用户名一致的数据库用户名是有意义的。不过,接受远程连接的服务器可能有许多数据库用户并没有本地操作系统账号,在这种情况下,数据库用户名与操作系统用户名之间就不必存在任何对应关系。
21.1. pg_hba.conf 文件 #
客户端认证由一个配置文件控制,该文件按惯例命名为
pg_hba.conf,并存放在数据库集簇的数据目录中。(HBA 代表 host-based authentication,即基于主机的认证。)当数据目录由 initdb 初始化时,会安装一个默认的
pg_hba.conf 文件。不过,也可以把认证配置文件放在别处;请参见配置参数 hba_file。
pg_hba.conf 文件的一般格式是一组记录,每行一条。空白行会被忽略,# 注释字符后面的任何文本也会被忽略。记录可以通过在行尾加反斜线延续到下一行。(反斜线除出现在行尾之外,没有其他特殊含义。)一条记录由若干个字段组成,这些字段之间以空格和/或制表符分隔。如果字段值用双引号括起,则其中可以包含空白。在数据库、用户或地址字段中给关键字加上引号(例如 all 或 replication),会使该词失去其特殊含义,而仅匹配同名的数据库、用户或主机。即使在带引号的文本或注释中,也同样适用反斜线续行。
每条认证记录都指定一种连接类型、一个客户端 IP 地址范围(如果该连接类型需要)、一个数据库名、一个用户名,以及对匹配这些参数的连接要使用的认证方法。第一条同时匹配连接类型、客户端地址、请求数据库和用户名的记录会被用来执行认证。这里不存在“继续向后匹配”或“后备”机制:如果选中某条记录而认证失败,就不会再考虑后续记录。如果没有任何记录匹配,则拒绝访问。
记录可以采用以下几种格式:
localdatabaseuserauth-method[auth-options] hostdatabaseuseraddressauth-method[auth-options] hostssldatabaseuseraddressauth-method[auth-options] hostnossldatabaseuseraddressauth-method[auth-options] hostgssencdatabaseuseraddressauth-method[auth-options] hostnogssencdatabaseuseraddressauth-method[auth-options] hostdatabaseuserIP-addressIP-maskauth-method[auth-options] hostssldatabaseuserIP-addressIP-maskauth-method[auth-options] hostnossldatabaseuserIP-addressIP-maskauth-method[auth-options] hostgssencdatabaseuserIP-addressIP-maskauth-method[auth-options] hostnogssencdatabaseuserIP-addressIP-maskauth-method[auth-options]
各字段的含义如下:
local#该记录匹配使用 Unix 域套接字的连接尝试。没有这种类型的记录时,Unix 域套接字连接将被禁止。
host#该记录匹配使用 TCP/IP 发起的连接尝试。
host记录既匹配 SSL 或非 SSL 连接尝试,也匹配使用 GSSAPI 加密或不使用 GSSAPI 加密的连接尝试。注意
除非服务器以适当的 listen_addresses 配置参数值启动,否则远程 TCP/IP 连接将不可用,因为默认行为是只在本地回环地址
localhost上监听 TCP/IP 连接。hostssl#该记录匹配使用 TCP/IP 发起的连接尝试,但仅当连接使用 SSL 加密时才匹配。
要使用此选项,服务器必须在构建时启用 SSL 支持。此外,还必须通过设置 ssl 配置参数来启用 SSL(有关更多信息,请参见第 19.9 节)。否则,
hostssl记录将被忽略,只会记录一条警告,说明它无法匹配任何连接。hostnossl#该记录类型与
hostssl的行为相反;它只匹配通过 TCP/IP 发起且不使用 SSL 的连接尝试。hostgssenc#该记录匹配使用 TCP/IP 发起的连接尝试,但仅当连接使用 GSSAPI 加密时才匹配。
要使用此选项,服务器必须在构建时启用 GSSAPI 支持。否则,
hostgssenc记录将被忽略,只会记录一条警告,说明它无法匹配任何连接。hostnogssenc#该记录类型与
hostgssenc的行为相反;它只匹配通过 TCP/IP 发起且不使用 GSSAPI 加密的连接尝试。database#指定此记录匹配的数据库名称。
all表示匹配所有数据库。sameuser表示请求的数据库与请求的用户同名时才匹配。samerole要求请求的用户是与所请求数据库同名的角色的成员。(samegroup是samerole已过时但仍被接受的写法。)对于samerole,只有显式地直接或间接属于该角色,超级用户才被视为其成员,仅凭超级用户身份并不算。replication表示请求物理复制连接时匹配此记录,但不匹配逻辑复制连接。注意,物理复制连接不指定任何特定数据库,而逻辑复制连接会指定数据库。其他值则表示某个特定的 PostgreSQL 数据库的名称。可以用逗号分隔多个数据库名称。也可以在文件名前加@,指定一个单独存放数据库名称的文件。user#指定此记录匹配的数据库用户名。
all表示匹配所有用户。其他值可以是某个特定数据库用户的名称,也可以是前面带+的组名。(请记住,在 PostgreSQL 中,用户和组并无实质区别;+实际表示“匹配直接或间接属于此角色的任何角色”,而不带+的名称仅匹配该角色本身。)为此,只有显式地直接或间接属于该角色,超级用户才被视为其成员,仅凭超级用户身份并不算。可以用逗号分隔多个用户名。也可以在文件名前加@,指定一个单独存放用户名的文件。address#指定此记录匹配的客户端机器地址。此字段可以包含主机名、IP 地址范围或下面提到的特殊关键字之一。
IP 地址范围使用标准的数字表示法来指定起始地址,然后是斜杠(
/)和一个 CIDR 掩码长度。掩码长度表示客户端 IP 地址必须匹配的高位比特数。给定 IP 地址中右侧的比特应为零。IP 地址、/和 CIDR 掩码长度之间不得有任何空白。以这种方式指定的 IPv4 地址范围的典型示例包括
172.20.143.89/32用于单个主机,或172.20.143.0/24用于小型网络,或10.6.0.0/16用于较大的网络。IPv6 地址范围可能看起来像::1/128用于单个主机(在这种情况下是 IPv6 环回地址)或fe80::7a31:c1ff:0000:0000/96用于小型网络。0.0.0.0/0代表所有 IPv4 地址,::0/0代表所有 IPv6 地址。要指定单个主机,请对 IPv4 使用 32 的掩码长度,对 IPv6 使用 128。在网络地址中,不要省略尾部的零。以 IPv4 格式给出的条目将仅匹配 IPv4 连接,以 IPv6 格式给出的条目将仅匹配 IPv6 连接,即使所代表的地址在 IPv4-in-IPv6 范围内。请注意,如果系统的 C 库不支持 IPv6 地址,以 IPv6 格式给出的条目将被拒绝。
你也可以写
all来匹配任何 IP 地址,samehost来匹配服务器自己的任何 IP 地址,或samenet来匹配服务器直接连接到的任何子网中的任何地址。如果指定了主机名(任何不是 IP 地址范围或特殊关键字的内容都被视为主机名),则将该名称与客户端 IP 地址的反向名称解析结果进行比较(例如,如果使用 DNS,则进行反向 DNS 查找)。主机名比较不区分大小写。如果匹配成功,则对主机名执行正向名称解析(例如,进行正向 DNS 查找),以检查其解析为的任何地址是否等于客户端 IP 地址。如果两个方向都匹配,则将条目视为匹配。(在
pg_hba.conf中使用的主机名应该是客户端 IP 地址的地址到名称解析返回的名称,否则该行将不会匹配。一些主机名数据库允许将 IP 地址与多个主机名关联,但操作系统在要求解析 IP 地址时只会返回一个主机名。)以点(
.)开头的主机名规范匹配实际主机名的后缀。因此,.example.com将匹配foo.example.com(但不匹配单独的example.com)。当在
pg_hba.conf中指定主机名时,应确保名称解析相对快速。设置一个本地名称解析缓存可能会有帮助,如nscd。此外,还可能希望启用配置参数log_hostname,以便在日志中看到客户端的主机名而不是 IP 地址。这些字段不适用于
local记录。注意
用户有时会想知道为什么主机名以这种看似复杂的方式处理,包括两次名称解析,其中包括对客户端 IP 地址的反向查找。如果客户端的反向 DNS 条目未设置或返回了不符合预期的主机名,则使用该功能会变得复杂。这主要是为了效率:这样,连接尝试最多需要两次解析器查找,一次反向查找和一次正向查找。如果某个地址存在解析器问题,那就只会成为该客户端的问题。一个只进行正向查找的假设替代实现,在每次连接尝试期间都必须解析
pg_hba.conf中提到的每个主机名。如果列出了许多名称,这可能会非常慢。如果其中一个主机名存在解析器问题,那么这将成为所有人的问题。此外,实现后缀匹配功能需要进行反向查找,因为需要知道实际客户端主机名以便将其与模式进行匹配。
请注意,这种行为与其他流行的基于主机名的访问控制实现一致,例如 Apache HTTP 服务器和 TCP Wrappers。
IP-addressIP-mask#这两个字段可以用作
IP-address/mask-length表示法的替代方案。不指定掩码长度,而是在单独一列中指定实际掩码。例如,255.0.0.0表示 IPv4 的 CIDR 掩码长度为 8,而255.255.255.255表示 CIDR 掩码长度为 32。这些字段不适用于
local记录。auth-method#指定连接匹配此记录时要使用的认证方法。可选值在此处做了概述;详细说明见第 21.3 节。所有选项都必须使用小写并且区分大小写,因此即使是像
ldap这样的首字母缩写也必须写成小写。trust#无条件允许连接。这种方法允许任何能够连接到 PostgreSQL 数据库服务器的人以任意他们希望的 PostgreSQL 用户身份登录,无需密码或任何其他认证。详见第 21.4 节。
reject#无条件拒绝连接。这对于“过滤掉”某些主机很有用,例如一个
reject行可以阻止特定主机连接,而后面的行允许特定网络中的其余主机连接。scram-sha-256#执行 SCRAM-SHA-256 认证以校验用户密码。详见第 21.5 节。
md5#执行 SCRAM-SHA-256 或 MD5 认证以校验用户密码。详见第 21.5 节。
password#要求客户端提供未加密的密码以完成认证。由于密码会以明文形式通过网络发送,因此不应在不受信任的网络上使用。详见第 21.5 节。
gss#使用 GSSAPI 对用户进行认证。这仅适用于 TCP/IP 连接。详见第 21.6 节。它可以与 GSSAPI 加密配合使用。
sspi#使用 SSPI 对用户进行认证。这仅适用于 Windows。详见第 21.7 节。
ident#通过联系客户端上的 ident 服务器获取客户端的操作系统用户名,并检查它是否与请求的数据库用户名匹配。Ident 认证只能用于 TCP/IP 连接。当为本地连接指定时,将改为使用 peer 认证。详见第 21.8 节。
peer#从操作系统获取客户端的操作系统用户名,并检查是否与请求的数据库用户名匹配。这仅适用于本地连接。有关详细信息,请参见第 21.9 节。
ldap#使用 LDAP 服务器进行认证。详见第 21.10 节。
radius#使用 RADIUS 服务器进行认证。详见第 21.11 节。
cert#使用 SSL 客户端证书进行认证。详见第 21.12 节。
pam#使用操作系统提供的可插拔认证模块(PAM)服务进行认证。详见第 21.13 节。
bsd#使用操作系统提供的 BSD 认证服务进行认证。详见第 21.14 节。
auth-options#在
auth-method字段之后,可以有一个或多个形如name=value的字段,用于指定认证方法的选项。下文会详细说明各认证方法有哪些可用选项。除了下面列出的特定于方法的选项外,还有一个方法无关的认证选项
clientcert,可以在任何hostssl记录中指定。此选项可以设置为verify-ca或verify-full。这两个选项都要求客户端提供有效(受信任的)SSL 证书,而verify-full还要求证书中的cn(通用名称)与用户名或适用映射匹配。这种行为类似于cert认证方法(见第 21.12 节),但允许把客户端证书验证与任何支持hostssl条目的认证方法配合使用。对于任何使用客户端证书认证的记录(即使用
cert认证方法的记录,或使用clientcert选项的记录),都可以通过clientname选项指定要匹配客户端证书凭据中的哪一部分。此选项有两个可选值。如果指定clientname=CN,这也是默认值,则用户名将与证书的Common Name (CN)匹配。如果改为指定clientname=DN,则用户名将与证书的完整Distinguished Name (DN)匹配。此选项可能更适合与用户名映射配合使用。比较时使用的是 RFC 2253 格式的DN。要查看以这种格式表示的客户端证书DN,请执行openssl x509 -in myclient.crt -noout -subject -nameopt RFC2253 | sed "s/^subject=//"
在使用此选项时需要小心,特别是在使用正则表达式匹配
DN时。
由 @ 构造引用的文件会被读取为名称列表,其中的名称可以用空白或逗号分隔。注释仍然用 # 引入,与 pg_hba.conf 中相同,并且允许嵌套的 @ 构造。除非 @ 后面的文件名是绝对路径,否则它会被视为相对于引用它的文件所在目录。
由于每次连接尝试都会按顺序检查 pg_hba.conf 记录,因此记录的顺序很重要。通常,靠前的记录使用较严格的连接匹配条件和较弱的认证方法,靠后的记录使用较宽松的匹配条件和较强的认证方法。例如,可能希望对本地 TCP/IP 连接使用 trust 认证,而要求远程 TCP/IP 连接提供密码。此时,为来自 127.0.0.1 的连接指定 trust 认证的记录,应放在为更大范围的允许客户端 IP 地址指定密码认证的记录之前。
在启动时以及主服务器进程收到 SIGHUP 信号时,pg_hba.conf 文件会被读取。如果你在运行中的系统上编辑了该文件,就需要通知 postmaster(使用 pg_ctl reload、调用 SQL 函数 pg_reload_conf(),或者使用 kill -HUP)重新读取该文件。
注意
前面的说明不适用于 Microsoft Windows:在 Windows 上,pg_hba.conf 文件中的任何更改会立即被应用到后续的新连接上。
系统视图 pg_hba_file_rules 有助于预先测试对 pg_hba.conf 文件的修改,也可以在加载该文件没有产生预期效果时用于诊断问题。视图中 error 字段非空的行表示文件对应行存在问题。
提示
要连接到一个特定数据库,一个用户不仅要通过 pg_hba.conf 检查,还必须有该数据库上的 CONNECT 权限。如果你希望限制哪些用户能够连接到哪些数据库,授予/撤销 CONNECT 权限通常比在 pg_hba.conf 项中设置规则简单。
例 21.1 展示了一些 pg_hba.conf 条目示例。有关不同认证方法的详细信息,参见下一节。
例 21.1. 示例 pg_hba.conf 项
# 允许本地系统上的任何用户 # 通过 Unix 域套接字(本地连接的默认方式)以任意 # 数据库用户名连接到任意数据库。 # # TYPE DATABASE USER ADDRESS METHOD local all all trust # 相同的规则,但是使用本地环回 TCP/IP 连接。 # # TYPE DATABASE USER ADDRESS METHOD host all all 127.0.0.1/32 trust # 和前一行相同,但是使用了一个独立的掩码列 # # TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD host all all 127.0.0.1 255.255.255.255 trust # IPv6 上相同的规则 # # TYPE DATABASE USER ADDRESS METHOD host all all ::1/128 trust # 使用主机名的相同规则(通常同时覆盖 IPv4 和 IPv6)。 # # TYPE DATABASE USER ADDRESS METHOD host all all localhost trust # 允许来自任意具有 IP 地址192.168.93.x 的主机上任意 # 用户以 ident 为该连接所报告的相同用户名连接到 # 数据库 "postgres"(通常是操作系统用户名)。 # # TYPE DATABASE USER ADDRESS METHOD host postgres all 192.168.93.0/24 ident # 如果用户的密码被正确提供,允许来自主机 192.168.12.10 # 的任意用户连接到数据库 "postgres"。 # # TYPE DATABASE USER ADDRESS METHOD host postgres all 192.168.12.10/32 scram-sha-256 # 如果用户的密码被正确提供,允许 example.com 域中主机上 # 的任意用户连接到任意数据库。 # # 为大部分用户要求SCRAM认证,但是用户'mike'是个例外, # 他使用的是不支持SCRAM认证的旧客户端。 # # TYPE DATABASE USER ADDRESS METHOD host all mike .example.com md5 host all all .example.com scram-sha-256 # 如果没有前面的 "host" 行,这三行 # 将拒绝所有来自 192.168.54.1的连接(因为该条目将首先被匹配), # 但是允许来自互联网其他任何地方的 # GSSAPI 加密连接。零掩码表示不会考虑主机 IP 地址中的任何位, # 因此它会匹配任意主机。未加密的 GSSAPI 连接 # (由于 "hostgssenc" 只匹配加密的 GSSAPI 连接,因此它们会继续匹配到第三行) # 也是允许的,但只能来自 192.168.12.10。 # # TYPE DATABASE USER ADDRESS METHOD host all all 192.168.54.1/32 reject hostgssenc all all 0.0.0.0/0 gss host all all 192.168.12.10/32 gss # 允许来自 192.168.x.x 主机的用户连接到任意数据库,如果它们能够 # 通过 ident 检查。例如,假设 ident说用户是 "bryanh" 并且他要求以 # PostgreSQL 用户 "guest1" 连接,如果在 pg_ident.conf 有一个映射 # "omicron" 的条目表明 "bryanh" 被允许以 "guest1" 连接,则该连接将被允许。 # # TYPE DATABASE USER ADDRESS METHOD host all all 192.168.0.0/16 ident map=omicron # 如果这些是本地连接的唯一三行,它们将允许本地用户只连接到 # 自己的数据库(与其数据库用户名同名的数据库),但管理员和 # 角色 "support" 的成员除外,他们可以连接到所有数据库。 # 文件 $PGDATA/admins 包含管理员名称列表。 # 所有情况下都要求提供密码。 # # TYPE DATABASE USER ADDRESS METHOD local sameuser all md5 local all @admins md5 local all +support md5 # 上面的最后两行可以被整合为一行: local all @admins,+support md5 # 数据库列也可以用列表和文件名: local db1,db2,@demodbs all md5
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.