Skip to content

Commit

Permalink
Add new custom GUC set_user.block_superuser
Browse files Browse the repository at this point in the history
Add new custom GUC set_user.block_superuser defaulting to off. When
on, will prevent switching to a role which has superuser privs.
  • Loading branch information
jconway committed Nov 7, 2016
1 parent 9281dcc commit f35e1e1
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 9 deletions.
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ reset_user() returns text
* set_user.block_alter_system = off (defaults to "on")
* set_user.block_copy_program = off (defaults to "on")
* set_user.block_log_statement = off (defaults to "on")
* set_user.block_superuser = on (defaults to "off")

## Description

This PostgreSQL extension allows privilege escalation with enhanced logging and
control. It provides an additional layer of logging and control when unprivileged
users must escalate themselves to superuser or object owner roles in order to
perform needed maintenance tasks. Specifically, when an allowed user executes
This PostgreSQL extension allows switching users and optionally privilege
escalation with enhanced logging and control. It provides an additional layer
of logging and control when unprivileged users must escalate themselves to
superuser or object owner roles in order to perform needed maintenance tasks.
Specifically, when an allowed user executes
```set_user('rolename')```, several actions occur:

* The current effective user becomes ```rolename```.
Expand All @@ -33,6 +35,7 @@ perform needed maintenance tasks. Specifically, when an allowed user executes
* If set_user.block_alter_system is set to "on", ```ALTER SYSTEM``` commands will be blocked.
* If set_user.block_copy_program is set to "on", ```COPY PROGRAM``` commands will be blocked.
* If set_user.block_log_statement is set to "on", ```SET log_statement``` and variations will be blocked.
* If set_user.block_superuser is set to "on", transition to a superuser role will be blocked.

When finished with required actions as ```rolename```, the ```reset_user()``` function
is executed to restore the original user. At that point, these actions occur:
Expand All @@ -43,9 +46,10 @@ is executed to restore the original user. At that point, these actions occur:

The concept is to grant the EXECUTE privilege to the ```set_user()```
function to otherwise unprivileged postgres users who can then
escalate themselves when needed to perform specific superuser actions.
The enhanced logging ensures an audit trail of what actions are taken
while privileges are escalated.
transition to other roles, possibly escalating themselves to superuser,
when needed to perform specific actions. The enhanced logging ensures
an audit trail of what actions are taken while privileges are altered
to those of the alternate role.

Once one or more unprivileged users are able to run ```set_user()```,
the superuser account (normally ```postgres```) can be altered to NOLOGIN,
Expand All @@ -62,6 +66,9 @@ Note that for the blocking of ```ALTER SYSTEM``` and ```COPY PROGRAM```
to work properly, you must include ```set_user``` in shared_preload_libraries
in postgresql.conf and restart PostgreSQL.

Also note that ```set_user('rolename')``` may not be executed from within
an explicit transaction block.

## Caveats

In its current state, this extension cannot prevent ```rolename``` from
Expand Down Expand Up @@ -170,7 +177,7 @@ $> createdb <database>
###### Install ```set_user``` functions:

Edit postgresql.conf and add ```set_user``` to the shared_preload_libraries
line, optionally also setting block_alter_system and/or block_copy_program.
line, optionally also changing custom settings as mentioned above.

First edit postgresql.conf in your favorite editor:

Expand All @@ -182,11 +189,12 @@ Then add these lines to the end of the file:
```
# Add set_user to any existing list
shared_preload_libraries = 'set_user'
# The following lines are only required to turn off the
# The following lines are only required to modify the
# blocking of each respective command if desired
set_user.block_alter_system = off #defaults to "on"
set_user.block_copy_program = off #defaults to "on"
set_user.block_log_statement = off #defaults to "on"
set_user.block_superuser = on #defaults to "off"
```

Finally, restart PostgreSQL (method may vary):
Expand All @@ -211,6 +219,9 @@ CREATE EXTENSION set_user;
* set_user.block_copy_program = on
* Block SET log_statement commands
* set_user.block_log_statement = on
* Block switching to a superuser role
* set_user.block_superuser = on


## Examples

Expand Down
11 changes: 11 additions & 0 deletions set_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ static bool Block_CP = false;
#endif

static bool Block_LS = false;
static bool Block_SU = false;

#ifdef HAS_TWO_ARG_GETUSERNAMEFROMID
/* 9.5 & master */
Expand Down Expand Up @@ -172,6 +173,11 @@ set_user(PG_FUNCTION_ARGS)
NewUser_is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
ReleaseSysCache(roleTup);

if (NewUser_is_superuser && Block_SU)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("Switching to superuser blocked by set_user config")));

/* keep track of original userid and value of log_statement */
save_OldUserId = OldUserId;
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
Expand Down Expand Up @@ -241,6 +247,11 @@ _PG_init(void)
NULL, &Block_LS, true, PGC_SIGHUP,
0, NULL, NULL, NULL);

DefineCustomBoolVariable("set_user.block_superuser",
"Blocks switching to superuser",
NULL, &Block_SU, false, PGC_SIGHUP,
0, NULL, NULL, NULL);

/* Install hook */
prev_hook = ProcessUtility_hook;
ProcessUtility_hook = PU_hook;
Expand Down

0 comments on commit f35e1e1

Please sign in to comment.