当前位置:   article > 正文

PostgreSQL 事件触发器应用 - DDL审计

postgresql 事件触发器

标签

PostgreSQL , 事件触发器 , DDL审计 , 表结构变更 , 建表等审计


背景

DDL语句的审计是非常重要的,目前PG的DDL审计记录在日志文件中。不便于查看。

为了让DDL事件记录到表中,方便查看,我们可以通过事件触发器来达到这个效果。

事件触发器审计DDL操作

事件触发器语法:

  1. Command: CREATE EVENT TRIGGER
  2. Description: define a new event trigger
  3. Syntax:
  4. CREATE EVENT TRIGGER name
  5. ON event
  6. [ WHEN filter_variable IN (filter_value [, ... ]) [ AND ... ] ]
  7. EXECUTE PROCEDURE function_name()

1、事件触发器的触发点(event)

目前支持4个触发点(event)

ddl_command_start, ddl_command_end, table_rewrite and sql_drop  

这四个触发点,有3个能捕获到事件发生时的信息。

1.1 ddl_command_end

通过这个函数进行捕获:pg_event_trigger_ddl_commands()

NameTypeDescription
classidOidOID of catalog the object belongs in
objidOidOID of the object in the catalog
objsubidintegerObject sub-id (e.g. attribute number for columns)
command_tagtextcommand tag
object_typetextType of the object
schema_nametextName of the schema the object belongs in, if any; otherwise NULL. No quoting is applied.
object_identitytextText rendering of the object identity, schema-qualified. Each and every identifier present in the identity is quoted if necessary.
in_extensionboolwhether the command is part of an extension script
commandpg_ddl_commandA complete representation of the command, in internal format. This cannot be output directly, but it can be passed to other functions to obtain different pieces of information about the command.

1.2 sql_drop

通过这个函数进行捕获:pg_event_trigger_dropped_objects()

NameTypeDescription
classidOidOID of catalog the object belonged in
objidOidOID the object had within the catalog
objsubidint32Object sub-id (e.g. attribute number for columns)
originalboolFlag used to identify the root object(s) of the deletion
normalboolFlag indicating that there's a normal dependency relationship in the dependency graph leading to this object
is_temporaryboolFlag indicating that the object was a temporary object.
object_typetextType of the object
schema_nametextName of the schema the object belonged in, if any; otherwise NULL. No quoting is applied.
object_nametextName of the object, if the combination of schema and name can be used as a unique identifier for the object; otherwise NULL. No quoting is applied, and name is never schema-qualified.
object_identitytextText rendering of the object identity, schema-qualified. Each and every identifier present in the identity is quoted if necessary.
address_namestext[]An array that, together with object_type and address_args, can be used by the pg_get_object_address() to recreate the object address in a remote server containing an identically named object of the same kind.
address_argstext[]Complement for address_names above.

1.3 table_rewrite

通过如下函数进行捕获:

NameReturn TypeDescription
pg_event_trigger_table_rewrite_oid()OidThe OID of the table about to be rewritten.
pg_event_trigger_table_rewrite_reason()intThe reason code(s) explaining the reason for rewriting. The exact meaning of the codes is release dependent.

2、创建三个触发点的捕获信息存储表

  1. create schema pgaudit;
  2. grant USAGE on schema pgaudit to public;
  3. create table pgaudit.audit_ddl_command_end (
  4. event text,
  5. tag text,
  6. username name default current_user,
  7. datname name default current_database(),
  8. client_addr inet default inet_client_addr(),
  9. client_port int default inet_client_port(),
  10. crt_time timestamp default now(),
  11. classid oid,
  12. objid oid,
  13. objsubid int,
  14. command_tag text,
  15. object_type text,
  16. schema_name text,
  17. object_identity text,
  18. is_extension bool,
  19. xid bigint default txid_current()
  20. );
  21. create table pgaudit.audit_sql_drop (
  22. event text,
  23. tag text,
  24. username name default current_user,
  25. datname name default current_database(),
  26. client_addr inet default inet_client_addr(),
  27. client_port int default inet_client_port(),
  28. crt_time timestamp default now(),
  29. classid oid,
  30. objid oid,
  31. objsubid int,
  32. original bool,
  33. normal bool,
  34. is_temporary bool,
  35. object_type text,
  36. schema_name text,
  37. object_name text,
  38. object_identity text,
  39. address_names text[],
  40. address_args text[],
  41. xid bigint default txid_current()
  42. );
  43. create table pgaudit.audit_table_rewrite (
  44. event text,
  45. tag text,
  46. username name default current_user,
  47. datname name default current_database(),
  48. client_addr inet default inet_client_addr(),
  49. client_port int default inet_client_port(),
  50. crt_time timestamp default now(),
  51. table_rewrite_oid oid,
  52. table_rewrite_reason int,
  53. xid bigint default txid_current()
  54. );
  55. grant select,update,delete,insert,truncate on pgaudit.audit_ddl_command_end to public;
  56. grant select,update,delete,insert,truncate on pgaudit.audit_sql_drop to public;
  57. grant select,update,delete,insert,truncate on pgaudit.audit_table_rewrite to public;

