diff --git a/CN/modules/ROOT/nav.adoc b/CN/modules/ROOT/nav.adoc index 991c4af..473ba5e 100644 --- a/CN/modules/ROOT/nav.adoc +++ b/CN/modules/ROOT/nav.adoc @@ -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[日常维护] diff --git a/CN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc b/CN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc index c766670..074ce67 100644 --- a/CN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc +++ b/CN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc @@ -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 环境下稳定运行。用户可以根据业务需求选择合适的插件,进一步提升数据库系统的能力和灵活性。 diff --git a/CN/modules/ROOT/pages/master/ecosystem_components/set_user.adoc b/CN/modules/ROOT/pages/master/ecosystem_components/set_user.adoc new file mode 100644 index 0000000..267d924 --- /dev/null +++ b/CN/modules/ROOT/pages/master/ecosystem_components/set_user.adoc @@ -0,0 +1,251 @@ + +:sectnums: +:sectnumlevels: 5 + += set_user + +== 概述 +set_user 是 PostgreSQL 安全审计扩展,由 pgaudit 项目维护,优化原生用户切换能力。支持普通用户间互切,也可受控切换至超级用户;可配置白名单限定允许切换的用户,所有切换操作留存审计日志,切换到超级用户时强制完整记录 SQL,拦截修改数据库配置、调用系统命令等高风险操作,规范临时提权,部署需预加载插件并重启数据库,再创建扩展使用。 + +项目地址: + +版本: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,具体安装步骤请参考: + +=== 源码安装 + +** 设置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} +---- \ No newline at end of file diff --git a/EN/modules/ROOT/nav.adoc b/EN/modules/ROOT/nav.adoc index b071b69..8194fcd 100644 --- a/EN/modules/ROOT/nav.adoc +++ b/EN/modules/ROOT/nav.adoc @@ -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] diff --git a/EN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc b/EN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc index 79180a5..4e3270c 100644 --- a/EN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc +++ b/EN/modules/ROOT/pages/master/ecosystem_components/ecosystem_overview.adoc @@ -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. diff --git a/EN/modules/ROOT/pages/master/ecosystem_components/set_user.adoc b/EN/modules/ROOT/pages/master/ecosystem_components/set_user.adoc new file mode 100644 index 0000000..7eeb7d6 --- /dev/null +++ b/EN/modules/ROOT/pages/master/ecosystem_components/set_user.adoc @@ -0,0 +1,250 @@ +:sectnums: +:sectnumlevels: 5 + += set_user + +== Overview +set_user is a PostgreSQL security auditing extension maintained by the pgaudit project that enhances the native user-switching capability. It supports switching between ordinary users as well as controlled escalation to superuser; configurable allowlists restrict which users may be switched to, all switching operations are recorded in the audit log, full SQL logging is enforced when switching to superuser, and high-risk operations such as modifying database configuration or invoking system commands are blocked. It standardizes temporary privilege escalation. Deployment requires preloading the plugin and restarting the database, then creating the extension before use. + +Project page: + +Version: REL4_2_0 + +License: PostgreSQL License + +== How It Works + +PostgreSQL's native `SET ROLE` / `SET SESSION AUTHORIZATION` has two security weaknesses: first, after escalating privileges a user can casually run `SET log_statement = 'none'` to turn off logging, or `RESET ROLE` to quietly switch back, leaving no audit trail; second, to let DBAs perform superuser work, they typically must be allowed to log in directly as a superuser account, granting excessive and uncontrolled privileges. + +set_user's approach is "don't forbid privilege escalation, but make every escalation fully traceable and non-repudiable." Once deployed, all superuser accounts can be set to NOLOGIN; DBAs log in with ordinary accounts and call set_user_u('postgres') when escalation is needed. Throughout the window from escalation until reset_user() restores the original identity: the user switch is written to the log, log_statement is forcibly set to all so that every SQL statement is persisted, and the log prefix automatically gains an AUDIT tag for easy filtering and alerting; at the same time, every channel that could undermine auditing or escape the identity — ALTER SYSTEM, COPY PROGRAM, SET log_statement, SET ROLE, and the set_config() backdoor — is blocked. Because session_user always remains the real login user, "who did what, when, and under which identity" is plainly visible in the logs. + +Implementation-wise, it is a C extension loaded via shared_preload_libraries and relies on three kernel mechanisms: ProcessUtility_hook to intercept dangerous statements, object_access_hook to block function-level backdoors, and transaction commit callbacks to guarantee transactional safety of the switch. Access control uses a dual-gate design — at the SQL layer, GRANT EXECUTE determines who may call the functions, while at the configuration layer, allowlists (superuser_allowlist, etc.) can be hot-adjusted at any time to tighten control. In addition, it provides a token-locked set_user(user, token) to prevent escape when a connection pooler holds connections on behalf of clients, and the irreversible set_session_auth() for permanent privilege drop before handing over a connection. + +set_user cannot prevent a superuser from acting maliciously — its role is to ensure that no privileged operation can evade auditing. Combined with a log alerting system, it forms a key component of a least-privilege operations framework for PostgreSQL. + +== Usage Notes + +[NOTE] +`set_user` must be added to `shared_preload_libraries`. If other plugins implement `post-execution hooks`, `set_user` must be listed before them. + +[IMPORTANT] +Superuser accounts (e.g., postgres) should be set to NOLOGIN; DBAs log in with ordinary accounts and call `set_user_u('postgres')` when escalation is needed. + +** Configuration options + +[cols="1,1,1,1", options="header"] +|=== +| Option | Description | Example | Function +| set_user.block_alter_system | `on` (default) or `off` | `on` | Block ALTER SYSTEM commands +| set_user.block_copy_program | `on` (default) or `off` | `on` | Block COPY PROGRAM commands +| set_user.block_log_statement | `on` (default) or `off` | `on` | Block changes to log_statement +| set_user.nosuperuser_target_allowlist | Wildcard '*' (default) or string | 'dba1, dba2, +admin_group' | List of target users that set_user() may switch to +| set_user.superuser_allowlist | Wildcard '*' (default) or string | 'dba1, dba2, +admin_group' | List of users allowed to call set_user_u() for escalation +| set_user.superuser_audit_tag | String | 'AUDIT' | Log prefix tag +| set_user.exit_on_error | `on` (default) or `off` | `on` | Whether to exit the current session on error +|=== + + +** Functions + +[cols="1,1,1,1", options="header"] +|=== +| Function | Default privilege | Parameters | Description +| `set_user(text)` | REMOVE FROM PUBLIC | Target non-superuser username | Switch the current session's user identity to the specified non-superuser +| `set_user(text, text)` | REMOVE FROM PUBLIC | Target non-superuser username, random token | Same as above, but requires a random token for added security +| `set_user_u(text)` | REMOVE FROM PUBLIC | Target superuser username | Switch the current session's user identity to the specified superuser +| `reset_user()` | GRANT TO PUBLIC | None | Restore the current session's user identity to the original user +| `reset_user(text)` | GRANT TO PUBLIC | Random token | Token-protected restore; the matching token must be supplied +| `set_session_auth(text)` | REMOVE FROM PUBLIC | Target non-superuser username | Similar to `SET SESSION AUTHORIZATION`, but can only switch to an ordinary user and cannot be reverted +|=== + +== Installation + +[TIP] +The source build was tested on Ubuntu 26.04. IvorySQL must be installed; for installation steps see: + +=== Building from Source + +** Set the IvorySQL installation path +[source,bash] +---- +export IVY_BIN_DIR=/usr/local/ivorysql/bin +export TEST_DB_DIR=/tmp/test_db +---- + +** Get the source code +[source,bash] +---- +git clone https://github.com/pgaudit/set_user.git +cd set_user +git checkout REL4_2_0 +---- + +** Build and install +[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 +---- + +** Configuration +[source,ini] +---- +shared_preload_libraries = 'set_user' +---- + +=== Creating a Test Database + +[NOTE] +`set_user` must be added to `shared_preload_libraries`. + +** Initialize the database +[source,bash] +---- +# Initialize the database +${IVY_BIN_DIR}/initdb -U postgres -D ${TEST_DB_DIR} + +# Add set_user to shared_preload_libraries (this can also be done manually) +sed -i "s/\(shared_preload_libraries = '.*\)'/\1, set_user'/g" ${TEST_DB_DIR}/ivorysql.conf + +# Change the PG port and the Oracle-compatible port to avoid conflicts +cat << EOF >> ${TEST_DB_DIR}/ivorysql.conf +ivorysql.port = 1522 +port = 5433 +EOF + +# Start the test database +${IVY_BIN_DIR}/pg_ctl -D ${TEST_DB_DIR} -l ${TEST_DB_DIR}/logfile start +---- + +=== Usage Test + +** Connect to the database + +[source,bash] +---- +PGHOST=127.0.0.1 PGPORT=1522 PGUSER=postgres $IVY_BIN_DIR/psql +---- + +** Test examples + +[TIP] +The default configuration is used here, i.e., the allowlist is '*'. + +[source,sql] +---- +CREATE EXTENSION set_user; +LOAD 'set_user'; + +--- Create two users +CREATE USER dba; +CREATE USER bob; + +-- Allow dba to execute 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; + +-- Switch to user dba to simulate a DBA login +SET SESSION AUTHORIZATION dba; +SELECT SESSION_USER, CURRENT_USER; +--- session_user | current_user +--- --------------+-------------- +--- dba | dba +--- (1 row) + +--- Escalate privileges: using set_user() here fails because the target user is a superuser +SELECT set_user('postgres'); +--- ERROR: switching to superuser not allowed +--- HINT: Use 'set_user_u' to escalate. + +--- Escalate privileges: using set_user_u() here succeeds +SELECT set_user_u('postgres'); +--- set_user_u +--- ------------ +--- OK +--- (1 row) + +--- Check the user state after escalation +SELECT SESSION_USER, CURRENT_USER; +--- session_user | current_user +--- --------------+-------------- +--- dba | postgres +--- (1 row) + +--- Test a repeated set_user; it fails +SELECT set_user('bob'); +--- ERROR: must reset previous user prior to setting again + +--- While escalated, ALTER SYSTEM fails +ALTER SYSTEM SET wal_level = minimal; +--- ERROR: ALTER SYSTEM blocked by set_user config + +--- While escalated, COPY PROGRAM fails +COPY (select 42) TO PROGRAM 'cat'; +--- ERROR: COPY PROGRAM blocked by set_user config + +--- While escalated, SET log_statement fails +SET log_statement = 'none'; +--- ERROR: "SET log_statement" blocked by set_user config + +--- Restore the original user +SELECT reset_user(); +SELECT SESSION_USER, CURRENT_USER; +--- session_user | current_user +--- --------------+-------------- +--- dba | dba +--- (1 row) + +--- Switch to the ordinary user bob +SELECT set_user('bob'); +SELECT SESSION_USER, CURRENT_USER; +--- session_user | current_user +--- --------------+-------------- +--- dba | bob +--- (1 row) + +--- Restore the original user +SELECT reset_user(); +SELECT SESSION_USER, CURRENT_USER; +--- session_user | current_user +--- --------------+-------------- +--- dba | dba +--- (1 row) +---- + +Inspecting `logfile` shows that every operation performed after escalation was recorded, with the `AUDIT` tag in the log prefix. Each user switch has a corresponding line: `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'); +---- + +=== Cleaning Up the Test Database + +[source,bash] +---- +# Stop the test database +${IVY_BIN_DIR}/pg_ctl -D ${TEST_DB_DIR} -l ${TEST_DB_DIR}/logfile stop + +# Remove the test database +rm -rf ${TEST_DB_DIR} +---- \ No newline at end of file