20.3. 认证方法 #
下面几节会更详细地介绍这些认证方法。
20.3.1. 信任认证 #
当 trust 认证被指定时,PostgreSQL 假设任何可以连接到服务器的人都被授权使用他们指定的任何数据库用户名(即使是超级用户)访问数据库。当然,在 database 和 user 列中设置的限制仍然适用。只有当在操作系统层对进入服务器的连接有足够保护时,才应该使用这种方法。
trust 认证对于单用户工作站的本地连接是非常合适和方便的。通常它本身不适用于一台多用户机器。不过,只要你利用文件系统权限限制了对服务器的 Unix 域套接字文件的访问,即使在多用户机器上,你也可能可以使用 trust。要做这些限制,你可以设置第 19.3 节中描述的 unix_socket_permissions 配置参数(可能还有 unix_socket_group)。或者你可以设置 unix_socket_directories 配置参数来把 Unix 域套接字文件放在一个经过恰当限制的目录中。
设置文件系统权限只能有助于 Unix 套接字连接。本地 TCP/IP 连接不会被文件系统权限限制。因此,如果你想利用文件系统权限来控制本地安全,那么从 pg_hba.conf 中移除 host ... 127.0.0.1 ...行,或者把它改为一个非 trust 认证方法。
只有当你信任由 pg_hba.conf 中指定 trust 的行所允许连接的每台机器上的每个用户时,trust 认证才适合用于 TCP/IP 连接。对来自 localhost(127.0.0.1)以外的任何 TCP/IP 连接使用 trust,通常都不合理。
20.3.2. 密码认证 #
有几种基于密码的认证方法。这些方法的过程类似,但是区别在于用户密码如何被存放在服务器上以及客户端提供的密码如何被通过连接发送。
scram-sha-256scram-sha-256方法执行 SCRAM-SHA-256 认证,其定义见 RFC 7677。它是一种挑战-响应方案,可防止在不受信任连接上嗅探密码,并支持在服务器上以被认为安全的密码学 hash 形式存储密码。这是当前提供的方法中最安全的一种,但是旧的客户端库不支持这种方法。
md5方法
md5使用一种自定义的、安全性较低的挑战-响应机制。它能防止密码嗅探,并避免在服务器上以明文存储密码;但如果攻击者设法从服务器上窃取了密码 hash,它就无法提供保护。此外,如今已不再认为 MD5 hash 算法能抵御有决心的攻击者。md5方法不能与 db_user_namespace 功能一起使用。为了简化从
md5方法到较新的 SCRAM 方法的转变,如果在pg_hba.conf中指定了md5但是用户在服务器上的密码是为 SCRAM(见下文)加密的,则将自动选择基于 SCRAM 的认证。password方法
password以明文形式发送密码,因此它容易受到密码“嗅探”攻击。如果可能应该尽量避免使用它。不过,如果连接被 SSL 加密保护着,那么可以安全地使用password(不过如果依靠 SSL,SSL 证书认证可能是更好的选择)。
PostgreSQL 数据库密码独立于操作系统用户密码。每个数据库用户的密码存储在 pg_authid 系统目录中。可以使用 SQL 命令 CREATE USER 和 ALTER ROLE 管理密码,例如 CREATE USER foo WITH PASSWORD 'secret',也可以使用 psql 命令 \password。如果未为某个用户设置密码,存储的密码就是空值,该用户的密码认证始终会失败。
不同基于密码的认证方法是否可用,取决于用户在服务器上的密码是如何加密的(更准确地说,是如何进行 hash 运算的)。这由设置密码时的配置参数 password_encryption 控制。如果密码是使用 scram-sha-256 设置加密的,那么它可以用于 scram-sha-256 和 password 认证方法(但后一种情况下密码会以明文传输)。如上所述,此时如果认证方法指定为 md5,也会自动切换为使用 scram-sha-256,因此同样能够工作。如果密码是使用 md5 设置加密的,那么它只能用于 md5 和 password 认证方法(同样,后一种情况下密码会以明文传输)。(以前的 PostgreSQL 版本支持在服务器上存储明文密码,但现在已经不再可能。)要检查当前存储的密码 hash,可以查看系统目录 pg_authid。
要把现有安装从 md5 升级到 scram-sha-256,需要先确保所有正在使用的客户端库都足够新并支持 SCRAM,然后在 postgresql.conf 中设置 password_encryption = 'scram-sha-256',要求所有用户重新设置密码,并将 pg_hba.conf 中的认证方法设置改为 scram-sha-256。
20.3.3. GSSAPI 认证 #
GSSAPI 是 RFC 2743 定义的安全认证行业标准协议。PostgreSQL 按照 RFC 1964 支持使用 Kerberos 认证的 GSSAPI。GSSAPI 为支持它的系统提供自动认证(单点登录)。认证过程本身是安全的,但除非使用 SSL,否则数据库连接上传输的数据不会加密。
当编译 PostgreSQL 时,GSSAPI 支持必须被启用,详见第 16 章。
GSSAPI 使用 Kerberos 时,采用格式为 的标准主体。PostgreSQL 服务器会接受其所用 keytab 中包含的任何主体,但客户端建立连接时,必须注意通过 servicename/hostname@realmkrbsrvname 连接参数指定正确的主体信息。(另见第 33.1.2 节。)构建时可以使用 ./configure --with-krb-srvnam=whatever,将安装默认值从 postgres 改为其他值。在大多数环境中,无需更改此参数。某些 Kerberos 实现可能要求不同的服务名,例如 Microsoft Active Directory 要求服务名使用大写(POSTGRES)。
hostname 是服务器机器的完全限定主机名。服务主体的 realm 是服务器机器的首选 realm。
可以通过 pg_ident.conf 将客户端主体映射到不同的 PostgreSQL 数据库用户名。例如,可以将 pgusername@realm 映射为 pgusername。也可以不使用任何映射,直接将完整的 username@realm 主体用作 PostgreSQL 中的角色名。
PostgreSQL 还支持一个从主体中去掉 realm 的参数。提供这种方法是为了向后兼容,强烈不建议使用,因为这样就无法区分来自不同 realm 但用户名相同的用户。要启用此行为,将 include_realm 设为 0。对于简单的单 realm 安装环境,如果同时设置 krb_realm 参数(它会检查主体的 realm 是否与 krb_realm 参数值完全一致),这种做法仍是安全的;但与在 pg_ident.conf 中指定显式映射相比,它的能力较弱。
确保 PostgreSQL 服务器账户能够读取服务器的 keytab 文件(最好只能读取,不能写入)。(另见第 18.1 节。)密钥文件的位置由 krb_server_keyfile 配置参数指定。默认位置是 /usr/local/pgsql/etc/krb5.keytab(或者构建时用 sysconfdir 指定的目录)。出于安全考虑,建议为 PostgreSQL 服务器使用专用 keytab,而不是放宽系统 keytab 文件的权限。
keytab 文件由 Kerberos 软件生成;详情参见 Kerberos 文档。以下示例适用于兼容 MIT 的 Kerberos 5 实现:
kadmin%ank -randkey postgres/server.my.domain.orgkadmin%ktadd -k krb5.keytab postgres/server.my.domain.org
连接数据库时,请确保持有与请求的数据库用户名相匹配的主体票据。例如,数据库用户名为 fred 时,主体 fred@EXAMPLE.COM 可以连接。如果还要允许主体 fred/users.example.com@EXAMPLE.COM,请按第 20.2 节所述使用用户名映射。
以下配置选项适用于 GSSAPI:
include_realm如果设为 0,则在通过用户名映射(第 20.2 节)之前,会先从已认证用户的主体名中去掉 realm 名称。不建议这样做;它主要是为了向后兼容而保留的,因为在多 realm 环境中这并不安全,除非同时使用了
krb_realm。建议将include_realm保持为默认值(1),并在pg_ident.conf中提供显式映射,把主体名转换成 PostgreSQL 用户名。map允许在系统用户名与数据库用户名之间建立映射。详见第 20.2 节。对于
username@EXAMPLE.COM(或较少见的username/hostbased@EXAMPLE.COM)这样的 GSSAPI/Kerberos 主体,映射所用的用户名是username@EXAMPLE.COM(或相应的username/hostbased@EXAMPLE.COM),除非将include_realm设为 0,此时映射所见的系统用户名为username(或username/hostbased)。krb_realm设置用于匹配用户主体名的 realm。如果设置了该参数,则只接受来自该 realm 的用户;如果未设置,则允许来自任意 realm 的用户连接,但仍受已执行的用户名映射约束。
20.3.4. SSPI 认证 #
SSPI 是一种提供安全认证和单点登录的 Windows 技术。PostgreSQL 会以 negotiate 模式使用 SSPI,尽可能使用 Kerberos,否则自动回退到 NTLM。只有服务器和客户端都运行 Windows,或者在非 Windows 平台上可用 GSSAPI 时,SSPI 认证才能工作。
当使用 Kerberos 认证时,SSPI 和 GSSAPI 的工作方式相同,详见第 20.3.3 节。
SSPI 支持下列配置选项:
include_realm如果设为 0,则在通过用户名映射(第 20.2 节)之前,会先从已认证用户的主体名中去掉 realm 名称。不建议这样做;它主要是为了向后兼容而保留的,因为在多 realm 环境中这并不安全,除非同时使用了
krb_realm。建议将include_realm保持为默认值(1),并在pg_ident.conf中提供显式映射,把主体名转换成 PostgreSQL 用户名。compat_realm如果设为 1,则会在
include_realm选项中使用域的 SAM 兼容名称(也称为 NetBIOS 名称)。这是默认值。如果设为 0,则会使用 Kerberos 用户主体名中的真实 realm 名称。只有当服务器运行在域账号(包括域成员系统上的虚拟服务账号)下,并且所有通过 SSPI 认证的客户端也都使用域账号时,才可禁用此选项;否则,禁用此选项会导致认证失败。
upn_username如果此选项与
compat_realm一起启用,则认证时会使用 Kerberos UPN 中的用户名。如果禁用它(默认值),则使用 SAM 兼容用户名。默认情况下,对新建用户账号而言,这两个名称是相同的。注意,如果没有显式指定用户名,libpq 会使用 SAM 兼容名称。如果你使用的是 libpq 或基于它的驱动,应当保持该选项为禁用状态,或者在连接字符串中显式指定用户名。
map允许在系统用户名和数据库用户名之间进行映射。详见第 20.2 节。对于 SSPI/Kerberos 主体,例如
username@EXAMPLE.COM(或者较少见的username/hostbased@EXAMPLE.COM),用于映射的用户名分别是username@EXAMPLE.COM(或username/hostbased@EXAMPLE.COM),除非已经将include_realm设为 0;在那种情况下,映射时视为系统用户名的是username(或username/hostbased)。krb_realm设置用于匹配用户主体名的 realm。如果设置了该参数,则只接受来自该 realm 的用户;如果未设置,则允许来自任意 realm 的用户连接,但仍受已执行的用户名映射约束。
20.3.5. Ident 认证 #
ident 认证方法通过从一个 ident 服务器获得客户端的操作系统用户名并且用它作为被允许的数据库用户名(和可选的用户名映射)来工作。它只在 TCP/IP 连接上支持。
注意
当为一个本地(非 TCP/IP)连接指定 ident 时,将实际使用 peer 认证(见第 20.3.6 节)。
以下配置选项适用于 ident:
map允许在系统用户名和数据库用户名之间进行映射。详见第 20.2 节。
“标识协议”在 RFC 1413 中定义。几乎所有类 Unix 操作系统都自带 ident 服务器,默认监听 TCP 端口 113。ident 服务器的基本功能是回答这样的问题:“从你的端口 X 连到我的端口 Y 的连接,是哪个用户发起的?”由于建立物理连接时,PostgreSQL 已知 X 和 Y,因此可以询问连接客户端所在主机上的 ident 服务器,理论上能够确定任意给定连接的操作系统用户。
这个过程的缺点在于它依赖客户端本身的可信性:如果客户端机器不可信或者已被攻破,攻击者几乎可以在 113 端口上运行任何程序,并返回任意他们选择的用户名。因此,这种认证方法只适用于封闭网络,在这类网络中每台客户端机器都受到严格控制,而且数据库管理员与系统管理员之间保持密切协作。换句话说,你必须信任运行 ident 服务器的那台机器。请注意下面的警告:
标识协议的本意不是作为一种授权或访问控制协议。 | ||
| --RFC 1413 | ||
有些 ident 服务器提供了一个非标准选项,会让返回的用户名被加密,而解密所需密钥只有发起连接机器的管理员才知道。将 ident 服务器与 PostgreSQL 配合使用时,绝不能启用这个选项,因为 PostgreSQL 无法解密返回的字符串,也就无法确定实际用户名。
20.3.6. Peer 认证 #
Peer 认证通过从内核获取客户端的操作系统用户名,并把它用作被允许的数据库用户名(可结合可选的用户名映射)来工作。这种方法只支持本地连接。
以下配置选项适用于 peer:
map允许在系统用户名和数据库用户名之间进行映射。详见第 20.2 节。
Peer 认证只在提供 getpeereid() 函数、SO_PEERCRED 套接字参数或类似机制的操作系统上可用。目前这包括 Linux、大多数 BSD 变种(包括 macOS)以及 Solaris。
20.3.7. LDAP 认证 #
这种认证方法的工作方式与 password 类似,只不过它使用 LDAP 作为密码验证方法。LDAP 只用于验证用户名/密码对。因此,在使用 LDAP 进行认证之前,用户必须已经存在于数据库中。
LDAP 认证可以在两种模式下工作。第一种模式称为简单绑定模式,服务器会绑定到按 prefix username suffix 形式构造出的可分辨名称。通常,prefix 参数用于指定 cn=,或在 Active Directory 环境中指定 DOMAIN\。suffix 则用于指定非 Active Directory 环境中 DN 的剩余部分。
第二种模式称为搜索+绑定模式,服务器首先使用由 ldapbinddn 和 ldapbindpasswd 指定的固定用户名和密码绑定到 LDAP 目录,并搜索试图登录数据库的用户。如果没有配置用户名和密码,则会尝试对目录进行匿名绑定。搜索会在 ldapbasedn 指定的子树上进行,并尝试对 ldapsearchattribute 指定的属性做精确匹配。一旦在搜索中找到了该用户,服务器会断开连接,再作为该用户重新绑定到目录,并使用客户端指定的密码来验证登录是否正确。这种模式与 Apache mod_authnz_ldap 和 pam_ldap 等软件中的 LDAP 认证方案相同。这种方法使目录中用户对象的位置更具灵活性,但会与 LDAP 服务器建立两个独立的连接。
以下配置选项适用于两种模式:
ldapserver要连接的 LDAP 服务器的名称或 IP 地址。可以指定多个服务器,用空格分隔。
ldapport要连接的 LDAP 服务器的端口号。如果未指定端口,则将使用 LDAP 库的默认端口设置。
ldaptls设为 1 时,PostgreSQL 与 LDAP 服务器之间的连接会使用 TLS 加密。注意,这只加密与 LDAP 服务器之间的流量 — 除非使用 SSL,否则与客户端之间的连接仍不加密。
以下选项仅适用于简单绑定模式:
ldapprefix在进行简单绑定认证时,附加到用户名前面以形成绑定 DN 的字符串。
ldapsuffix在进行简单绑定认证时,附加到用户名后面以形成绑定 DN 的字符串。
以下选项仅适用于搜索加绑定模式:
ldapbasedn在进行搜索+绑定认证时,用作用户搜索起点的根 DN。
ldapbinddn在进行搜索+绑定认证时,用于绑定到目录并执行搜索的用户 DN。
ldapbindpasswd在进行搜索+绑定认证时,用于绑定到目录并执行搜索的用户密码。
ldapsearchattribute在进行搜索+绑定认证时,用于与用户名匹配的属性。如果未指定属性,则会使用
uid属性。ldapurl符合 RFC 4516 的 LDAP URL。这是另一种指定部分 LDAP 选项的方式,写法更紧凑、更标准。其格式为
ldap://
host[:port]/basedn[?[attribute][?[scope]]]scope必须是以下值之一:base,one,sub,通常使用最后一个。只会使用一个属性,而且不支持标准 LDAP URL 的其他某些组件,例如过滤器和扩展。对于非匿名绑定,必须将
ldapbinddn和ldapbindpasswd指定为单独的选项。要使用加密的 LDAP 连接,除了
ldapurl,还必须使用ldaptls选项。不支持ldapsURL 方案(直接 SSL 连接)。目前只有 OpenLDAP 支持 LDAP URL,Windows 不支持。
将简单绑定模式的配置选项与搜索+绑定模式的配置选项混用是错误的。
下面是一个简单绑定 LDAP 配置示例:
host ... ldap ldapserver=ldap.example.net ldapprefix="cn=" ldapsuffix=", dc=example, dc=net"
当请求以数据库用户 someuser 连接数据库服务器时,PostgreSQL 将尝试使用 DN cn=someuser, dc=example, dc=net 和客户端提供的密码绑定到 LDAP 服务器。如果该连接成功,数据库访问就会被授予。
下面是搜索加绑定配置的示例:
host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapsearchattribute=uid
当请求以数据库用户 someuser 的身份连接数据库服务器时,PostgreSQL 会尝试匿名绑定到 LDAP 服务器(因为没有指定 ldapbinddn),并在指定的基础 DN 下搜索 (uid=someuser)。如果找到了条目,就会尝试使用找到的信息和客户端提供的密码进行绑定。如果第二次连接成功,就会授予数据库访问权限。
下面是以 URL 形式写出的同一个搜索+绑定配置:
host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub"
某些支持 LDAP 认证的其他软件也使用相同的 URL 格式,因此共享这类配置会更容易。
提示
如示例中所示,由于 LDAP 通常使用逗号和空格来分割一个 DN 的不同部分,在配置 LDAP 选项时通常有必要使用双引号包围的参数值。
20.3.8. RADIUS 认证 #
这种认证方法的工作方式与 password 类似,只不过它使用 RADIUS 作为密码验证方式。RADIUS 只用于验证用户名/密码对。因此,在使用 RADIUS 进行认证之前,用户必须已经存在于数据库中。
使用 RADIUS 认证时,会向配置好的 RADIUS 服务器发送一条 Access Request 消息。该请求的类型为 Authenticate Only,并包含 user name、password(加密的)以及 NAS Identifier 参数。该请求会使用与服务器共享的密钥进行加密。RADIUS 服务器会返回 Access Accept 或 Access Reject 作为响应。PostgreSQL 不支持 RADIUS 记账。
可以指定多个 RADIUS 服务器,这种情况下会按顺序依次尝试。如果某台服务器返回否定响应,则认证失败;如果没有收到响应,则继续尝试列表中的下一台服务器。要指定多台服务器,可用双引号括起整个列表,并用逗号分隔服务器名称。如果指定了多台服务器,其他 RADIUS 选项也可以写成逗号分隔的列表,以便为每台服务器分别提供不同取值;也可以只指定单个值,此时该值会应用到所有服务器。
RADIUS 支持下列配置选项:
radiusservers要连接的 RADIUS 服务器的 DNS 名称或 IP 地址。此参数为必需项。
radiussecrets与 RADIUS 服务器进行安全通信时使用的共享密钥。它在 PostgreSQL 服务器和 RADIUS 服务器上必须完全相同。建议它至少是一个 16 个字符长的字符串。此参数为必需项。
注意
只有当 PostgreSQL 在构建时启用了 OpenSSL 支持,所使用的加密向量才具有足够的密码学强度。在其他情况下,到 RADIUS 服务器的传输只能被视为经过混淆,而非受到安全保护;如有必要,应额外采取外部安全措施。
radiusports要连接的 RADIUS 服务器端口号。如果未指定端口,则会使用默认的 RADIUS 端口
1812。radiusidentifiers在 RADIUS 请求中用作
NAS Identifier的字符串。举例来说,这个参数可用于标识用户正在尝试连接哪个数据库集簇,从而便于在 RADIUS 服务器上进行策略匹配。如果未指定标识符,则默认使用postgresql。
如果某个 RADIUS 参数值中必须包含逗号或空白,可以通过用双引号括起该值来实现,但这样会比较繁琐,因为此时需要两层双引号。下面是一个在 RADIUS 密钥字符串中包含空白的示例:
host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""secret two"""
20.3.9. 证书认证 #
这种认证方法使用 SSL 客户端证书进行认证,因此仅适用于 SSL 连接。使用此方法时,服务器要求客户端提供有效、受信任的证书,不会向客户端发送密码提示。服务器会将证书的 cn(通用名称)属性与请求的数据库用户名比较,匹配时才允许登录。可以使用用户名映射,允许 cn 与数据库用户名不同。
SSL 证书认证支持下列配置选项:
map允许在系统用户名和数据库用户名之间进行映射。详见第 20.2 节。
在指定证书认证的 pg_hba.conf 记录中,认证选项 clientcert 被视为 1,且不能关闭,因为此方法必须使用客户端证书。cert 方法在基本的 clientcert 证书有效性检查之外,还会检查 cn 属性是否与数据库用户名匹配。
20.3.10. PAM 认证 #
这种认证方法与 password 类似,只是使用 PAM(可插拔认证模块)作为认证机制。默认的 PAM 服务名为 postgresql。PAM 仅用于验证用户名与密码的组合,也可选择验证连接的远程主机名或 IP 地址。因此,必须先在数据库中创建该用户,才能使用 PAM 进行认证。有关 PAM 的更多信息,参见 Linux-PAM 页面。
PAM 支持下列配置选项:
pamservicePAM 服务名称。
pam_use_hostname决定是通过
PAM_RHOST项向 PAM 模块提供远程 IP 地址还是主机名。默认情况下使用 IP 地址。将此选项设为 1 可改为使用解析出的主机名。主机名解析可能导致登录延迟。(大多数 PAM 配置并不会使用这项信息,因此只有在 PAM 配置被专门设计为利用该信息时,才需要考虑这个设置。)
注意
如果 PAM 被设置为读取 /etc/shadow,认证将会失败,因为 PostgreSQL 服务器是由非 root 用户启动的。不过,当 PAM 被配置为使用 LDAP 或其他认证方法时,这就不是问题。
20.3.11. BSD 认证 #
这种认证方法操作起来类似于 password,不过它使用 BSD 认证来验证密码。BSD 认证只被用来验证用户名/密码对。因此,在 BSD 认证可以被用于认证之前,用户的角色必须已经存在于数据库中。BSD 认证框架当前只在 OpenBSD 上可用。
PostgreSQL 中的 BSD 认证使用 auth-postgresql 登录类型,如果 login.conf 中定义了 postgresql 登录分类,就会用它来认证。默认情况下这种登录分类不存在,PostgreSQL 将使用默认的登录分类。
注意
要使用 BSD 认证,PostgreSQL 用户账号(也就是运行服务器的操作系统用户)必须首先被加入到 auth 组中。在 OpenBSD 系统上默认存在 auth 组。