What is Postgres Auditing?Auditing is the systematic investigation of procedures, processes, and practices related to an organization’s IT infrastructure. Auditors typically need a comprehensive log of changes and activity related to software, business data, and security systems. They also need to track changes to organizational structure, security policies, and user account definitions. PostgreSQL supports auditing with the following audit logging options. We’ll describe each of these methods, and help you understand which one is the most suitable for your scenario:
- The log_statement = all configuration option lets you exhaustively log all activity in a PostgreSQL database
- The pgaudit extension, provided by the Postgres community, lets you apply filtering and convert logs to a format required for auditing
- Custom triggers let you create your own customized audit flows
- What is log_statement=all?
- What is pgAudit?
- Why Choose pgAudit Over log_statement=all?
- pgAudit Considerations
- Defining Custom Triggers for Auditing
- How to Implement a Custom Trigger
- Pros and Cons of the Custom Trigger Methods
- Postgres Auditing with Satori
What is log_statement=all?The log_statement parameter in PostgreSQL configuration specifies which SQL statements are logged. Only superusers can change it. It has the following values:
- none – no logging. This is the default option.
- ddl – logging all data definition operations like CREATE, DROP, and ALTER
- mod – same as ddl, and also logs operations that modify data, like INSERT, UPDATE and DELETE
- all – logs all SQL queries.
What is pgAudit?
pgAudit stands for “PostgreSQL Audit Extension”. It is a native PostgreSQL extension that uses the standard PostgreSQL logging facility to provide detailed object and session audit logging. pgAudit was designed to extend native PostgreSQL capabilities, providing users with the ability to produce audit logs. It is often used to supply information for financial or regulatory compliance, as well as for ISO certifications.
Why Choose pgAudit Over log_statement=all?
The basic way to enable an audit trail in PostgreSQL is to enable log_statement=all. However, this will simply list all database operations—it won’t let you filter certain operations, and will not generate logs in the format needed for auditing. pgAudit provides these capabilities.
pgAudit also allows you to log specific database commands, for example read-only statements like SELECT and COPY, write commands like INSERT, UPDATE and DELETE, etc. You can also specify that only operations on specific objects should be logged.
Another important difference is that pgAudit logs the details of each operation, with its full internals, using a structured output that is suitable for audit searches.
For example, here is a DO statement, and the log generated by log_statement=all compared to the log generated by pgAudit. This code was shared in the pgAudit documentation.
Depending on your settings, pgAudit can generate a large volume of logs. You should carefully determine the exact log content required for audits in your environment, to avoid an explosion of logs, which will make it more difficult to analyze logs and also have storage implications.
For example, when working in an OLAP environment, the log file can be several times the size of the actual data inserted, so you should not include it as part of your fact table. Disk space can quickly run out, and logging can have a significant impact on database performance. Try to limit the audit log to specific tables, and if you cannot, perform testing to evaluate the performance impact, and ensure you have enough disk space.
Defining Custom Triggers for Auditing
The basic idea behind custom trigger auditing is to create a shadow audit table for the database tables you need to track. Each change to the main table should be logged to the shadow table, using triggers.
It is important to only implement triggers using database operations and stored procedures, not via application code. If triggers are created as part of application code, the code can break when ad-hoc changes are made to the data. You can also create triggers in PostgreSQL using PL, pgSQL, or Python.
How to Implement a Custom Trigger
You can use the following process to define a shadow table and log all changes from a master table called MYTABLE to it:
- Create the shadow table, naming it MYTABLE_LOG. Create at least the following audit columns: Change time, User, and Type of change, and in addition, the data columns from MYTABLE which are important for your auditing process.
- Create a function that inserts new values into the MYTABLE_LOG shadow table, and populates the audit columns.
- Create a trigger in MYTABLE to invoke the auditing function each time a row is altered. There are two options for defining the trigger:
- Use an AFTER trigger to log the change without any impact on the table.
- Use a BEFORE trigger for transactions impacting thousands of rows, to ensure you use less resources and minimize the likelihood of exceptions.
Pros and Cons of the Custom Trigger Methods
- The advantage is that custom triggers can explicitly log the information you need for your audits, separate audit data to its own table, and enable easy search of audit logs, by indexing shadow tables.
- The disadvantage is that you need to write a stored procedure and manage the trigger definition over time. Triggers can also result in schema duplication, and inflate database size, because some or all of the data is duplicated to other tables.