Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CN/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
*** xref:master/ecosystem_components/pg_readonly.adoc[pg_readonly]
*** xref:master/ecosystem_components/zhparser.adoc[zhparser]
*** xref:master/ecosystem_components/pgbackrest.adoc[pgBackRest]
*** xref:master/ecosystem_components/set_user.adoc[set_user]
* 监控运维
** xref:master/getting-started/daily_monitoring.adoc[日常监控]
** xref:master/getting-started/daily_maintenance.adoc[日常维护]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ IvorySQL 作为一款兼容 Oracle 且基于 PostgreSQL 的高级开源数据库
| 27 | xref:master/ecosystem_components/pg_readonly.adoc[pg_readonly] | 1.0.5 | 可将 PostgreSQL 数据库集群设置为只读 | 系统调试、灾难恢复
| 28 | xref:master/ecosystem_components/zhparser.adoc[zhparser] | master branch | 用于中文全文搜索的PostgreSQL插件,基于SCWS(即:简易中文分词系统)实现了一个中文解析器 | 搜索引擎、关键字提取
| 29 | xref:master/ecosystem_components/pgbackrest.adoc[pgBackRest] | 2.58.0 | 可靠的 PostgreSQL 备份和恢复解决方案 | 容灾备份、大库备份、异地/多层容灾
| 30 | xref:master/ecosystem_components/set_user.adoc[set_user] | REL4_2_0 | PostgreSQL 安全审计扩展,可控角色切换,支持白名单、强制审计、拦截高危操作 | 可控角色切换、权限管理、审计日志
|====

这些插件均经过 IvorySQL 团队的测试和适配,确保在 IvorySQL 环境下稳定运行。用户可以根据业务需求选择合适的插件,进一步提升数据库系统的能力和灵活性。
Expand Down
251 changes: 251 additions & 0 deletions CN/modules/ROOT/pages/master/ecosystem_components/set_user.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@

:sectnums:
:sectnumlevels: 5

= set_user

== 概述
set_user 是 PostgreSQL 安全审计扩展,由 pgaudit 项目维护,优化原生用户切换能力。支持普通用户间互切,也可受控切换至超级用户;可配置白名单限定允许切换的用户,所有切换操作留存审计日志,切换到超级用户时强制完整记录 SQL,拦截修改数据库配置、调用系统命令等高风险操作,规范临时提权,部署需预加载插件并重启数据库,再创建扩展使用。

项目地址:<https://github.com/pgaudit/set_user>

版本:REL4_2_0

开源协议:PostgreSQL License

== 原理介绍

PostgreSQL原生的 `SET ROLE` / `SET SESSION AUTHORIZATION` 有两个安全短板:一是提权后可以随手 `SET log_statement = 'none'` 关掉日志、`RESET ROLE` 悄悄切回来,审计留不下痕迹;二是要让 DBA 能干超级用户的活,通常得给他们能直接登录 superuser 账号,权限过大且不可控。

set_user 的思路是"不禁止提权,但让提权全程留痕、无法抵赖"。部署后,所有超级用户账号可设为 NOLOGIN,DBA 以普通账号登录,需要时调用 set_user_u('postgres') 提权。从提权到 reset_user() 恢复的整个窗口内:用户切换被记入日志,log_statement 被强制改为 all 使每条 SQL 落盘,日志前缀自动追加 AUDIT 标签便于过滤告警;同时 ALTER SYSTEM、COPY PROGRAM、SET log_statement、SET ROLE 及 set_config() 后门等所有可能破坏审计或逃逸身份的通道被全部封锁。由于 session_user 始终保持真实登录者,"谁在什么时候以什么身份做了什么"在日志中一目了然。

实现上,它以 C 扩展形式通过 shared_preload_libraries 加载,核心依赖三个内核机制:ProcessUtility_hook 拦截危险语句,object_access_hook 封堵函数级后门,事务提交回调保证切换的事务安全。权限控制采用双闸设计——SQL 层的 GRANT EXECUTE 决定谁能调用,配置层的白名单(superuser_allowlist 等)可随时热调整收口。此外还提供带口令锁的 set_user(user, token) 供连接池代持连接时防逃逸,以及不可逆的 set_session_auth() 用于连接移交前的永久降权。

