Skip to content

pg_ident.conf 用户映射 ​

pg_ident.conf 是 PostgreSQL 的用户名映射配置文件。它的作用只有一件事:当「操作系统用户名」与「数据库角色名」不一致时,告诉 PostgreSQL 它们之间的对应关系。文件本身很小,但它决定了 peer、ident、gss、sspi、cert 这几种外部认证方式能否真正落到正确的数据库角色上。理解它,是打通「操作系统身份 → 数据库身份」这一步的关键。

前置知识

阅读本节前,建议先了解:pg_hba.conf 客户端认证

基础概念 ​

为什么需要用户映射 ​

很多外部认证方式提供的是外部身份,而不是数据库角色名:

认证方式提供的外部身份是否通常需要映射
peerUnix 域套接字的内核 UID 对应的 OS 用户名需要(当 OS 用户名 ≠ 角色名)
ident通过 ident 服务查询到的 OS 用户名需要
gss / sspiKerberos / Windows 域主体名(如 user@REALM)几乎总是需要
cert客户端证书的 Common Nameverify-full 时不需要,verify-ca 时常需要
ldap通过 LDAP 属性映射的用户名可选
text
  客户端                     PostgreSQL                      数据库角色
  ┌──────────────┐          ┌──────────────┐               ┌──────────────┐
  │ OS 用户 alice │──peer──▶│ 取出 "alice"  │──映射查表──▶ │ 角色 appuser  │
  └──────────────┘          └──────────────┘   pg_ident.conf └──────────────┘
                                   │
                                   │ 若 HBA 没用 map= 或映射不匹配
                                   ▼
                            FATAL: Peer authentication failed
                              for user "alice"

不加 map= 时的默认行为

如果没有指定 map=,PostgreSQL 会直接把外部用户名当作数据库角色名去匹配认证。所以:

  • OS 用户 alice → 需要存在角色 alice,否则报 role "alice" does not exist。
  • Kerberos 主体 alice@EXAMPLE.COM → 需要存在同名角色(含 @,几乎不可能),必须用映射。

文件位置 ​

sql
SHOW ident_file;
-- /var/lib/postgresql/18/main/pg_ident.conf

默认位于 PGDATA 目录,可通过 ident_file 参数(postmaster 上下文,需重启)改到别处。

三列格式 ​

pg_ident.conf 每行三列,用空白分隔:

列名称说明
1MAPNAME映射名,与 pg_hba.conf 中的 map= 取值对应
2SYSTEM-USERNAME外部系统提供的用户名(可使用正则)
3DATABASE-USERNAME要映射到的数据库角色名(\1 可引用正则捕获组)
ini
# MAPNAME       SYSTEM-USERNAME         PG-USERNAME
os_db_map       alice                   alice
os_db_map       deploy                  appuser
krb_map         /^(.*)@EXAMPLE\.COM$    \1

与 pg_hba.conf 的关系 ​

两者是一对多的引用关系:一个 map= 名可以包含任意多行映射;一个映射名也可以被多条 HBA 规则引用。

text
pg_hba.conf                       pg_ident.conf
┌───────────────────────────┐    ┌────────────────────────────────────┐
│ local all postgres peer   │    │ admins  root      postgres         │
│   map=admins              │───▶│ admins  postgres  postgres         │
│                           │    │                                    │
│ local all +app_users peer │    │ appmap  deploy    appuser          │
│   map=appmap              │───▶│ appmap  ci        appuser          │
│                           │    │ appmap  jenkins   appuser          │
└───────────────────────────┘    └────────────────────────────────────┘

同名映射名在文件中可以分散在多行,不必连续书写。

匹配后是否还需要角色存在 ​

必须存在。 映射只解决「名字对应关系」,不创建角色。如果映射到 appuser 而该角色不存在,认证依然失败:

text
FATAL:  role "appuser" does not exist

所以「改 pg_ident.conf 让某人能登录」这个想法是不完整的——你还需要 CREATE ROLE。

语法与用法 ​

基本格式 ​

ini
# 注释以 # 开头
# MAPNAME  SYSTEM-USERNAME  PG-USERNAME
mymap      alice            alice
mymap      bob              alice          # 两个 OS 用户映射到同一个角色