3、创建三个触发点的事件触发器函数

  1. create or replace function pgaudit.et_ddl_command_end() returns event_trigger as $$
  2. declare
  3. begin
  4. insert into pgaudit.audit_ddl_command_end (event, tag, classid, objid, objsubid, command_tag, object_type, schema_name, object_identity, is_extension )
  5. select TG_EVENT, TG_TAG,
  6. classid, objid, objsubid, command_tag, object_type, schema_name, object_identity, in_extension from
  7. pg_event_trigger_ddl_commands();
  8. -- exception when others then
  9. -- return;
  10. end;
  11. $$ language plpgsql strict;
  1. create or replace function pgaudit.et_sql_drop() returns event_trigger as $$
  2. declare
  3. begin
  4. insert into pgaudit.audit_sql_drop (event, tag, classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args)
  5. select TG_EVENT, TG_TAG,
  6. classid, objid, objsubid, original, normal, is_temporary, object_type, schema_name, object_name, object_identity, address_names, address_args from
  7. pg_event_trigger_dropped_objects();
  8. -- exception when others then
  9. -- return;
  10. end;
  11. $$ language plpgsql strict;
  1. create or replace function pgaudit.et_table_rewrite() returns event_trigger as $$
  2. declare
  3. begin
  4. insert into pgaudit.audit_table_rewrite (event, tag, table_rewrite_oid, table_rewrite_reason)
  5. select TG_EVENT, TG_TAG,
  6. pg_event_trigger_table_rewrite_oid(),
  7. pg_event_trigger_table_rewrite_reason();
  8. -- exception when others then
  9. -- return;
  10. end;
  11. $$ language plpgsql strict;

4、创建三个触发点的事件触发器

  1. CREATE EVENT TRIGGER et_ddl_command_end on ddl_command_end EXECUTE PROCEDURE pgaudit.et_ddl_command_end();
  2. CREATE EVENT TRIGGER et_sql_drop on sql_drop EXECUTE PROCEDURE pgaudit.et_sql_drop();
  3. CREATE EVENT TRIGGER et_table_rewrite on table_rewrite EXECUTE PROCEDURE pgaudit.et_table_rewrite();

5、模板化

在模板库,执行第二到第四步。

  1. \c template1 postgres
  2. -- 在模板库,执行第二到第四步。

6、通过模板创建的数据库,会自动继承这个模板。

  1. postgres=# create database db1 template template1;
  2. CREATE DATABASE

7、例子

7.1、建表

  1. postgres=# \c db1 test
  2. You are now connected to database "db1" as user "test".
  3. db1=> create table tbl(id int);
  4. CREATE TABLE

7.2、写入数据

  1. db1=> insert into tbl select generate_series(1,100);
  2. INSERT 0 100

7.3、重写表

  1. db1=> alter table tbl add column info text default 'abc';
  2. ALTER TABLE

7.4、删表

  1. db1=> drop table tbl;
  2. DROP TABLE