set_user 并不能阻止超级用户作恶——它的定位是确保任何特权操作都无法逃避审计,配合日志告警系统,构成 PostgreSQL 最小权限运维体系的关键一环。

== 使用说明

[NOTE]
需要将 `set_user` 添加到 `shared_preload_libraries`。如果有其他插件实现 `post-execution hooks` 那么需要将 `set_user` 列在这些插件之前。

[IMPORTANT]
superuser 账号(如 postgres)需要设为 NOLOGIN,DBA 用普通账号登录,需要时调用 `set_user_u('postgres')` 提权。

** 配置介绍

[cols="1,1,1,1", options="header"]
|===
| 配置项 | 说明 | 示例 | 功能
| set_user.block_alter_system | `on` (默认)或者 `off` | `on` | 阻止 ALTER SYSTEM 命令
| set_user.block_copy_program | `on` (默认)或者 `off` | `on` | 阻止 COPY PROGRAM 命令
| set_user.block_log_statement | `on` (默认)或者 `off` | `on` | 阻止修改 log_statement
| set_user.nosuperuser_target_allowlist | 通配符 '*' (默认)或字符串 | 'dba1, dba2, +admin_group' | 允许 set_user() 切换到的目标用户名单
| set_user.superuser_allowlist | 通配符 '*' (默认)或字符串 | 'dba1, dba2, +admin_group' | 允许调用 set_user_u() 提权的用户名单
| set_user.superuser_audit_tag | 字符串 | 'AUDIT' | 日志前缀标签
| set_user.exit_on_error | `on` (默认)或者 `off` | `on` | 出现错误时是否退出当前会话
|===


** 函数介绍

[cols="1,1,1,1", options="header"]
|===
| 函数 | 默认权限 | 参数 | 说明
| `set_user(text)` | REMOVE FROM PUBLIC | 目标非superuser用户名 | 将当前会话的用户身份切换为指定的非superuser用户
| `set_user(text, text)` | REMOVE FROM PUBLIC | 目标非superuser用户名,随机token | 同上,但需要提供一个随机token以增加安全性
| `set_user_u(text)` | REMOVE FROM PUBLIC | 目标superuser用户名 | 将当前会话的用户身份切换为指定的superuser用户
| `reset_user()` | GRANT TO PUBLIC | 无 | 将当前会话的用户身份恢复为原始用户
| `reset_user(text)` | GRANT TO PUBLIC | 随机token | 带口令恢复,但是需要提供对应的token
| `set_session_auth(text)` | REMOVE FROM PUBLIC | 目标非superuser用户名 | 类似 `SET SESSION AUTHORIZATION` 但是只能切换到普通用户,并且不能切换回来
|===

== 安装

[TIP]
源码测试安装环境为 Ubuntu 26.04。需要安装IvorySQL,具体安装步骤请参考:<https://github.com/IvorySQL/IvorySQL>

=== 源码安装

** 设置IvorySQL安装路径
[source,bash]
----
export IVY_BIN_DIR=/usr/local/ivorysql/bin
export TEST_DB_DIR=/tmp/test_db
----

** 获取源码
[source,bash]
----
git clone https://github.com/pgaudit/set_user.git
cd set_user
git checkout REL4_2_0
----

** 编译安装
[source,bash]
----
make USE_PGXS=1 PG_CONFIG=${IVY_BIN_DIR}/pg_config clean
make USE_PGXS=1 PG_CONFIG=${IVY_BIN_DIR}/pg_config
make USE_PGXS=1 PG_CONFIG=${IVY_BIN_DIR}/pg_config install
----

** 配置
[source,ini]
----
shared_preload_libraries = 'set_user'
----

=== 创建测试数据库

[NOTE]
需要将 `set_user` 添加到 `shared_preload_libraries`。