格式规则:

  • 三列必填,用空白(空格或 Tab)分隔。
  • 大小写敏感(与 postgresql.conf 的参数名不同)。
  • 支持 include / include_if_exists / include_dir 指令(与 postgresql.conf 相同的语义)。
  • 空行与注释行被忽略。

all 关键字 ​

第三列(PG-USERNAME)支持 all,含义是「映射到与请求的数据库角色同名」:

ini
# MAPNAME  SYSTEM-USERNAME  PG-USERNAME
mymap      alice            all      # 数据库端请求谁,就以谁的身份认证

all 加引号会失去特殊含义

官方文档明确提示:all 必须不加引号才具有关键字含义。写成 "all" 会被当作字面角色名 all。

ini
# 正确:关键字
mymap  alice  all
# 错误!被当成字面角色名 "all"
mymap  alice  "all"

all 的实用场景:一批外部用户各自对应同名的数据库角色,不需要逐个列举。

ini
# 所有 EXAMPLE.COM 域用户,各自对应同名数据库角色
krb_map  /^(.*)@EXAMPLE\.COM$  all

注意此时 SYSTEM-USERNAME 是正则、PG-USERNAME 是 all,那么请求的角色名必须与完整的外部用户名匹配——这通常不是想要的。所以更常见的是配合捕获组:

ini
# 把 alice@EXAMPLE.COM 映射到角色 alice
krb_map  /^(.*)@EXAMPLE\.COM$  \1

+role 前缀 ​

第三列支持 +role 语法,表示「映射到某个角色的成员身份」检查:

ini
# MAPNAME  SYSTEM-USERNAME  PG-USERNAME
mymap      alice            +app_users

含义:外部用户 alice 被映射为「属于 app_users 角色的用户」。这常用于「外部身份只需属于某角色组即可登录」的场景。

正则与捕获组 ​

第二列(SYSTEM-USERNAME)以 / 开头时按正则解释。第三列(DATABASE-USERNAME)中出现 \1、\2 等时,会替换为第二列正则的捕获组内容。

ini
# 单个捕获组 + \1 引用
krb_map   /^(.*)@EXAMPLE\.COM$   \1

# 多捕获组
host_map  /^([a-z]+)_([a-z]+)$   \1

# 域后缀固定,只取用户名
mail_map  /^([^@]+)@example\.com$  \1

官方文档给出的示例(Example 20.2 风格),把 omicron 相关用户映射到不同角色:

ini
# MAPNAME   SYSTEM-USERNAME      PG-USERNAME
omicron     bryan                bryan
omicron     ann                   sales
omicron     robert                sales
omicron     zoe                   sales
omicron     /^.*@omicron\.com$   bob

第三列用正则时不能引用 \1

官方明确规定:当 DATABASE-USERNAME 本身是正则表达式(以 / 开头)时,不能使用 \1 这类反向引用。 \1 只在第三列是普通角色名时才有捕获组替换语义。

ini
# 合法:第三列是普通名,可引用 \1
good  /^(.*)@EXAMPLE\.COM$  \1

# 不合法:第三列同时是正则又想引用 \1,行为不符合预期
bad   /^(.*)@EXAMPLE\.COM$  /^db_\1$/

include 机制 ​

ini
# 主文件
include 'ident.d/10_os_users.conf'
include_dir 'ident.d'
include_if_exists 'ident.d/99_local.conf'

三者的语义与 postgresql.conf 完全一致:include 文件不存在会报错,include_if_exists 静默忽略,include_dir 只读 .conf 结尾、不以 . 开头的文件,按 C locale 排序。

pg_ident_file_mappings 视图 ​

这是核验映射是否被正确解析的核心视图:

sql
SELECT map_number, map_name, sys_name, pg_username, error
FROM pg_ident_file_mappings
ORDER BY map_number;
text
 map_number | map_name |       sys_name        | pg_username | error
------------+----------+-----------------------+-------------+-------
          1 | os_db_map| alice                 | alice       |
          2 | os_db_map| deploy                | appuser     |
          3 | krb_map  | /^(.*)@EXAMPLE\.COM$  | \1          |

解析失败的行只有 line_number 与 error 有值(map_number 为 NULL):

