当一个客户端应用与数据库服务器进行联接时,它声明 它将以哪个 PostgreSQL 用户的名称进行联接, 就象我们登录一台 Unix 计算机 一样.在 SQL 环境里,活跃的数据库用户名决定数据库对象的各种访问。因此, 实际上我们要限制的是用户可以联接的数据库;
认证 是数据库服务器建立客户端应用的标识, 然后通过一些手段判断是否允许此客户端应用(或者运行这个客户端应用的 用户)与它所要求的用户名进行联接的过程;
PostgreSQL 提供多种不同的客户端认证方式.认证某个特定客户端联接所使用的方法可以通过基于 (客户端)的主机地址,数据库和用户的方式进行选择; 一些认证方法还允许你通过用户名进行限制;
PostgreSQL 用户名在逻辑上是和服务器运行的操作系统用户名相互独立的. 如果某个服务器的所有用户在那台服务器机器上也有帐号,那么给数据库用户赋与操作系统用户名是有意义的.不过, 一个接收远程访问的服务器很有可能有许多没有本地帐号的用户,因而在这种情况下数据库用户和操作系统用户名之间不必有任何联系;
1. pg_hba.conf 文件;
客户端认证是由数据目录里的文件pg_hba.conf 控制的,比如: /etc/postgresql/8.2/main/pg_hba.conf (hba 的意思是 host-based authentication:基于主机的认证.)在initdb初始化数据目录的时候,它会安装一个缺省的文件;
文件 pg_hba.conf 的常用格式是一套记录, 每行一条。空白行行被忽略,井号( "#" )开头的注释 也被忽略。一条记录是由若干用空格和/或 tab 分隔的字段组成。 如果字段用引号包围,那么它可以包含空白.记录不能夸行存在;
每条记录声明一种联接类型,一个客户端 IP 地址范围 (如果和联接类型相关的话),一个数据库名,一个用户名字,以及对匹配这些参数的联接使用的认证方法. 匹配联接类型,客户端地址和联接企图请求的数据库名和用户名的第一条记录将用于执行认证.这个处理过程没有"跨越"或者 "回头"的说法∶如果选择了一条记录而且认证失败,那么将不考虑后面的记录.如果没有匹配的记录,那么访问将被拒绝.
每条记录可以下面三种格式之一:
local database user authentication-method [authentication-option]
host database user IP-address IP-mask authentication-method [authentication-option]
hostssl database user IP-address IP-mask authentication-method [authentication-option]
1.1 各个字段的含义;
local
这条记录匹配通过 Unix 域套接字进行的联接企图. 没有这种类型的记录,就不允许 Unix 域套接字的联接。
host
这条记录匹配通过 TCP/IP 网络进行的联接尝试.请注意,除非服务器是 带着 -i 选项或者打开了 postgresql.conf 里面的 tcpip_socket 配置参数集启动的,否则 TCP/IP 联接是被禁止掉的.
hostssl
这条记录匹配通过在 TCP/IP 上进行的 SSL 联接企图. host 记录可以匹配 SSL 和非 SSL 的联接企图, 但 hostssl 记录需要 SSL 联接。
要使用这个选项,制作服务器的时候必须打开 SSL 支持.而且在服务器启动的时候, 必须打开在postgresql.conf里的ssl选项。
database
声明记录所匹配的数据库。值 all 表明该记录匹配所有数据库, 值 sameuser表示如果被请求的数据库和请求的用户同名,则匹配。 samegroup 表示请求的用户必须是一个与数据库同名的组中的成员。 在其他情况里,这就是一个特定的 PostgreSQL 的名字。我们可以通过用逗号分隔的方法声明多个数据库。 一个包含数据库名的文件可以 通过对该文件前缀 @ 来声明.该文件必需和 pg_hba.conf 在同一个目录;
user
为这条记录声明所匹配的PostgreSQL用户.值 all 表明它匹配 于所有用户.否则,它就是特定 PostgreSQL 用户的名字.多个用户名可以通过用逗号分隔的方法声明.组名字 可以通过用 + 做组名字前缀来声明.一个包含用户名的文件可以 通过在文件名前面前缀 @ 来声明.该文件必需和 pg_hba.conf 在同一个目录;
IP-address
IP-mask
这两个字段包含标准的点分十进制表示的 IP地址/掩码值。 (IP地址只能用数字的方式声明,而不能用域名或者主机名。) 它们俩放在一起,声明了这条记录匹配的客户机的 IP 地址。 准确的逻辑是
(actual-IP-address xor IP-address-field) and IP-mask-field
对于要匹配的记录必需为零. (当然,IP地址是可以欺骗的,但是这个考虑在 PostgreSQL 的范围之外。)
这些域只适用于 host 和 hostssl 记录;
1.2 authentication-method(认证方法);
trust
无条件地允许联接.这个方法允许任何可以与PostgreSQL 数据库联接的用户以他们期望的任意 PostgreSQL 数据库用户身份进行联接,而不需要口令。
reject
联接无条件拒绝.常用于从一个组中"过滤"某些主机.
md5
要求客户端提供一个 MD5 加密的口令进行认证. 这个方法是允许加密口令存储在pg_shadow里的唯一的一个方法.
crypt
类似 md5 方法,只是用的是老式的 crypt 加密认证, 用于 7.2 以前的客户端.对于 7.2 以及以后的客户端,我们建议使用 md5.
password
和"md5"一样,但是口令是以明文形式在网络上传递的. 我们不应该在不安全的网络上使用这个方式;
krb4
用 Kerberos V4 认证用户.只有在进行 TCP/IP 联接的时候才能用;
krb5
用 Kerberos V5 认证用户.只有在进行 TCP/IP 联接的时候才能用;
ident
获取客户的操作系统名(对于 TCP/IP 联接,用户的身份是通过与运行在客户端上的 ident 服务器联接进行判断的,对于本地联接,它是从操作系统获取的。) 然后检查一下,看看用户是否允许以要求的数据库用户进行联接, 方法是参照在 ident 关键字后面声明的映射;
如果你使用了 sameuser 映射,那么假设用户名 是相等的。如果没有声明这个关键字,则在与 pg_hba.conf 同目录的 pg_ident.conf 文件中找出映射名。如果这个文件里包含一条记录标识着ident提供的用户名 和请求的 PostgreSQL 用户名的映射, 那么联接被接受;
对于本地联接,只有在系统支持Unix域套接字信任证的情况下 才能使用(目前是 Linux, FreeBSD, NetBSD, OpenBSD, 和 BSD/OS);
pam
使用操作系统提供的可插入的认证模块服务 (Pluggable Authentication Modules) (PAM)来认证;
1.3 示例;
因为认证时系统是为每个联接请求顺序检查 pg_hba.conf 里的记录的,所以这些记录的顺序是非常关键的.通常,靠前的记录有比较严的联接匹配参数和比较弱的 认证方法,而靠后的记录有比较松的匹配参数和比较严的认证方法.比如,我们一般都希望对本地 TCP 联接使用 trust 认证, 而对远端的 TCP 联接要求口令.在这种情况下我们将 trust 认证方法用于来自 127.0.0.1 的联接,这条记录将出现在 允许更广泛的客户端 IP 地址的使用口令认证的记录前面;
注: 不要禁止超级用户访问 template1 数据库。各种工具命令都 需要访问 template1;
在启动和 postmaster 收到SIGHUP 信号的时候, 系统都会重新装载 pg_hba.conf 文件. 如果你在活跃的系统上编辑了该文件,你就需要用 kill 向 postmaster 发一个 SIGHUP信号,好让它重新读取该文件;
下面是 pg_hba.conf 的一个例子:
# 允许在本机上的任何用户使用 Unix 域套接字(本地连接的缺省)
# 以任何身份联接任何数据库
#
#
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
local all all trust
# 和上面相同,但是使用的是自环的(loopback)TCP/IP 连接
#
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
host all all 127.0.0.1 255.255.255.255 trust
# 同样,但用的是 Unix-套接字联接
local all all trust
# 允许 IP 地址为 192.168.93.x 的任何主机与数据库
# "template1" 相连,用与他们在自己的主机上相同 ident 的用户名标识他自己
# (通常是他的 Unix 用户名)
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
host template1 all 192.168.93.0 255.255.255.0 ident sameuser
# 允许来自主机 192.168.12.10 的用户与 "template1" 数据库联接,
# 只要该用户提供了在正确的口令.
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
host template1 all 192.168.12.10 255.255.255.255 md5
# 如果前面没有其它 "host" 行,那么下面两行将拒绝所有来自
# 192.168.54.1 的联接请求 (因为前面的记录先匹配
# 但是允许来自互联网上其它任何地方的有效的 Kerberos V5 认证的联接
# 零掩码表示不考虑主机 IP 的任何位.因此它匹配任何主机:
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
host all all 192.168.54.1 255.255.255.255 reject
host all all 0.0.0.0 0.0.0.0 krb5
# 允许来自 192.168.x.x 的任何用户与任意数据库联接,只要他们通过 ident 检查
# 但如果 ident 说该用户是 "bryanh" 而他要求以 PostgreSQL 用户 "guest1" 联接,
# 那么只有在 `pg_ident.conf' 里有 "omicron" 的映射,说 "bryanh" 允许以
# "guest1" 进行联接时才真正可以进行联接.
#
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
host all all 192.168.0.0 255.255.0.0 ident omicron
# 如果下面是用于本地联接的仅有的三行,那么它们将允许本地用户
# 只和它们自己的数据库联接(数据库名和用户名同名),
# 只有管理员和组"support"里的成员例外,他们可以联接到任何数据库。
# 文件 $PGDATA/admins 列出了那些允许与所有数据库联接的用户名.
# 在所有情况下都需要口令。
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
local sameuser all md5
local all @admins md5
local all +support md5
# 上面最后两行可以合起来写成一行
local all @admins,+support md5
# 数据库字段也可以使用列表和文件名,但组不行:
local db1,db2,@demodbs all md5
2. 认证方法;
2.1. 信任认证;
如果声明了 trust (信任)认证模式, PostgreSQL 就假设任何可以联接到 服务器的人都可以以任何他声明的数据库用户(包括数据库超级用户) 这个方法应该用于那些在联接到服务器端口已经有足够系统层次保护 的环境里;
trust 认证对于单用户工作站的本地联接是非常合适和方便的. 通常它本身并不适用于多用户环境的机器.不过,即使在多用户的机器上,你也可以使用 trust, 只要你利用文件系统权限限制了对 postmaster 的套接字文件的访问.要做这些限制,你可以设置 postgresql.conf 里面的 参数 unix_socket_permissions (以及可能还有 unix_socket_group).或者你可以 设置 unix_socket_directory,把套接字文件放在 一个经过恰当限制的目录里;
设置文件系统权限只能帮助 Unix 套接字联接.它不会限制本地 TCP 联接; 因此,如果你想利用权限来控制本地安全,那么删除 pg_hba.conf 里的 host ... 127.0.0.1 ... 行,或者把它改为一个 非 trust 的认证方法;
trust 认证模式只有在你信任那些在 pg_hba.conf 里声明为 trust,允许联接到服务器的行上的所有机器上面的所有用户的时候才是合适的.很少有理由使用 trust 作为任何除来自localhost(127.0.0.1) 以外的 TCP 联接的认证方式;
2.2. 口令认证;
以口令为基础的认证方法包括 md5,crypt, 和 password.这些方法操作上非常类似,只不过口令通过联接传送的方法不同.如果你担心口令被窃听("sniffing"), 那么 md5 比较合适,如果你必需支持 7.2 以前的老的客户端,那么可以选 crypt.如果我们在开放的互联网上使用, 应该尽可能避免使用 password(除非你在联接上使用了 SSL, SSH,或者其他通讯安全的联接封装;
PostgreSQL 数据库口令与任何操作系统用户口令无关. 各个数据库用户的口令是存储在pg_shadow系统表里面的.口令可以用查询语言命令CREATE USER 和ALTER USER 等管理,也就是说, CREATE USER foo WITH PASSWORD 'secret';.缺省时,如果没有明确地设置口令,存储的口令是 空并且该用户的口令认证总会失败;
要限制允许访问某数据库的用户集,在 一个独立的文件里列出用户,用户之间用逗号分隔.该文件应该包含逗号分隔的用户名或者每个用户名一行,并且和 pg_hba.conf 在同一行. 在用户列中写该文件的(本)名(不带路径),并且加一个前缀 @.数据库名字列也类似 这样接受一列数值或者一个文件名.你也可以通过在组名字前面 前缀 + 来声明一个组名;
2.3. Kerberos 认证;
Kerberos 是一种适用于在公共网络上进行分布计算的工业标准的安全 认证系统. 对 Kerberos 系统的叙述远远的超出了本文档的范围;概括说来它是相当复杂(同样也相当强大)的系统. Kerberos FAQ 或 MIT 雅典娜计划是个开始探索的好地方.现存在好几种Kerberos发布的源代码;
要使用 Kerberos,对它的支持必须在制作的时候打开. Kerberos 4 和 5 都被支持, 不过我们在一次制作中只能支持一个版本;
PostgreSQL 运行时象一个普通的 Kerberos 服务. 服务主的名字是 servicename/hostname@realm, 这里的 servicename 是 postgres (除非在配置时用 ./configure --with-krb-srvnam=whatever 选择了一个不同的 hostname. hostname 是服务器及其全称的域名字. 服务器的域是服务器机器的优先域;
客户主自给必须用它们自己的 PostgreSQL 用户名作为第一个部件,比如 pgusername/otherstuff@realm. 目前 PostgreSQL 没有检查客户的域;因此如果你打开了跨域的认证,那么在任意域里任何可以和你通讯的主都会被接受;
确认你的服务器的密钥文件是可以被 PostgreSQL服务器帐户读取(最好就是只读的).密钥文件( keytab)的位置是 用运行时配置参数 krb_server_keyfile 声明的;
要生成密钥文件(keytab),可以用下面例子(对版本5)
kadmin% ank -randkey postgres/server.my.domain.org
kadmin% ktadd -k krb5.keytab postgres/server.my.domain.org
阅读 Kerberos 的文档获取详细信息;
在和数据库联接的时候,请确保自己对每个主都拥有一张匹配所请求的数据库 用户名的门票.例子∶对于数据库用户 fred, 主 fred@EXAMPLE.COM 和 fred/users.example.com@EXAMPLE.COM 都可以用于与数据库服务器认证;
如果你在你的Apache web 服务器上使用了mod_auth_krb和 mod_perl, 你可以用一个mod_perl脚本进行 AuthType KerberosV5SaveCredentials. 这样就有了一个通过 web 的安全数据库访问,不需要额外的口令;
2.4. 基于 Ident 的认证;
身份(ident)认证方法的运做模式是使用一个映射文件列出许可的用户 和对应的用户的配对,然后通过检查客户端的操作系统用户名 以及判断许可的数据库用户名的方法来认证。判断客户端的用户名 是非常关键的安全点,根据连接类型的不同,它的实现方法也略有不同;
2.4.1. 透过 TCP/IP 的身份认证
"Identification Protocol(标识协议)"在 RFC 1413 里面描述.实际上每个类Unix的操作系统都带着一个缺省时侦听113端口的身份服务器. 身份服务器的基本功能是回答类似这样的问题: "是什么用户从你的端口X初始化出来联接到我的端口Y上来了?". 因为在建立起物理联接后,PostgreSQL 既知道 X 也知道 Y,因此它可以询问运行尝试联接的客户端的主机,并且理论上可以用这个方法判断 发起联接的操作系统用户;
这样做的缺点是它取决于客户端的完整性:如果客户端不可信 或者被攻击者攻破,而且它们可以在113端口上运行任何程序并且返回他们选择的任何用户的话,就无法认证了. 因此这个认证方法只适用于封闭的网络,这样的网络里的每台客户机都处于严密的控制下并且数据库和操作系统管理员可以比较方便地 联系上.换句话说,你必须信任运行身份(ident)服务的机器.下面是警告:
身份标识协议并不适用于认证或者访问控制协议.
--RFC 1413
2.4.2. 透过本地套接字的身份认证;
在支持用于 Unix 域套接字的SO_PEERCRED请求的系统上, (目前是 Linux, FreeBSD, NetBSD, 和 BSD/OS), 身份认证也可以用于局部联接.这个时候,使用身份认证不会增加 安全风险;实际上这也是在这种系统上使用本地联接时的优选方法;
在没有 SO_PEERCRED 请求的系统上,身份认证只能 通过TCP/IP连接获取。如果需要绕开这个限制,我们可以声明 localhost 地址 127.0.0.1,然后让连接指向这个地址;
2.4.3. Ident 映射;
当使用以身份为基础的认证时,在判断了初始化联接的操作系统用户 的名字后,PostgreSQL 判断他是否可以以他所请求的数据库用户的身份联接. 这个判断是由跟在pg_hba.conf 文件里的 ident 关键字后面的身份映射控制的.有一个预定义的身份映射是sameuser,表示任何 操作系统用户都可以以同名数据库用户进行联接(如果后者存在的 话).其他映射必须手工创建;
非 sameuser 的身份映射存放在数据目录的文件 里.每行的格式通常是:
map-name ident-username database-username
注释和空白和普通情况一样处理.map-name 是将用于 在pg_hba.conf里引用这个映射的任意名称.另外两个域声明某个操作系统用户被允许以哪个数据库用户的身份 进行联接.同一个map-name 可以重复用于在一个映射里声明更多的用户映射.对一个操作系统用户可以映射为多少个数据库用户没有 限制,反之亦然;
在系统启动和 postmaster 收到一个 SIGHUP 信号的时候会读取 pg_ident.conf 文件;如果你在一台活跃的系统上编辑该文件,那么你需要给 postmaster 发信号 (用 pg_ctl reload 或者 kill -HUP) 令其重新读取该文件;
2.5 一个 pg_ident.conf 文件例子;
# MAPNAME IDENT-USERNAME PG-USERNAME
omicron bryanh bryanh
omicron ann ann
# bob 在这台机器上的用户名是 robert
omicron robert bob
# bryanh 也可以以 guest1 身份连接
omicron bryanh guest1
2.6 PAM 认证;
这个认证类型操作起来类似 password, 只不过它使用 PAM(Pluggable Authentication Modules)作为 认证机制。缺省的 PAM 服务名是 postgresql。 你可以在 pam 关键字后面提供自己的可选服务名。
3. 认证问题;
真正的认证失败以及相关的问题通常由类似下面的错误信息 表白自身;
No pg_hba.conf entry for host 123.123.123.123, user andym, database testdb
这条信息出现的最大可能是你已经联接了服务器,但她不愿意和你说话. 就象信息自己表示的那样,服务器拒绝了联接请求,因为她没有在她的 pg_hba.conf 配置文件里找到认证记录;
Password authentication failed for user 'andym'
这样的信息表示你联接了服务器,并且她也愿意和你交谈,但是 你必须通过pg_hba.conf 文件里声明的 认证方法.检查你提交的口令,或者如果错误信息提到 这些 Kerberos 或 IDENT 认证类型时 检查你的这些软件;
FATAL 1: user "andym" does not exist
这是表示此用户不存在的另一方法;
FATAL 1: Database "testdb" does not exist in the system catalog.
你试图联接的数据库不存在.请注意如果你没有声明数据库名,缺省 是数据库用户名,这可能正确也可能不正确;
请注意服务器日志可能包含比报告给客户端的更多的 有关认证失败的信息.如果你被失败的原因搞糊涂了,那么请检查日志。