** 初始化数据库
[source,bash]
----
# 初始化数据库
${IVY_BIN_DIR}/initdb -U postgres -D ${TEST_DB_DIR}

# 将 set_user 添加到 shared_preload_libraries 中,也可以手动配置
sed -i "s/\(shared_preload_libraries = '.*\)'/\1, set_user'/g" ${TEST_DB_DIR}/ivorysql.conf

# 修改PG端口和Oracle兼容端口,避免冲突
cat << EOF >> ${TEST_DB_DIR}/ivorysql.conf
ivorysql.port = 1522
port = 5433
EOF

# 启动测试数据库
${IVY_BIN_DIR}/pg_ctl -D ${TEST_DB_DIR} -l ${TEST_DB_DIR}/logfile start
----

=== 使用测试

** 连接数据库

[source,bash]
----
PGHOST=127.0.0.1 PGPORT=1522 PGUSER=postgres $IVY_BIN_DIR/psql
----

** 测试示例

[TIP]
这里使用默认配置,即白名单为 '*。

[source,sql]
----
CREATE EXTENSION set_user;
LOAD 'set_user';

--- 创建两个用户
CREATE USER dba;
CREATE USER bob;

-- 让 dba 可以执行 set_user()
GRANT EXECUTE ON FUNCTION set_user(text) TO dba;
GRANT EXECUTE ON FUNCTION set_user(text,text) TO dba;
GRANT EXECUTE ON FUNCTION set_user_u(text) TO dba;

-- 切换到用户 dba,模拟dba登录
SET SESSION AUTHORIZATION dba;
SELECT SESSION_USER, CURRENT_USER;
--- session_user | current_user
--- --------------+--------------
--- dba | dba
--- (1 row)

--- 提权, 这里使用 set_user() 提权失败,因为目标用户是 superuser
SELECT set_user('postgres');
--- ERROR: switching to superuser not allowed
--- HINT: Use 'set_user_u' to escalate.

--- 提权, 这里使用 set_user_u() 提权成功
SELECT set_user_u('postgres');
--- set_user_u
--- ------------
--- OK
--- (1 row)

--- 查看提权后的用户状态
SELECT SESSION_USER, CURRENT_USER;
--- session_user | current_user
--- --------------+--------------
--- dba | postgres
--- (1 row)

--- 测试重复 set_user,会失败
SELECT set_user('bob');
--- ERROR: must reset previous user prior to setting again

--- 提权中,测试 ALTER SYSTEM 会失败
ALTER SYSTEM SET wal_level = minimal;
--- ERROR: ALTER SYSTEM blocked by set_user config

--- 提权中,测试 COPY PROGRAM 会失败
COPY (select 42) TO PROGRAM 'cat';
--- ERROR: COPY PROGRAM blocked by set_user config

--- 提权中,测试 SET log_statement 会失败
SET log_statement = 'none';
--- ERROR: "SET log_statement" blocked by set_user config

--- 恢复初始用户
SELECT reset_user();
SELECT SESSION_USER, CURRENT_USER;
--- session_user | current_user
--- --------------+--------------
--- dba | dba
--- (1 row)

--- 切换到普通用户bob
SELECT set_user('bob');
SELECT SESSION_USER, CURRENT_USER;
--- session_user | current_user
--- --------------+--------------
--- dba | bob
--- (1 row)

--- 恢复初始用户
SELECT reset_user();
SELECT SESSION_USER, CURRENT_USER;
--- session_user | current_user
--- --------------+--------------
--- dba | dba
--- (1 row)
----

查看 `logfile` 能看到提权后的所有操作都被记录了下来,并且日志前缀带有 `AUDIT` 标签。每个用户切换都有一条: `LOG: XXX transitioning to YYY` 对应。