sql
SELECT line_number, error FROM pg_ident_file_mappings WHERE error IS NOT NULL;

修改后如何生效 ​

pg_ident.conf 属于 sighup 级别,reload 即可:

bash
pg_ctl reload -D /var/lib/postgresql/18/main
sql
SELECT pg_reload_conf();

详细说明 ​

一、peer 认证 + 映射的完整链路 ​

这是最常用的组合。假设服务器上有 OS 用户 deploy,但应用要连的数据库角色是 appuser,且要求免密(因为部署脚本以 deploy 用户运行)。

第一步,创建角色并授权:

sql
CREATE ROLE appuser WITH LOGIN;
GRANT CONNECT ON DATABASE myapp TO appuser;
GRANT USAGE ON SCHEMA public TO appuser;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser;

第二步,配置映射:

ini
# pg_ident.conf
appmap   deploy   appuser

第三步,在 HBA 中引用:

ini
# pg_hba.conf
local  myapp  appuser  peer  map=appmap

第四步,reload 并验证:

bash
sudo -u deploy psql -d myapp -c "SELECT current_user, session_user;"
text
 current_user | session_user
--------------+--------------
 appuser      | appuser
(1 row)

如果缺了 map=appmap,会得到:

text
$ sudo -u deploy psql -d myapp
FATAL:  Peer authentication failed for user "deploy"

排查 peer 认证失败

报 Peer authentication failed for user "X" 时,含义是「外部身份是 X,但没有匹配到规则/映射」:

  1. 检查 HBA 中该行的 map= 是否指向了正确的映射名。
  2. 检查映射名在 pg_ident.conf 中是否存在(大小写敏感)。
  3. 检查映射的第一列是否精确等于 X。
  4. 检查映射到的角色是否真的存在。

二、GSSAPI / Kerberos 场景的映射 ​

Kerberos 主体名通常带 Realm(域),如 alice@EXAMPLE.COM,而数据库角色名只有 alice。这是映射最典型的用途。

ini
# pg_ident.conf:剥离 @REALM
krb_map   /^(.*)@EXAMPLE\.COM$   \1
ini
# pg_hba.conf
hostgssenc  all  all  0.0.0.0/0  gss  map=krb_map
host        all  all  0.0.0.0/0  gss  map=krb_map

如果 Kerberos 主体名就是数据库角色名(无域),则不需要映射:

ini
# 不加 map=,直接同名匹配
hostgssenc  all  all  0.0.0.0/0  gss

检查 Kerberos 下 HBA 规则中的包括名:官方文档提到 include_realm 选项(默认 1),它决定是否在传给映射前保留 Realm 部分:

ini
# include_realm=1(默认):传给映射的是 alice@EXAMPLE.COM
hostgssenc  all  all  0.0.0.0/0  gss  include_realm=1  map=krb_map
# include_realm=0:先剥掉 Realm,传给映射的是 alice
hostgssenc  all  all  0.0.0.0/0  gss  include_realm=0  map=krb_map

对应两种映射写法:

ini
# include_realm=1 时的映射
krb_map  /^(.*)@EXAMPLE\.COM$  \1
ini
# include_realm=0 时的映射(只需处理裸用户名)
krb_map  alice  alice

GSSAPI 的 krb_realm 选项

把 krb_realm=EXAMPLE.COM 写在 HBA 行里,可以限定只接受该 Realm 的主体,防止其他 Realm 的同名用户冒用。这与映射是互补的两层防护。

三、Windows SSPI 场景 ​

Windows 域环境使用 sspi,映射写法与 GSSAPI 类似:

ini
# pg_ident.conf
sspi_map  /^(.*)@EXAMPLE$   \1
ini
# pg_hba.conf
host  all  all  0.0.0.0/0  sspi  map=sspi_map

Windows 用户的 SSPI 名字通常是 DOMAIN\user 或 user@DOMAIN 形式。可以用正则统一处理:

ini
# 处理 DOMAIN\user 形式
sspi_map  /^EXAMPLE\\(.*)$  \1

四、cert 认证下的映射 ​