8、查询审计信息

  1. db1=> select * from pgaudit.audit_ddl_command_end ;
  2. -[ RECORD 1 ]---+---------------------------
  3. event | ddl_command_end
  4. tag | CREATE TABLE
  5. username | test
  6. datname | db1
  7. client_addr |
  8. client_port |
  9. crt_time | 2017-09-25 16:05:39.459787
  10. classid | 1259
  11. objid | 33212
  12. objsubid | 0
  13. command_tag | CREATE TABLE
  14. object_type | table
  15. schema_name | public
  16. object_identity | public.tbl
  17. is_extension | f
  18. -[ RECORD 2 ]---+---------------------------
  19. event | ddl_command_end
  20. tag | ALTER TABLE
  21. username | test
  22. datname | db1
  23. client_addr |
  24. client_port |
  25. crt_time | 2017-09-25 16:05:59.781995
  26. classid | 1259
  27. objid | 33212
  28. objsubid | 0
  29. command_tag | ALTER TABLE
  30. object_type | table
  31. schema_name | public
  32. object_identity | public.tbl
  33. is_extension | f
  1. db1=> select * from pgaudit.audit_sql_drop ;
  2. -[ RECORD 1 ]---+--------------------------------
  3. event | sql_drop
  4. tag | DROP TABLE
  5. username | test
  6. datname | db1
  7. client_addr |
  8. client_port |
  9. crt_time | 2017-09-25 16:06:08.22198
  10. classid | 1259
  11. objid | 33212
  12. objsubid | 0
  13. original | t
  14. normal | f
  15. is_temporary | f
  16. object_type | table
  17. schema_name | public
  18. object_name | tbl
  19. object_identity | public.tbl
  20. address_names | {public,tbl}
  21. address_args | {}
  22. -[ RECORD 2 ]---+--------------------------------
  23. event | sql_drop
  24. tag | DROP TABLE
  25. username | test
  26. datname | db1
  27. client_addr |
  28. client_port |
  29. crt_time | 2017-09-25 16:06:08.22198
  30. classid | 2604
  31. objid | 33215
  32. objsubid | 0
  33. original | f
  34. normal | f
  35. is_temporary | f
  36. object_type | default value
  37. schema_name |
  38. object_name |
  39. object_identity | for public.tbl.info
  40. address_names | {public,tbl,info}
  41. address_args | {}
  42. -[ RECORD 3 ]---+--------------------------------
  43. event | sql_drop
  44. tag | DROP TABLE
  45. username | test
  46. datname | db1
  47. client_addr |
  48. client_port |
  49. crt_time | 2017-09-25 16:06:08.22198
  50. classid | 1247
  51. objid | 33214
  52. objsubid | 0
  53. original | f
  54. normal | f
  55. is_temporary | f
  56. object_type | type
  57. schema_name | public
  58. object_name | tbl
  59. object_identity | public.tbl
  60. address_names | {public.tbl}
  61. address_args | {}
  62. -[ RECORD 4 ]---+--------------------------------
  63. event | sql_drop
  64. tag | DROP TABLE
  65. username | test
  66. datname | db1
  67. client_addr |
  68. client_port |
  69. crt_time | 2017-09-25 16:06:08.22198
  70. classid | 1247
  71. objid | 33213
  72. objsubid | 0
  73. original | f
  74. normal | f
  75. is_temporary | f
  76. object_type | type
  77. schema_name | public
  78. object_name | _tbl
  79. object_identity | public.tbl[]
  80. address_names | {public.tbl[]}
  81. address_args | {}
  82. -[ RECORD 5 ]---+--------------------------------
  83. event | sql_drop
  84. tag | DROP TABLE
  85. username | test
  86. datname | db1
  87. client_addr |
  88. client_port |
  89. crt_time | 2017-09-25 16:06:08.22198
  90. classid | 1259
  91. objid | 33222
  92. objsubid | 0
  93. original | f
  94. normal | f
  95. is_temporary | f
  96. object_type | toast table
  97. schema_name | pg_toast
  98. object_name | pg_toast_33212
  99. object_identity | pg_toast.pg_toast_33212
  100. address_names | {pg_toast,pg_toast_33212}
  101. address_args | {}
  102. -[ RECORD 6 ]---+--------------------------------
  103. event | sql_drop
  104. tag | DROP TABLE
  105. username | test
  106. datname | db1
  107. client_addr |
  108. client_port |
  109. crt_time | 2017-09-25 16:06:08.22198
  110. classid | 1259
  111. objid | 33224
  112. objsubid | 0
  113. original | f
  114. normal | f
  115. is_temporary | f
  116. object_type | index
  117. schema_name | pg_toast
  118. object_name | pg_toast_33212_index
  119. object_identity | pg_toast.pg_toast_33212_index
  120. address_names | {pg_toast,pg_toast_33212_index}
  121. address_args | {}
  122. -[ RECORD 7 ]---+--------------------------------
  123. event | sql_drop
  124. tag | DROP TABLE
  125. username | test
  126. datname | db1
  127. client_addr |
  128. client_port |
  129. crt_time | 2017-09-25 16:06:08.22198
  130. classid | 1247
  131. objid | 33223
  132. objsubid | 0
  133. original | f
  134. normal | f
  135. is_temporary | f
  136. object_type | type
  137. schema_name | pg_toast
  138. object_name | pg_toast_33212
  139. object_identity | pg_toast.pg_toast_33212
  140. address_names | {pg_toast.pg_toast_33212}
  141. address_args | {}
  1. db1=> select * from pgaudit.audit_table_rewrite ;
  2. event | tag | username | datname | client_addr | client_port | crt_time | table_rewrite_oid | table_rewrite_reason
  3. ---------------+-------------+----------+---------+-------------+-------------+----------------------------+-------------------+----------------------
  4. table_rewrite | ALTER TABLE | test | db1 | | | 2017-09-25 16:05:59.781995 | 33212 | 2
  5. (1 row)