[source,log]
----
2026-07-17 13:55:34.905 CST [2670610] LOG: Role dba transitioning to Superuser Role postgres
2026-07-17 13:55:34.905 CST [2670610] STATEMENT: SELECT set_user_u('postgres');
2026-07-16 16:01:21.193 CST [2644289] AUDIT: LOG: statement: SELECT SESSION_USER, CURRENT_USER;
2026-07-16 16:01:43.825 CST [2644289] AUDIT: LOG: statement: SELECT set_user('bob');
2026-07-16 16:01:43.829 CST [2644289] AUDIT: ERROR: must reset previous user prior to setting again
2026-07-16 16:01:43.829 CST [2644289] AUDIT: STATEMENT: SELECT set_user('bob');
2026-07-16 16:02:20.177 CST [2644289] AUDIT: LOG: statement: ALTER SYSTEM SET wal_level = minimal;
2026-07-16 16:02:20.177 CST [2644289] AUDIT: ERROR: ALTER SYSTEM blocked by set_user config
2026-07-16 16:02:20.177 CST [2644289] AUDIT: STATEMENT: ALTER SYSTEM SET wal_level = minimal;
2026-07-17 13:56:26.924 CST [2670610] STATEMENT: SELECT set_user_u('postgres');
2026-07-17 14:01:12.017 CST [2670610] AUDIT: LOG: statement: select reset_user();
2026-07-17 14:01:12.017 CST [2670610] AUDIT: LOG: Superuser Role postgres transitioning to Role dba
2026-07-17 14:18:42.884 CST [2671784] LOG: Role dba transitioning to Role bob
2026-07-17 14:18:42.884 CST [2671784] STATEMENT: select set_user('bob');
2026-07-17 14:18:53.301 CST [2671784] LOG: Role bob transitioning to Role dba
2026-07-17 14:18:53.301 CST [2671784] STATEMENT: select reset_user();
2026-07-17 14:26:16.737 CST [2671784] LOG: Role dba transitioning to Role bob
2026-07-17 14:26:16.737 CST [2671784] STATEMENT: SELECT set_user('bob');
----

=== 清理测试数据库

[source,bash]
----
# 关闭测试数据库
${IVY_BIN_DIR}/pg_ctl -D ${TEST_DB_DIR} -l ${TEST_DB_DIR}/logfile stop

# 删除测试数据库
rm -rf ${TEST_DB_DIR}
----
1 change: 1 addition & 0 deletions EN/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
*** xref:master/ecosystem_components/pg_readonly_en.adoc[pg_readonly]
*** xref:master/ecosystem_components/zhparser_en.adoc[zhparser]
*** xref:master/ecosystem_components/pgbackrest.adoc[pgBackRest]
*** xref:master/ecosystem_components/set_user.adoc[set_user]
* Monitor and O&M
** xref:master/getting-started/daily_monitoring.adoc[Monitoring]
** xref:master/getting-started/daily_maintenance.adoc[Maintenance]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ IvorySQL, as an advanced open-source database compatible with Oracle and based o
|*27*| xref:master/ecosystem_components/pg_readonly_en.adoc[pg_read_only] | 1.0.5 | Allows to set all PostgreSQL cluster databases read only. | System debugging、disaster recevory
|*28*| xref:master/ecosystem_components/zhparser_en.adoc[zhparser] | master branch | PostgreSQL extension for full-text search of Chinese language (Mandarin Chinese). It implements a Chinese language parser base on the | Search engine、keyword extraction
|*29*| xref:master/ecosystem_components/pgbackrest.adoc[pgBackRest] | 2.58.0 | pgBackRest is a reliable backup and restore solution for PostgreSQL that seamlessly scales up to the largest databases and workloads | Disaster recovery backup, large database backup, off-site/multi-tier disaster recovery
| *30* | xref:master/ecosystem_components/set_user.adoc[set_user] | REL4_2_0 | PostgreSQL security auditing extension with controlled role switching, supporting allowlists, enforced auditing, and blocking of high-risk operations | Controlled role switching, privilege management, audit logging
|====

These plugins have all been tested and adapted by the IvorySQL team to ensure stable operation in the IvorySQL environment. Users can select appropriate plugins based on business needs to further enhance the capabilities and flexibility of the database system.
Expand Down
Loading
Loading