cert 认证使用客户端证书的 Common Name(CN)作为外部身份。verify-full 时 CN 必须等于角色名(无需映射);verify-ca 时只校验证书由受信 CA 签发,需要用映射把 CN 对应到角色。

ini
# pg_hba.conf
# verify-full:不需要映射
hostssl  all  all  0.0.0.0/0  cert  clientcert=verify-full
# verify-ca:需要映射
hostssl  all  all  0.0.0.0/0  cert  clientcert=verify-ca  map=cert_map
ini
# pg_ident.conf
# CN 形如 alice@example.com,映射到角色 alice
cert_map  /^([^@]+)@example\.com$/  \1

verify-ca + 无映射 = 任何人可用任意 CN

verify-ca 只验证「证书由受信 CA 签发」,不验证 CN 与角色名的对应关系。若此时又不加 map=,则任何持有该 CA 签发证书、且证书 CN 恰好是某角色名的人都能登录。

安全做法:要么用 verify-full,要么用 verify-ca + 严格的正则映射。

五、多个外部身份映射到同一个角色 ​

常见于「值班轮换」或「多个 CI runner 都用同一个部署身份」:

ini
# pg_ident.conf
deploy_map  deploy_prod     appuser
deploy_map  deploy_staging  appuser
deploy_map  jenkins         appuser
deploy_map  gitlab_runner   appuser
ini
# pg_hba.conf
local  myapp  appuser  peer  map=deploy_map

审计时要注意:数据库端看到的都是 appuser,无法区分具体是哪个 OS 用户。若需要区分,可以在 HBA 中加 log_line_prefix 记录外部身份,或改用不同角色。

sql
-- 通过 pg_stat_activity 只能看到最终角色
SELECT usename, client_addr, application_name FROM pg_stat_activity;

六、all 与 +role 的联合使用 ​

用 +role 做「外部身份只需属于某组」的粗粒度控制:

ini
# pg_ident.conf
# 任意外部用户映射为 app_users 组成员
app_map  /^ci-.*$/  +app_users

配合 HBA:

ini
# pg_hba.conf
local  myapp  +app_users  peer  map=app_map

含义:外部用户名以 ci- 开头的,被认作 app_users 角色的成员,从而通过 HBA 中 +app_users 的匹配。

七、include 组织映射文件 ​

域用户多时,映射文件会变长,用 include_dir 拆分:

text
PGDATA/pg_ident.d/
├── 10_os_users.conf     # 本机 OS 用户
├── 20_kerberos.conf     # Kerberos 主体现
├── 30_ci_runners.conf   # CI 机器人
└── 99_local.conf        # 本机覆盖
ini
# pg_ident.conf
include_dir 'pg_ident.d'
ini
# pg_ident.d/20_kerberos.conf
krb_map  /^(.*)@EXAMPLE\.COM$   \1
krb_map  /^(.*)@CORP\.EXAMPLE\.COM$  \1

八、预检与核验的固定流程 ​

和 HBA 一样,改完先预检:

sql
-- 1. 检查解析错误
SELECT line_number, error FROM pg_ident_file_mappings WHERE error IS NOT NULL;
text
 line_number | error
-------------+-------
(0 rows)
sql
-- 2. reload
SELECT pg_reload_conf();
-- 3. 核验映射清单
SELECT map_name, sys_name, pg_username FROM pg_ident_file_mappings ORDER BY map_name, map_number;
text
 map_name  |       sys_name        | pg_username
-----------+-----------------------+-------------
 krb_map   | /^(.*)@EXAMPLE\.COM$  | \1
 os_db_map | alice                 | alice
 os_db_map | deploy                | appuser
(3 rows)
sql
-- 4. 确认被映射的角色真的存在
SELECT rolname FROM pg_roles WHERE rolname IN ('alice', 'appuser') ORDER BY rolname;
text
 rolname
---------
 alice
 appuser
(2 rows)

九、用 grep 辅助排查映射名拼写 ​

映射名是普通字符串,拼错不会报错,只会导致「映射不生效」。用文本搜索快速核对:

bash
# 找出 HBA 中所有 map= 引用
grep -o 'map=[A-Za-z0-9_]*' $PGDATA/pg_hba.conf | sort -u

