It might be that there is a requirement to audit operations in the database. Maybe because of legal requirements, maybe because of security requirements or whatever. I’ve already written a post in the past describing what you can do in community PostgreSQL, this post is specific to EDB Postgres. The auditting features come be default in EDB Postgres and you do not need to install any extension such as pgaudit.
I am using EDB Postgres Enterprise version 11.2 for this post but it should work the same in previous versions:
enterprisedb@edb1:/var/lib/edb/ [pg1] psql -X postgres psql.bin (11.2.9) Type "help" for help. postgres=# select version(); version ----------------------------------------------------------------------------------------------------------------------------------------------- PostgreSQL 11.2 (EnterpriseDB Advanced Server 11.2.9) on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit (1 row)
The parameter which controls if auditing is enabled or not is “edb_audit”:
postgres=# show edb_audit; edb_audit ----------- (1 row) postgres=#
When it is not set (the default) auditing is enabled. You have two options to enable it:
- csv: Enabled autiting and will write the audit records to a csv file
- xml: Enabled autiting and will write the audit records to a csv file
Before enabling auditing you should think about where you want to store the audit files. It should be a location only the operating system user which runs EDB Postgres should have access to. You might think of $PGDATA but do you really want to have all the audit files included in every base backup you will be doing in the future? A better location is outside $PGDATA so you can keep the audit files separated. Lets go with “/var/lib/edb/audit” for the scope of this post:
postgres=# \! mkdir /var/lib/edb/audit postgres=# \! chmod 700 /var/lib/edb/audit postgres=# alter system set edb_audit_directory = '/var/lib/edb/audit'; ALTER SYSTEM postgres=# alter system set edb_audit='csv'; ALTER SYSTEM postgres=# select name,context from pg_settings where name in ('edb_audit_directory','edb_audit'); name | context ---------------------+--------- edb_audit | sighup edb_audit_directory | sighup (2 rows)
Both parameter changes can be made active by reloading the server, a restart is not required:
postgres=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row)
The default file name that will be used for the audit logs is:
postgres=# show edb_audit_filename; edb_audit_filename --------------------- audit-%Y%m%d_%H%M%S (1 row)
Lets keep that as it is which is sufficient for the scope of this post. Now you need to think about what you want to audit. There are several options available:
- edb_audit_connect: Logs all connections to the instance, either successful, failed or all
- edb_audit_disconnect: The opposite of edb_audit_connect, logs all disconnections/li>
- edb_audit_statement: Here you have several options to log SQL statements such as insert,truncate, whatever, more on that later
- edb_audit_tag: When set, adds a string value to all audit log files
We start with logging connections and disconnections. When we set edb_audit_connect to all, we should see all connections to a database, no matter if successful or failed:
postgres=# alter system set edb_audit_connect = 'all'; ALTER SYSTEM postgres=# select context from pg_settings where name = 'edb_audit_connect'; context --------- sighup (1 row) postgres=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row)
From now on we should have the audit information in the log file for every successful connection and every connection attempt that failed. Is it true?
postgres=# \! psql edb psql.bin (11.2.9) Type "help" for help. [local]:5444 enterprisedb@edb=# \q postgres=# \! psql -U dummy edb psql.bin: FATAL: role "dummy" does not exist postgres=#
That should have produced two lines in the latest audit file:
enterprisedb@edb1:/var/lib/edb/audit/ [pg1] pwd /var/lib/edb/audit enterprisedb@edb1:/var/lib/edb/audit/ [pg1] ls -latr total 8 drwx------. 5 enterprisedb enterprisedb 183 Mar 24 14:24 .. -rw-------. 1 enterprisedb enterprisedb 611 Mar 24 14:38 audit-20190324_143640.csv drwx------. 2 enterprisedb enterprisedb 72 Mar 24 14:38 . -rw-------. 1 enterprisedb enterprisedb 412 Mar 24 14:41 audit-20190324_143805.csv enterprisedb@edb1:/var/lib/edb/audit/ [pg1] cat audit-20190324_143805.csv 2019-03-24 14:40:54.683 CET,"enterprisedb","edb",1534,"[local]",5c9788e6.5fe,1,"authentication",2019-03-24 14:40:54 CET,5/133,0,AUDIT,00000,"connection authorized: user=enterprisedb database=edb",,,,,,,,,"","","" 2019-03-24 14:41:16.617 CET,"dummy","edb",1563,"[local]",5c9788fc.61b,1,"authentication",2019-03-24 14:41:16 CET,5/136,0,AUDIT,00000,"connection authorized: user=dummy database=edb",,,,,,,,,"","",""
a
As expected, we can see the successful connection request and in addition the one that failed. When we want to log disconnections as well, we can do so:
postgres=# alter system set edb_audit_disconnect = 'all'; ALTER SYSTEM postgres=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row) postgres=# \! psql edb psql.bin (11.2.9) Type "help" for help. [local]:5444 enterprisedb@edb=# \q postgres=#
In the same audit file as before:
2019-03-24 14:47:42.447 CET,"enterprisedb","edb",1929,"[local]",5c978a7a.789,2,"idle",2019-03-24 14:47:38 CET,,0,AUDIT,00000,"disconnection: session time: 0:00:03.708 user=enterprisedb database=edb host=[local]",,,,,,,,,"psql.bin","",""
The duration of the session is logged as well. So far for the basic auditing features. Logging connections and disconnections is a good start but probably not enough. You might soon come to a point where you want to have more information, such as what the user was doing exactly in the database. This is where “edb_audit_statement” comes into the game. You can set it to something simple like “all inserts” or “all updates”:
postgres=# alter system set edb_audit_statement = 'insert'; ALTER SYSTEM postgres=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row) postgres=# create table t1 ( a int ); CREATE TABLE postgres=# insert into t1 values(1); INSERT 0 1
Looking at the audit file:
2019-03-24 14:55:36.744 CET,,,9004,,5c977540.232c,3,,2019-03-24 13:17:04 CET,,0,LOG,00000,"received SIGHUP, reloading configuration files",,,,,,,,,"","","" 2019-03-24 14:55:53.460 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,6,"idle",2019-03-24 14:13:30 CET,4/477,0,AUDIT,00000,"statement: insert into t1 values(1);",,,,,,,,,"psql.bin","INSERT",""
The insert is logged. You may also spot a potential issue here: Depending on how the statement is written the actual values (1 in this case) is written to the log. This might open another security hole if the audit files are not handled with care. You can not prevent that using prepared statements and in fact the “prepare” part is logged as well:
postgres=# prepare stmt as insert into t1 values($1); PREPARE postgres=# execute stmt(2); INSERT 0 1 postgres=# select * from t1; a --- 1 2 (2 rows)
The entries in the audit log:
2019-03-24 14:58:50.395 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,7,"idle",2019-03-24 14:13:30 CET,4/478,0,AUDIT,00000,"statement: prepare stmt as insert into t1 values($1);",,,,,,,,,"psql.bin","PREPARE","" 2019-03-24 14:59:02.952 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,8,"idle",2019-03-24 14:13:30 CET,4/479,0,AUDIT,00000,"statement: execute stmt(2);","prepare: prepare stmt as insert into t1 values($1);",,,,,,,,"psql.bin","EXECUTE",""
Although we only asked to log “inserts”, the prepare and execute statements are logged as well. If we prepare an update it is not logged (what is correct):
postgres=# prepare stmt2 as update t1 set a = $1; PREPARE postgres=# execute stmt2(2); UPDATE 5
The last line in the audit file is still this one:
2019-03-24 15:02:33.502 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,9,"idle",2019-03-24 14:13:30 CET,4/487,0,AUDIT,00000,"statement: execute stmt(5);","prepare: prepare stmt as insert into t1 values($1);",,,,,,,,"psql.bin","EXECUTE",""
The power of edb_audit_statement comes when you want to audit multiple kinds of statements but do not want to set it to “all” (this would log all the statements):
postgres=# alter system set edb_audit_statement='insert,update,delete,create table,drop view'; ALTER SYSTEM postgres=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row)
This should log all inserts, updates and deletes and in addition every create table or drop view:
postgres=# create table t2 ( a int ); CREATE TABLE postgres=# insert into t2 values(1); INSERT 0 1 postgres=# update t2 set a = 2; UPDATE 1 postgres=# delete from t2 where a = 2; DELETE 1 postgres=# truncate t2; TRUNCATE TABLE postgres=# create view v1 as select * from t2; CREATE VIEW postgres=# drop view v1; DROP VIEW
We should see entries for the insert, the update and the delete, but not for the truncate. The drop view should be logged as well:
2019-03-24 15:08:46.245 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,10,"idle",2019-03-24 14:13:30 CET,4/496,0,AUDIT,00000,"statement: create table t2 ( a int );",,,,,,,,,"psql.bin","CREATE TABLE","" 2019-03-24 15:08:59.713 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,12,"idle",2019-03-24 14:13:30 CET,4/498,0,AUDIT,00000,"statement: insert into t2 values(1);",,,,,,,,,"psql.bin","INSERT","" 2019-03-24 15:09:21.299 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,13,"idle",2019-03-24 14:13:30 CET,4/499,0,AUDIT,00000,"statement: update t2 set a = 2;",,,,,,,,,"psql.bin","UPDATE","" 2019-03-24 15:09:29.614 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,14,"idle",2019-03-24 14:13:30 CET,4/500,0,AUDIT,00000,"statement: delete from t2 where a = 2;",,,,,,,,,"psql.bin","DELETE","" 2019-03-24 15:12:51.652 CET,"enterprisedb","postgres",31899,"[local]",5c97827a.7c9b,15,"idle",2019-03-24 14:13:30 CET,4/503,0,AUDIT,00000,"statement: drop view v1;",,,,,,,,,"psql.bin","DROP VIEW",""
Fine. Using edb_audit_statement we have control of what exactly we want to log. What we did now was valid for the whole instance, can we modify auditing to a specific role? Yes, this is possible:
edb=# alter user enterprisedb set edb_audit_statement = 'truncate'; ALTER ROLE edb=# create role test; CREATE ROLE edb=# alter role test set edb_audit_statement = 'truncate'; ALTER ROLE
The same is true on the database level:
edb=# alter database edb set edb_audit_statement = 'truncate'; ALTER DATABASE
Lets do a small test and create user and then set edb_audit_statement on the user level, and reset it on the instance level:
edb=# create user u1 with login password 'u1'; CREATE ROLE edb=# alter user u1 set edb_audit_statement = 'create table'; ALTER ROLE edb=# alter system set edb_audit_statement = 'none'; ALTER SYSTEM edb=# select pg_reload_conf(); pg_reload_conf ---------------- t (1 row)
Create table statements from that user should now be logged:
edb=# \c edb u1 You are now connected to database "edb" as user "u1". edb=> create table t1 ( a int ); CREATE TABLE
The statement is indeed logged:
2019-03-24 15:44:19.793 CET,"u1","edb",6243,"[local]",5c9797b7.1863,1,"idle",2019-03-24 15:44:07 CET,5/30177,0,AUDIT,00000,"statement: create table t1 ( a int );",,,,,,,,,"psql.bin","CREATE TABLE",""
Does the same work for a role?
edb=> \c edb enterprisedb You are now connected to database "edb" as user "enterprisedb". edb=# create role r1; CREATE ROLE edb=# alter role r1 set edb_audit_statement = 'drop table'; ALTER ROLE edb=# grant r1 to u1; GRANT ROLE edb=# \c edb u1 You are now connected to database "edb" as user "u1". edb=> drop table t1; DROP TABLE edb=>
No, in this case the drop statement is not logged. You can set the parameter for a role, but is does not have any effect.
The last test for today: What happens when the directory we configured for the audit files is removed?
enterprisedb@edb1:/var/lib/edb/ [pg1] pwd /var/lib/edb enterprisedb@edb1:/var/lib/edb/ [pg1] ls -l total 0 drwx------. 4 enterprisedb enterprisedb 51 Mar 24 13:16 as11 drwx------. 2 enterprisedb enterprisedb 72 Mar 24 14:38 audit drwxrwxr-x. 3 enterprisedb enterprisedb 17 Mar 24 13:09 local enterprisedb@edb1:/var/lib/edb/ [pg1] mv audit/ audit_org enterprisedb@edb1:/var/lib/edb/ [pg1] ls -l total 0 drwx------. 4 enterprisedb enterprisedb 51 Mar 24 13:16 as11 drwx------. 2 enterprisedb enterprisedb 72 Mar 24 14:38 audit_org drwxrwxr-x. 3 enterprisedb enterprisedb 17 Mar 24 13:09 local
These two inserts should generate audit records:
edb=> create table t2 ( a int ); CREATE TABLE edb=> create table t3 ( a int ); CREATE TABLE edb=>
Nothing happens, not even a log entry in the server log file. I would have at least expected to get a warning that the directory does not exist. Lets restart the instance:
enterprisedb@edb1:/var/lib/edb/as11/data/log/ [pg1] pg_ctl -D /var/lib/edb/as11/data/ restart -m fast waiting for server to shut down.... done server stopped waiting for server to start....2019-03-24 15:51:59 CET LOG: listening on IPv4 address "0.0.0.0", port 5444 2019-03-24 15:51:59 CET LOG: listening on IPv6 address "::", port 5444 2019-03-24 15:51:59 CET LOG: listening on Unix socket "/tmp/.s.PGSQL.5444" 2019-03-24 15:51:59 CET LOG: redirecting log output to logging collector process 2019-03-24 15:51:59 CET HINT: Future log output will appear in directory "log". done server started
And again: Nothing. But the audit directory is being recreated once the server starts:
enterprisedb@edb1:/var/lib/edb/ [pg1] ls -l total 0 drwx------. 4 enterprisedb enterprisedb 51 Mar 24 13:16 as11 drwx------. 2 enterprisedb enterprisedb 39 Mar 24 15:51 audit drwx------. 2 enterprisedb enterprisedb 72 Mar 24 14:38 audit_org drwxrwxr-x. 3 enterprisedb enterprisedb 17 Mar 24 13:09 local
Changing the permissions so that the enterprisedb user can not write anymore to that directory will prevent the server from restarting:
enterprisedb@edb1:/var/lib/edb/ [pg1] sudo chown root:root audit enterprisedb@edb1:/var/lib/edb/ [pg1] ls -l total 0 drwx------. 4 enterprisedb enterprisedb 51 Mar 24 13:16 as11 drwx------. 2 root root 39 Mar 24 15:51 audit drwx------. 2 enterprisedb enterprisedb 72 Mar 24 14:38 audit_org drwxrwxr-x. 3 enterprisedb enterprisedb 17 Mar 24 13:09 local enterprisedb@edb1:/var/lib/edb/ [pg1] pg_ctl -D /var/lib/edb/as11/data/ restart -m fast waiting for server to shut down.... done server stopped waiting for server to start....2019-03-24 15:55:44 CET LOG: listening on IPv4 address "0.0.0.0", port 5444 2019-03-24 15:55:44 CET LOG: listening on IPv6 address "::", port 5444 2019-03-24 15:55:44 CET LOG: listening on Unix socket "/tmp/.s.PGSQL.5444" 2019-03-24 15:55:44 CET FATAL: could not open log file "/var/lib/edb/audit/audit-20190324_155544.csv": Permission denied 2019-03-24 15:55:44 CET LOG: database system is shut down stopped waiting pg_ctl: could not start server Examine the log output.
Hope that helps…
Cet article Auditing with EDB Postgres Enterprise est apparu en premier sur Blog dbi services.