PG 9.4的例子

  1. create schema pgaudit;
  2. grant USAGE on schema pgaudit to public;
  3. create extension hstore SCHEMA pgaudit;
  4. create table pgaudit.audit_ddl_command_end (
  5. event text,
  6. tag text,
  7. username name default current_user,
  8. datname name default current_database(),
  9. client_addr inet default inet_client_addr(),
  10. client_port int default inet_client_port(),
  11. crt_time timestamp default now(),
  12. ctx pgaudit.hstore,
  13. xid bigint default txid_current()
  14. );
  15. create table pgaudit.audit_sql_drop (
  16. event text,
  17. tag text,
  18. username name default current_user,
  19. datname name default current_database(),
  20. client_addr inet default inet_client_addr(),
  21. client_port int default inet_client_port(),
  22. crt_time timestamp default now(),
  23. classid oid,
  24. objid oid,
  25. objsubid int,
  26. object_type text,
  27. schema_name text,
  28. object_name text,
  29. object_identity text,
  30. xid bigint default txid_current()
  31. );
  32. grant select,update,delete,insert,truncate on pgaudit.audit_ddl_command_end to public;
  33. grant select,update,delete,insert,truncate on pgaudit.audit_sql_drop to public;
  34. create or replace function pgaudit.ef_ddl_command_end() returns event_trigger as $$
  35. declare
  36. rec pgaudit.hstore;
  37. begin
  38. select pgaudit.hstore(pg_stat_activity.*) into rec from pg_stat_activity where pid=pg_backend_pid();
  39. insert into pgaudit.audit_ddl_command_end (event, tag, ctx) values (TG_EVENT, TG_TAG, rec);
  40. end;
  41. $$ language plpgsql strict;
  42. create or replace function pgaudit.ef_sql_drop() returns event_trigger as $$
  43. declare
  44. begin
  45. insert into pgaudit.audit_sql_drop (event, tag, classid, objid, objsubid, object_type, schema_name, object_name, object_identity)
  46. select TG_EVENT, TG_TAG, classid, objid, objsubid, object_type, schema_name, object_name, object_identity from
  47. pg_event_trigger_dropped_objects();
  48. -- exception when others then
  49. -- return;
  50. end;
  51. $$ language plpgsql strict;
  52. create event trigger ef_ddl_command_end on ddl_command_end execute procedure pgaudit.ef_ddl_command_end();
  53. create event trigger ef_sql_drop on sql_drop execute procedure pgaudit.ef_sql_drop();

小结

1、本文以PG 10为例,介绍了通过事件触发器,审计DDL的功能。(其他版本可能需要略微修改。)

2、事件触发器的其他应用,例如限制用户执行某些DDL等。

《PostgreSQL Oracle 兼容性之 - 事件触发器实现类似Oracle的回收站功能》

《PostgreSQL 事件触发器 - DDL审计 , DDL逻辑复制 , 打造DDL统一管理入》

《PostgreSQL 事件触发器 - PostgreSQL 9.3 Event Trigger》

参考

https://www.postgresql.org/docs/9.6/static/functions-event-triggers.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/773729
推荐阅读
相关标签
  

闽ICP备14008679号