Skip to content

Commit

Permalink
Update README formatting to be more maintainable
Browse files Browse the repository at this point in the history
Use 80-char linewrap and add some missing code tags.
  • Loading branch information
mpalmi committed May 17, 2018
1 parent bb96d79 commit e3adc76
Showing 1 changed file with 109 additions and 62 deletions.
171 changes: 109 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ reset_user(text token) returns text
## Inputs

`rolename` is the role to be transitioned to.
`token` if provided during set_user is saved, and then required to be provided again for reset.
`token` if provided during set_user is saved, and then required to be provided
again for reset.

## Configuration Options

* Add `set_user` to `shared_preload_libraries` in postgresql.conf.
* Optionally, the following custom parameters may be set to control their respective commands:

* Optionally, the following custom parameters may be set to control their
respective commands:
* 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")
Expand All @@ -27,7 +30,8 @@ reset_user(text token) returns text
* list of user roles (i.e. `<role1>, <role2>,...,<roleN>`)
* Group roles may be indicated by `+<roleN>`
* The wildcard character `*`
* To make use of the optional `set_user` and `reset_user` hooks, please refer to the [hooks](#post-execution-hooks) section.
* To make use of the optional `set_user` and `reset_user` hooks, please refer to
the [hooks](#post-execution-hooks) section.

## Description

Expand All @@ -39,49 +43,52 @@ Specifically, when an allowed user executes `set_user('rolename')` or
`set_user_u('rolename')`, several actions occur:

* The current effective user becomes `rolename`.
* The role transition is logged, with a specific notation if `rolename` is a superuser.
* The role transition is logged, with a specific notation if `rolename` is a
superuser.
* `log_statement` setting is set to "all", meaning every SQL statement executed
while in this state will also get logged.
* 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_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_log_statement` is set to "on" and `rolename` is a
database superuser, the current `log_statement` setting is changed to "all",
meaning every SQL statement executed
* If `set_user.block_log_statement` is set to "on" and `rolename` is a database
superuser, the current `log_statement` setting is changed to "all", meaning
every SQL statement executed
* If `set_user.superuser_audit_tag` is set, the string value will be appended to
`log_line_prefix` upon superuser escalation. All logs after superuser escalation
will be tagged with the value of `set_user.superuser_audit_tag`. This value
defaults to `'AUDIT'`.
`log_line_prefix` upon superuser escalation. All logs after superuser
escalation will be tagged with the value of `set_user.superuser_audit_tag`.
This value defaults to `'AUDIT'`.
* [Post-execution hook](#post_set_user_hook) for `set_user` is called if it is set.

Only users with `EXECUTE` permission on `set_user_u('rolename')` may escalate
to superuser. Additionally, only roles explicitly listed or included by a group
that is explicitly listed (e.g. `'+admin'`) in `set_user.superuser_whitelist` can
escalate to superuser. If `set_user.superuser_whitelist` is explicitly set to the
empty set, `''`, superuser escalation is blocked for all users. If the whitelist
is equal to the wildcard character, `'*'`, all users with `EXECUTE` permission on
`set_user_u()` can escalate to superuser. The default value of
Only users with `EXECUTE` permission on `set_user_u('rolename')` may escalate to
superuser. Additionally, only roles explicitly listed or included by a group
that is explicitly listed (e.g. `'+admin'`) in `set_user.superuser_whitelist`
can escalate to superuser. If `set_user.superuser_whitelist` is explicitly set
to the empty set, `''`, superuser escalation is blocked for all users. If the
whitelist is equal to the wildcard character, `'*'`, all users with `EXECUTE`
permission on `set_user_u()` can escalate to superuser. The default value of
`set_user.superuser_whitelist` is `'*'`.

Additionally, with `set_user('rolename','token')` the `token` is stored
for the lifetime of the session.
Additionally, with `set_user('rolename','token')` the `token` is stored for the
lifetime of the session.

When finished with required actions as `rolename`, the `reset_user()`
function is executed to restore the original user. At that point, these actions
occur:
When finished with required actions as `rolename`, the `reset_user()` function
is executed to restore the original user. At that point, these actions occur:

* Role transition is logged.
* `log_statement` setting is set to its original value.
* Blocked command behaviors return to normal.
* [Post-execution hook](#post_reset_user_hook) for `reset_user` is called if it is set.
* [Post-execution hook](#post_reset_user_hook) for `reset_user` is called if it
is set.

If `set_user`, was provided with a `token`, then
`reset_user('token')` must be called instead of `reset_user()`:
If `set_user`, was provided with a `token`, then `reset_user('token')` must be
called instead of `reset_user()`:

* The provided `token` is compared with the stored token.
* If the tokens do not match, or if a `token` was provided to `set_user`
but not `reset_user`, an ERROR occurs.
* If the tokens do not match, or if a `token` was provided to `set_user` but not
`reset_user`, an ERROR occurs.

### `set_user` Usage

Expand All @@ -106,7 +113,7 @@ to `set_user`.
2) `dbclient2` is an unprivileged user that can run as `dbclient` through calls
to `set_user`.
3) `dbadmin` is the privileged (non-superuser) role, which is able to escalate
privileges to superuser with Enhanced Logging.
privileges to superuser with Enhanced Logging.

#### Call `set_user` to Transition

Expand All @@ -120,7 +127,8 @@ optional `token`, as described above).
SELECT set_user('dbclient2');
```

Alternatively, transitions can be made to superusers through use of `set_user_u`:
Alternatively, transitions can be made to superusers through use of
`set_user_u`:

```sql
SELECT set_user_u('postgres');
Expand All @@ -131,10 +139,10 @@ SELECT set_user_u('postgres');
that have been granted `EXECUTE` on `set_user_u` can escalate to superuser.
This is the default setting of `set_user.superuser_whitelist`.

Once one or more unprivileged users are able to run `set_user_u()` in order
to escalate their privileges, the superuser account (typically `postgres`)
can be altered to `NOLOGIN`, preventing any direct database connection by a
superuser which would bypass the enhanced logging.
Once one or more unprivileged users are able to run `set_user_u()` in order to
escalate their privileges, the superuser account (typically `postgres`) can be
altered to `NOLOGIN`, preventing any direct database connection by a superuser
which would bypass the enhanced logging.

Naturally for this to work as expected, the PostgreSQL cluster must be audited
to ensure there are no other PostgreSQL roles existing which are both superuser
Expand Down Expand Up @@ -172,20 +180,29 @@ properly, you must include `set_user` in `shared_preload_libraries` in
`postgresql.conf` and restart PostgreSQL.



Notes:

If set_user.block_log_statement is set to "off", the `log_statement` setting is left unchanged.
If set_user.block_log_statement is set to "off", the `log_statement` setting is
left unchanged.

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.
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.

Neither `set_user('rolename')` nor `set_user_u('rolename')` may be executed from within an explicit transaction block.
Neither `set_user('rolename')` nor `set_user_u('rolename')` may be executed from
within an explicit transaction block.

## Caveats

In its current state, this extension cannot prevent `rolename` from performing a variety of nefarious or otherwise undesireable actions. However, these actions will be logged providing an audit trail, which could also be used to trigger alerts.
In its current state, this extension cannot prevent `rolename` from performing a
variety of nefarious or otherwise undesireable actions. However, these actions
will be logged providing an audit trail, which could also be used to trigger
alerts.

Although this extension compiles and works with all supported versions of PostgreSQL starting with PostgreSQL 9.1, all features are not supported until PostgreSQL 9.4 or higher. The ALTER SYSTEM command does not exist prior to 9.4 and COPY PROGRAM does not exist prior to 9.3.
Although this extension compiles and works with all supported versions of
PostgreSQL starting with PostgreSQL 9.1, all features are not supported until
PostgreSQL 9.4 or higher. The ALTER SYSTEM command does not exist prior to 9.4
and COPY PROGRAM does not exist prior to 9.3.

## TODO

Expand All @@ -195,38 +212,58 @@ The following changes/enhancements are contemplated:

## Post-Execution Hooks

`set_user` exposes two hooks that may be used to control post-execution behavior for `set_user` and `reset_user`.
`set_user` exposes two hooks that may be used to control post-execution behavior
for `set_user` and `reset_user`.

### Description

The following hooks are called (if set) directly before returning from successful calls to `set_user` and `reset_user`. These hooks are meant to give other extensions awareness of `set_user` actions. This is helpful, for instance, to keep track of dynamic user switching within a session.
The following hooks are called (if set) directly before returning from
successful calls to `set_user` and `reset_user`. These hooks are meant to give
other extensions awareness of `set_user` actions. This is helpful, for instance,
to keep track of dynamic user switching within a session.

###### `post_set_user_hook`

Allows another extension to take action after calls to `set_user`. This hook takes the username as an argument so that the hook implementation is aware of the username.
Allows another extension to take action after calls to `set_user`. This hook
takes the username as an argument so that the hook implementation is aware of
the username.

###### `post_reset_user_hook`

Allows another extension to take action after calls to `reset_user`. This hook does not take any arguments, since the resulting username will always be the `session_user`.
Allows another extension to take action after calls to `reset_user`. This hook
does not take any arguments, since the resulting username will always be the
`session_user`.

### Configuration

Follow the instructions below to implement `set_user` and `reset_user` post-execution hooks in another extension:
Follow the instructions below to implement `set_user` and `reset_user`
post-execution hooks in another extension:

* Add '-I$(includedir)' to `CPPFLAGS` of the extension which implements the post-execution hooks.
* Add '-I$(includedir)' to `CPPFLAGS` of the extension which implements the
post-execution hooks.
* `#include set_user.h` in whichever file implements the hooks.
* Register `post_set_user_hook` and `post_reset_user_hook` with local implementations in the extension which implements the post-execution hooks.
* Ensure that `set_user` is listed before any implementing extension in `shared_preload_libraries` so postgres loads the hooks into memory before the implementation is loaded.
* Register `post_set_user_hook` and `post_reset_user_hook` with local
implementations in the extension which implements the post-execution hooks.
* Ensure that `set_user` is listed before any implementing extension in
`shared_preload_libraries` so postgres loads the hooks into memory before the
implementation is loaded.

Configuration is described in more detail in the [post-execution hooks](#install-set_user-post-execution-hooks) subsection of the Install documentation.
Configuration is described in more detail in the [post-execution
hooks](#install-set_user-post-execution-hooks) subsection of the Install
documentation.

### Caveats

If another extension implements the post-execution hooks, `post_set_user_hook` and `post_reset_user_hook`, `set_user` must be listed before that extension in `shared_preload_libraries`. This is due to the way `shared_preload_libraries` are opened and loaded into memory by postgres: the hooks need to be loaded into memory before their implementations can access them.
If another extension implements the post-execution hooks, `post_set_user_hook`
and `post_reset_user_hook`, `set_user` must be listed before that extension in
`shared_preload_libraries`. This is due to the way `shared_preload_libraries`
are opened and loaded into memory by postgres: the hooks need to be loaded into
memory before their implementations can access them.

### TODO

* Add ability to create dependencies in `shared_preload_libraries` such that extension order does not matter.
* Add ability to create dependencies in `shared_preload_libraries` such that
extension order does not matter.

## Installation

Expand Down Expand Up @@ -287,7 +324,9 @@ $> make install

#### Using PGXS

If an instance of PostgreSQL is already installed, then PGXS can be utilized to build and install `set_user`. Ensure that PostgreSQL binaries are available via the `$PATH` environment variable then use the following commands.
If an instance of PostgreSQL is already installed, then PGXS can be utilized to
build and install `set_user`. Ensure that PostgreSQL binaries are available via
the `$PATH` environment variable then use the following commands.

```bash
$> make USE_PGXS=1
Expand All @@ -296,7 +335,9 @@ $> make USE_PGXS=1 install

### Configure

The following bash commands should configure your system to utilize set_user. Replace all paths as appropriate. It may be prudent to visually inspect the files afterward to ensure the changes took place.
The following bash commands should configure your system to utilize `set_user`.
Replace all paths as appropriate. It may be prudent to visually inspect the
files afterward to ensure the changes took place.

###### Initialize PostgreSQL (if needed):

Expand All @@ -312,7 +353,8 @@ $> createdb <database>

###### Install `set_user` functions:

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

First edit postgresql.conf in your favorite editor:

Expand Down Expand Up @@ -355,20 +397,23 @@ This can be done automatically upon normal installation:
$> make USE_PGXS=1 install
```

There is also an explicit make target available to copy the header file to the appropriate directory.
There is also an explicit make target available to copy the header file to the
appropriate directory.

```bash
$> make USE_PGXS=1 install-headers
```

Ensure that the implementing extension adds `-I$(includedir)` to `CPPFLAGS` in its Makefile:
Ensure that the implementing extension adds `-I$(includedir)` to `CPPFLAGS` in
its Makefile:

```
# Add -I$(includedir) to CPPFLAGS so the set_user header is included
override CPPFLAGS += -I$(includedir)
```

Ensure that the implementing extension includes the `set_user` header file in the appropriate C file:
Ensure that the implementing extension includes the `set_user` header file in
the appropriate C file:

```
/* Include set_user hooks in whichever C file implements the hooks */
Expand All @@ -380,19 +425,21 @@ post_set_user_hook = my_post_set_user_implementation;
post_reset_user_hook_type = my_post_reset_user_implementation;
```

Edit postgresql.conf and ensure that `set_user` is listed before the implementing extension `<extension_name>` in `shared_preload_libraries`:
Edit postgresql.conf and ensure that `set_user` is listed before the
implementing extension `<extension_name>` in `shared_preload_libraries`:

```
# Make sure set_user is listed before implementing extension
shared_preload_libraries = 'set_user, <extension_name>'
```

## GUC Parameters

* Block ALTER SYSTEM commands
* Block `ALTER SYSTEM` commands
* `set_user.block_alter_system = on`
* Block COPY PROGRAM commands
* Block `COPY PROGRAM` commands
* `set_user.block_copy_program = on`
* Block SET log_statement commands
* Block `SET log_statement` commands
* `set_user.block_log_statement = on`
* Allow list of roles to escalate to superuser
* `set_user.superuser_whitelist = '<role1>,<role2>,...,<roleN>'`
Expand Down

0 comments on commit e3adc76

Please sign in to comment.