# 列出 ident 文件中所有映射名
awk '!/^#/ && NF>=3 {print $1}' $PGDATA/pg_ident.conf | sort -u

两边的输出做 diff,就能发现「HBA 引用了不存在的映射名」或「定义了映射但没人引用」。

十、映射名大小写与引号 ​

若映射名或角色名包含需要引号的字符(如空格、大写),用双引号包裹:

ini
# 需要引号的情形
mymap   alice   "Alice Admin"

但不加引号的标识符会被折叠为原样字符串(不像 SQL 标识符会折叠为小写),所以:

ini
# 下面两行是同一个映射名,不是两个
MyMap  alice  alice
mymap  bob    bob

角色名中的大写必须加引号

PostgreSQL 的角色名在 SQL 中不加引号会折叠为小写。如果角色真的叫 Alice(含大写),pg_ident.conf 里必须写成 "Alice",否则会去找小写的 alice。

sql
-- 创建了含大写的角色
CREATE ROLE "Alice" LOGIN;
ini
# 必须加引号才匹配
mymap  alice  "Alice"

实战示例 ​

场景:为「本地 OS 用户 + Kerberos 域用户 + CI 机器人」统一映射 ​

一家公司有三类登录来源,要求:

  1. 服务器上 OS 用户 postgres 免密管理(peer)。
  2. OS 用户 deploy 以角色 appuser 免密执行部署(peer)。
  3. Kerberos 域 EXAMPLE.COM 的用户,各自映射到同名数据库角色(gss)。
  4. CI runner(用户名以 ci- 开头)统一映射到角色 ci_bot(peer)。

第一步,准备角色:

sql
-- 管理角色
-- postgres 已存在
-- 应用部署角色
CREATE ROLE appuser WITH LOGIN;
-- CI 机器人角色
CREATE ROLE ci_bot WITH LOGIN;
-- Kerberos 用户对应角色
CREATE ROLE alice WITH LOGIN;
CREATE ROLE bob   WITH LOGIN;
text
CREATE ROLE appuser WITH LOGIN;
CREATE ROLE
CREATE ROLE ci_bot WITH LOGIN;
CREATE ROLE
CREATE ROLE alice WITH LOGIN;
CREATE ROLE
CREATE ROLE bob   WITH LOGIN;
CREATE ROLE

第二步,写映射文件:

ini
# =========================================================
# pg_ident.conf  生成日期 2026-09-29
# 三列:MAPNAME  SYSTEM-USERNAME  PG-USERNAME
# 映射名大小写敏感;系统用户名以 / 开头表示正则
# =========================================================

# ---- 1. 本地管理:postgres 直接同名(也可省略映射) ----
admins      postgres        postgres

# ---- 2. 部署账号:OS deploy 映射到 appuser ----
deploy_map  deploy          appuser
deploy_map  root            postgres

# ---- 3. Kerberos:剥离 @EXAMPLE.COM,映射到同名角色 ----
krb_map     /^(.*)@EXAMPLE\.COM$   \1

# ---- 4. CI 机器人:ci-* 统一映射到 ci_bot ----
ci_map      /^ci-.*$        ci_bot

第三步,HBA 引用这些映射:

ini
# pg_hba.conf
# 本地管理:peer + admins 映射
local   all            postgres   peer  map=admins
local   all            appuser    peer  map=deploy_map
local   all            ci_bot     peer  map=ci_map

# Kerberos:gss + krb_map
hostgssenc  all  all  0.0.0.0/0  gss  include_realm=1  map=krb_map
host        all  all  0.0.0.0/0  gss  include_realm=1  map=krb_map

# 兜底
host    all  all  0.0.0.0/0  reject

第四步,reload 并核对:

bash
pg_ctl reload -D /var/lib/postgresql/18/main
sql
-- 核验映射解析结果
SELECT map_name, sys_name, pg_username
FROM pg_ident_file_mappings
ORDER BY map_name, map_number;
text
 map_name  |          sys_name           | pg_username
-----------+-----------------------------+-------------
 admins    | postgres                    | postgres
 ci_map    | /^ci-.*$                    | ci_bot
 deploy_map| deploy                      | appuser
 deploy_map| root                        | postgres
 krb_map   | /^(.*)@EXAMPLE\.COM$        | \1
