Hooks in Postgresql
Hooks in Postgresql
Hooks in PostgreSQL
1
Who's Guillaume Lelarge?
French translator of the PostgreSQL manual
●
CTO of Dalibo
●
Mail: [email protected]
●
Twitter: g_lelarge
●
Blog: http://blog.guillaume.lelarge.info
●
2
PostgreSQL
Well known for its extensibility
●
– Types
– Functions
– Operators
– Etc
Less known is the hook system
●
3
Hooks
Interrupt, and modify behaviour
●
4
Most used hooks
Hook Initial release
check_password_hook 9.0
ClientAuthentication_hook 9.1
ExecutorStart_hook 8.4
ExecutorRun_hook 8.4
ExecutorFinish_hook 8.4
ExecutorEnd_hook 8.4
ExecutorCheckPerms_hook 9.1
ProcessUtility_hook 9.0
5
Other hooks
Hook Used in Initial release
explain_get_index_name_hook 8.3
ExplainOneQuery_hook IndexAdvisor 8.3
fmgr_hook sepgsql 9.1
get_attavgwidth_hook 8.4
get_index_stats_hook 8.4
get_relation_info_hook plantuner 8.3
get_relation_stats_hook 8.4
join_search_hook saio 8.3
needs_fmgr_hook sepgsql 9.1
object_access_hook sepgsql 9.1
planner_hook planinstr 8.3
shmem_startup_hook pg_stat_statements 8.4
6
And one plugin
PlpgSQL_plugin
●
7
How do they work inside PG
Hooks consist of global function pointers
●
8
How do we set the function pointer?
A hook function is available in a shared library
●
9
How do we unset the function
pointer?
At unload time, PostgreSQL calls the _PG_fini()
●
10
Example with
ClientAuthentication_hook
●Declaration
– extract from src/include/libpq/auth.h, line 27
11
Example with
ClientAuthentication_hook
●Set
– extract from src/backend/libpq/auth.c, line 215
/*
* but before the user has been informed about the results. It could be used
*/
12
Example with
ClientAuthentication_hook
Check, and execute
●
if (ClientAuthentication_hook)
13
Writing hooks
Details on some hooks
●
– ClientAuthentication
– Executor
– check_password
And various examples
●
14
ClientAuthentication_hook details
Get control
●
15
ClientAuthentication_hook use
Modules using this hook
●
– auth_delay
– sepgsql
– connection_limits
(https://github.com/tvondra/connection_limits)
16
ClientAuthentication_hook function
Two parameters
●
include/libpq/libpq-be.h
– remote_host, remote_hostname, remote_port,
database_name, user_name, guc_options,
etc.
Status is a status code
●
– STATUS_ERROR, STATUS_OK
17
Writing a ClientAuthentication_hook
Example: forbid connection if a file is present
●
18
Writing a ClientAuthentication_hook
●First, initialize the hook
19
Writing a ClientAuthentication_hook
● Check availability of the file, and allow or deny
connection
if (next_client_auth_hook)
(*next_client_auth_hook) (port, status);
if (status != STATUS_OK)
return;
if(!stat("/tmp/connection.stopped", &buf))
ereport(FATAL, (errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Connection not authorized!!")));
}
20
Executor hooks details
Start
●
Already used by
●
– pg_stat_statements
– auto_explain
– pg_log_userqueries
http://pgxn.org/dist/pg_log_userqueries/
– query_histogram
http://pgxn.org/dist/query_histogram/
– query_recorder
http://pgxn.org/dist/query_recorder/
22
Writing an ExecutorEnd_hook
Example: log queries executed by superuser
●
only
Needs three functions
●
23
Writing a ExecutorEnd_hook
●First, install the hook
void _PG_init(void)
{
prev_ExecutorEnd = ExecutorEnd_hook;
ExecutorEnd_hook = pgluq_ExecutorEnd;
}
24
Writing a ExecutorEnd_hook
● The hook itself:
– check if the user has the superuser attribute
– log (or not) the query
– fire the next hook or the default one
static void
pgluq_ExecutorEnd(QueryDesc *queryDesc)
{
Assert(query != NULL);
if (superuser())
elog(log_level, "superuser %s fired this query %s",
GetUserNameFromId(GetUserId()),
query);
if (prev_ExecutorEnd)
prev_ExecutorEnd(queryDesc);
else
standard_ExecutorEnd(queryDesc);
}
25
Writing a ExecutorEnd_hook
●Finally, uninstall the hook
void _PG_fini(void)
{
ExecutorEnd_hook = prev_ExecutorEnd;
}
26
check_password hook details
Get control
●
Already used by
●
– passwordcheck
28
check_password_hook function
Five parameters
●
– PASSWORD_TYPE_PLAINTEXT
– PASSWORD_TYPE_MD5
29
Writing a check_password_hook
Example: disallow plain text passwords
●
30
Writing a check_password_hook
●First, install the hook
void _PG_init(void)
{
check_password_hook = check_password;
}
31
Writing a check_password_hook
●The hook itself:
– check if the password is encrypted
static void
check_password(const char *username,
const char *password, int password_type,
Datum validuntil_time, bool validuntil_null)
{
if (password_type == PASSWORD_TYPE_PLAINTEXT)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("password is not encrypted")));
}
}
32
Compiling hooks
●Usual Makefile
MODULE_big = your_hook
OBJS = your_hook.o
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/your_hook
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
33
Compiling hooks – example
●Make is your friend (and so is pg_config)
$ make USE_PGXS=1
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-
statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv
-fexcess-precision=standard -fpic -I. -I. -I/opt/postgresql-
9.1/include/server -I/opt/postgresql-9.1/include/internal -D_GNU_SOURCE
-c -o your_hook.o your_hook.c
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-
statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv
-fexcess-precision=standard -fpic -shared -o your_hook.so
only_encrypted_passwords.o -L/opt/postgresql-9.1/lib -Wl,--as-needed -Wl,-
rpath,'/opt/postgresql-9.1/lib',--enable-new-dtags
34
Installing hooks – from source
●Make is still your friend
$ make USE_PGXS=1 install
/bin/mkdir -p '/opt/postgresql-9.1/lib'
/bin/sh /opt/postgresql-9.1/lib/pgxs/src/makefiles/../../config/install-sh -c
-m 755 your_hook.so '/opt/postgresql-9.1/lib/your_hook.so'
35
Using hooks
Install the shared library
●
In postgresql.conf
●
– shared_preload_libraries
– And possibly other shared library GUCs
Restart PG
●
36
Using hooks – example
●Install the hook...
●In postgresql.conf
shared_preload_libraries = 'only_encrypted_passwords'
●Restart PostgreSQL
$ pg_ctl start
server starting
2012-01-28 16:01:32 CET LOG: loaded library "only_encrypted_passwords"
37
Using hooks – example
●Use the hook...
postgres=# CREATE USER u1 PASSWORD 'supersecret';
ERROR: password is not encrypted
38
Future hooks?
Logging hook, by Martin Pihlak
●
– https://commitfest.postgresql.org/action/patch_v
iew?id=717
Planner hook, by Peter Geoghegan
●
– parse_analyze() and
parse_analyze_varparams()
– Query normalisation within pg_stat_statements
39
Conclusion
Hooks are an interesting system to extend the
●
capabilities of PostgreSQL
Be cautious to avoid adding many of them
●
– https://github.com/gleu/Hooks-in-PostgreSQL
40
Hooks in PostgreSQL
Hooks in PostgreSQL
● Mail: [email protected]
● Twitter: g_lelarge
● Blog: http://blog.guillaume.lelarge.info
2
PostgreSQL
● Well known for its extensibility
● For example, a user can add
– Types
– Functions
– Operators
– Etc
● Less known is the hook system
10
11
/*
* but before the user has been informed about the results. It could be used
*/
12
if (ClientAuthentication_hook)
13
14
15
16
connection
● connection_limits, written by Tomas Vondra, and
17
18
19
if (next_client_auth_hook)
(*next_client_auth_hook) (port, status);
if (status != STATUS_OK)
return;
if(!stat("/tmp/connection.stopped", &buf))
ereport(FATAL, (errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Connection not authorized!!")));
}
20
23
void _PG_init(void)
{
prev_ExecutorEnd = ExecutorEnd_hook;
ExecutorEnd_hook = pgluq_ExecutorEnd;
}
24
if (superuser())
elog(log_level, "superuser %s fired this query %s",
GetUserNameFromId(GetUserId()),
query);
if (prev_ExecutorEnd)
prev_ExecutorEnd(queryDesc);
else
standard_ExecutorEnd(queryDesc);
}
25
void _PG_fini(void)
{
ExecutorEnd_hook = prev_ExecutorEnd;
}
26
And this last function sets the hook with the previous
ExecutorEnd_hook.
check_password hook details
● Get control
– When CREATE/ALTER USER is executed
– But before commiting
● Usefull to
– Check the password according to some
enterprise rules
– Log change of passwords
– Disallow plain text passwords
● Major issue
– Less effective with encrypted passwords :-/
27
28
29
30
void _PG_init(void)
{
check_password_hook = check_password;
}
31
32
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/your_hook
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
33
34
35
36
● Restart PostgreSQL
$ pg_ctl start
server starting
2012-01-28 16:01:32 CET LOG: loaded library "only_encrypted_passwords"
37
38
39
40