(5 rows)

第五步,端到端验证:

bash
# 1) OS deploy 用户 -> appuser
$ sudo -u deploy psql -d myapp -c "SELECT current_user, session_user;"
 current_user | session_user
--------------+--------------
 appuser      | appuser
(1 row)

# 2) CI runner 用户 ci-runner-01 -> ci_bot
$ sudo -u ci-runner-01 psql -d myapp -c "SELECT current_user;"
 current_user
--------------
 ci_bot
(1 row)

# 3) 未配置映射的 OS 用户 -> 失败
$ sudo -u nobody psql -d myapp
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed:
FATAL:  no pg_hba.conf entry for host "[local]", user "nobody", database "myapp", no encryption

# 4) Kerberos 用户 alice@EXAMPLE.COM -> alice
$ kinit alice@EXAMPLE.COM
$ psql "host=db.example.com dbname=myapp user=alice"
myapp=> SELECT current_user, session_user;
 current_user | session_user
--------------+--------------
 alice        | alice
(1 row)

结果解读:

  • 第 2 步证明正则映射 ^ci-.*$ → ci_bot 生效,多个 CI runner 共用一个数据库角色。
  • 第 3 步的报错说明 nobody 既没落在 deploy_map 也没落在 ci_map 里,HBA 无匹配入口——符合预期(拒绝)。
  • 第 4 步证明 krb_map 的 \1 捕获组把 alice@EXAMPLE.COM 正确裁剪成了 alice。

注意事项 ​

不要忘记在 HBA 里写 map=。 只定义了映射而不引用,映射不会生效,登录会去找同名角色。

ini
# pg_ident.conf 定义了,但 HBA 没引用 —— 无效
appmap  deploy  appuser
ini
# 错误!缺少 map=,会去找角色 "deploy"
local  myapp  appuser  peer
# 正确
local  myapp  appuser  peer  map=appmap

不要把 all 写成 "all"。 加引号后关键字语义丢失,会被当成字面角色名。

ini
# 错误!
mymap  alice  "all"
# 正确
mymap  alice  all

第三列是正则时不要用 \1。 官方明确不支持这种组合。

ini
# 错误!第三列同时是正则又想引用捕获组
bad  /^(.*)@EXAMPLE\.COM$/  /^db_\1$/
# 正确:第三列用普通角色名
good  /^(.*)@EXAMPLE\.COM$/  \1

映射到的角色必须存在。 映射不是创建角色,只是改名字对应关系。

sql
-- 忘记创建角色时会失败
-- FATAL: role "appuser" does not exist
CREATE ROLE appuser WITH LOGIN;   -- 必须

不要指望 peer 能映射远程连接。 peer 只对 Unix 域套接字(local 类型)有意义;TCP 连接要用 ident 或 gss 等。

ini
# 错误!host 类型不支持 peer
host  all  all  10.0.0.0/24  peer

大小写一定要核对。 映射名与系统用户名都是大小写敏感的字符串比较。

ini
# 这两行是不同的映射
MyMap  alice  alice
mymap  alice  alice

角色名含大写时必须加引号。 否则 SQL 层会折叠为小写,导致映射到不存在的角色。

ini
# 正确
mymap  alice  "Alice"
# 错误!会去找小写 alice
mymap  alice  Alice

不要用 verify-ca + 无映射的 cert 认证。 这等于「任何持有受信 CA 证书的人都能以任意角色登录」。

ini
# 危险
hostssl  all  all  0.0.0.0/0  cert  clientcert=verify-ca
# 安全
hostssl  all  all  0.0.0.0/0  cert  clientcert=verify-full

include 的文件路径相对路径解析基准要确认。 与 postgresql.conf 相同,相对路径相对于当前配置文件所在目录,不是 PGDATA。

ini
# 若 pg_ident.conf 在 /etc/postgresql/,则此路径解析为 /etc/postgresql/ident.d/...
include 'ident.d/10_os_users.conf'

改完不要忘记 reload。 pg_ident.conf 是 sighup 级别,不 reload 不生效。

bash
pg_ctl reload -D $PGDATA

不要用映射来「绕过」角色权限设计。 把大量外部身份都映射到 postgres 超级用户是很常见的错误做法。

ini
# 危险!所有人都是超级用户
wide_map  /^.*$/  postgres

Kerberos 的 Realm 要限定。 不加 krb_realm 时,任何 Realm 中同名主体都可能被接受。

ini
# 推荐:限定 Realm
hostgssenc  all  all  0.0.0.0/0  gss  krb_realm=EXAMPLE.COM  map=krb_map

排查失败时先看视图,不要猜文件。 pg_ident_file_mappings 才是「系统实际解析到的内容」。

sql
SELECT * FROM pg_ident_file_mappings WHERE error IS NOT NULL;

最佳实践 ​

为每个用途起一个语义清晰的映射名。 名字里体现出「谁映射到谁」,避免 map1、map2。

ini
deploy_map   deploy   appuser
ci_map       /^ci-.*$/  ci_bot
krb_map      /^(.*)@EXAMPLE\.COM$/  \1

正则映射优先用捕获组剥离域后缀。 这样新增域用户不需要改文件。

ini
krb_map  /^(.*)@EXAMPLE\.COM$/  \1

映射文件拆分到 pg_ident.d/ 并按来源分类。 本机 OS 用户、Kerberos、CI 各自独立,变更互不影响。

ini
include_dir 'pg_ident.d'

把「映射清单」和「HBA 引用清单」做交叉核对。 两边不一致是最高频的故障原因。

bash
grep -o 'map=[A-Za-z0-9_]*' $PGDATA/pg_hba.conf | sort -u
awk '!/^#/ && NF>=3 {print $1}' $PGDATA/pg_ident.conf | sort -u

预检 + reload + 核验,三步固定执行。

sql
SELECT line_number, error FROM pg_ident_file_mappings WHERE error IS NOT NULL;
SELECT pg_reload_conf();
SELECT map_name, sys_name, pg_username FROM pg_ident_file_mappings ORDER BY map_name, map_number;

用 +role 做粗粒度分组,减少逐条维护。 尤其适合角色成员经常变动的场景。

ini
app_map  /^ci-.*$/  +app_users

对 CI/CD 机器人单独建角色,避免与人类账号混用。 便于审计、便于一次性吊销。

sql
CREATE ROLE ci_bot WITH LOGIN;
REVOKE ALL ON DATABASE myapp FROM ci_bot;
GRANT CONNECT ON DATABASE myapp TO ci_bot;

Kerberos / SSPI 场景一律显式限定 Realm/Domain。 防止其他信任域的同名主体冒用。

ini
hostgssenc  all  all  0.0.0.0/0  gss  krb_realm=EXAMPLE.COM  map=krb_map

证书认证优先 verify-full。 只有在必须用 verify-ca 时才依赖映射,并且映射要用严格正则。

ini
hostssl  all  all  0.0.0.0/0  cert  clientcert=verify-full

把映射与 HBA 变更写进同一个变更单。 它们是一体两面,分开改动极易漏掉一半。

text
变更单字段:
- HBA 新增行:local myapp appuser peer map=deploy_map
- ident 新增行:deploy_map deploy appuser
- 前置:CREATE ROLE appuser WITH LOGIN;
- 验证命令:sudo -u deploy psql -d myapp -c "SELECT current_user;"

映射文件纳入版本库。 它是安全配置的一部分,需要评审、需要历史可追溯。

bash
git add pg_ident.conf pg_ident.d/
git commit -m "feat(hba): add kerberos realm mapping and ci bot account"

每个映射行都写注释说明业务来源。 半年后没人记得 ci-.* 是哪套流水线。

ini
# GitLab 自建 runner(用户名 ci-runner-*),统一到 ci_bot
ci_map  /^ci-.*$/  ci_bot

定期清理无引用的映射。 用交叉核对脚本找出「定义了但 HBA 没引用」的映射名,避免配置腐化。

bash
comm -23 <(awk '!/^#/ && NF>=3 {print $1}' $PGDATA/pg_ident.conf | sort -u) \
         <(grep -o 'map=[A-Za-z0-9_]*' $PGDATA/pg_hba.conf | cut -d= -f2 | sort -u)

下一节

继续学习:配置参数生效方式

参考链接 ​