<?xml version="1.0" encoding="UTF-8" ?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" version="2.0"><channel><title>Jason O'Donnell | CrunchyData Blog</title>
<atom:link href="https://www.crunchydata.com/blog/author/jason-odonnell/rss.xml" rel="self" type="application/rss+xml" />
<link>https://www.crunchydata.com/blog/author/jason-odonnell</link>
<image><url>https://www.crunchydata.com/build/_assets/default.png-W4XGD4DB.webp</url>
<title>Jason O'Donnell | CrunchyData Blog</title>
<link>https://www.crunchydata.com/blog/author/jason-odonnell</link>
<width>256</width>
<height>256</height></image>
<description>PostgreSQL experts from Crunchy Data share advice, performance tips, and guides on successfully running PostgreSQL and Kubernetes solutions</description>
<language>en-us</language>
<pubDate>Tue, 27 Mar 2018 05:00:00 EDT</pubDate>
<dc:date>2018-03-27T09:00:00.000Z</dc:date>
<dc:language>en-us</dc:language>
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<item><title><![CDATA[ An Easy Recipe for Creating a PostgreSQL Cluster with Docker Swarm ]]></title>
<link>https://www.crunchydata.com/blog/an-easy-recipe-for-creating-a-postgresql-cluster-with-docker-swarm</link>
<description><![CDATA[ This article provides an easy recipe for creating a PostgreSQL primary-replica cluster using Docker Swarm. ]]></description>
<content:encoded><![CDATA[ <p>One of the biggest benefits of running PostgreSQL is running your cluster in primary-replica setup for the purposes of high-availability or load balancing your read-only queries. It is not necessarily simple to deploy a primary-replica setup out of the box, but by using modern containerization technology, we can greatly simplify the process.<p>In this article, I will demonstrate how to easily setup and deploy a PostgreSQL primary-replica cluster using <a href=https://www.docker.com/>Docker</a> and <a href=https://docs.docker.com/engine/swarm/>Docker Swarm</a>.<h2 id=background><a href=#background>Background</a></h2><p><a href=https://www.postgresql.org/docs/current/static/warm-standby.html#STREAMING-REPLICATION>Streaming replication</a>, or pushing changes from a primary PostgreSQL instance to its replicas in "real-time," was introduced into PostgreSQL in <a href=https://www.postgresql.org/docs/current/static/release-9-0.html>version 9.0</a> and has continued to receive numerous enhancements in all subsequent releases, including:<ul><li>Asynchronous replication<li><a href=https://www.postgresql.org/docs/current/static/warm-standby.html#SYNCHRONOUS-REPLICATION>Synchronous replication</a><li>Quorum commit<li><a href=https://www.postgresql.org/docs/current/static/warm-standby.html#CASCADING-REPLICATION>Cascading replication</a><li><a href=https://www.postgresql.org/docs/10/static/logical-replication.html>Logical Replication</a></ul><p>The PostgreSQL documentation also provides an <a href=https://www.postgresql.org/docs/current/static/different-replication-solutions.html>overview and comparison of the different replication methods</a>.<p>The primary-replica methodology of deploying a PostgreSQL replica is an essential tool in creating high-availability environments for your database cluster: a proper deployment should ensure that your data is stored on different disks in different data centers.<p>Replication is not a "setup and forget" operation: in a production system, you will want to ensure you have appropriate <a href=https://github.com/CrunchyData/pgmonitor>monitoring</a> in place, for instance, to know that all of your replicas are online or to know how much data a replica may need to be in sync with the primary.<p>While the primary-replica configuration with PostgreSQL is great, it can take a little bit of work to setup. Fortunately, there is a way to deploy this setup more easily with Docker.<h2 id=environment-setup><a href=#environment-setup>Environment SETUP</a></h2><p>In order to deploy this recipe, you will need <strong>at least</strong> Docker 1.12, which was released in July 2016, installed in order to successfully deploy this recipe.<p>To begin, provision a Docker cluster. For this example a development cluster, you can load Docker on each of the machines you will use in your Swarm. This recipe is using the following architecture:<p><img alt="PostgreSQL Docker Swarm Architecture"loading=lazy src=https://cdn2.hubspot.net/hubfs/2283855/diagram.png title="PostgreSQL Docker Swarm Architecture"><p>Each host should have Docker enabled and started.<h2 id=swarm-setup><a href=#swarm-setup>Swarm Setup</a></h2><p>Since version 1.12, Docker has included the Swarm clustering technology directly within the Docker Engine.<p>Creating a Docker Swarm cluster is out of scope for this tutorial, however, documentation on <a href=https://docs.docker.com/engine/swarm/swarm-tutorial/create-swarm>setting up a Swarm cluster can be found here</a>.<h2 id=container-placement><a href=#container-placement>Container Placement</a></h2><p>Two or more hosts are required for a <a href=https://www.crunchydata.com/products/crunchy-high-availability-postgresql>high-availability PostgreSQL</a> cluster configuration. The primary and replica(s) should run on different worker nodes to maximize availability.<p>In order to deploy the <a href=https://github.com/CrunchyData/crunchy-containers/>Crunchy PostgreSQL containers</a> to multiple hosts, you will need to use <a href=https://docs.docker.com/engine/reference/commandline/node_update/#add-label-metadata-to-a-node>node labels</a>. Labeling hosts has a few advantages when it comes to PostgreSQL containers:<ul><li>Spreading services out on many worker nodes improves availability<li>Hosts can be optimized (such as using high-performance disks) for reads (replicas) or writes (primary)</ul><p><strong>Remember</strong>: as of PostgreSQL 10, a primary database can support both reads and writes, but a replica only allows read queries.<p>To allow for container placement on specific worker nodes, add a metadata label to the Swarm nodes:<pre><code class=language-shell>node1_id=$(docker node list | grep worker1 | awk '{print $1}')
docker node update --label-add type=primary ${node1_id?}
</code></pre><p>In the above example, a label called <code>primary</code> has been added to the <code>worker1</code>. Using this label we can apply constraints to the Docker Swarm deployment of the PostgreSQL stack.<p><strong>Note</strong>*: We did not apply a constraint for replicas as we can just use the inverse constraint:<pre><code class=language-pgsql>node.labels.type != primary
</code></pre><h2 id=postgresql-stack-definition><a href=#postgresql-stack-definition>PostgreSQL Stack Definition</a></h2><p>With the Swarm deployed and the worker node properly labeled, we can deploy the PostgreSQL stack.<p>The PostgreSQL stack is comprised of a primary and replica services. The following is the service definition:</p><figure>
<figcaption><p><em><code>docker-compose.yml</code></em></p></figcaption><pre><code class=language-yaml>---
version: '3.3'

services:
  primary:
    hostname: 'primary'
    image: crunchydata/crunchy-postgres:centos7-10.3-1.8.2
    environment:
      - PGHOST=/tmp
      - MAX_CONNECTIONS=10
      - MAX_WAL_SENDERS=5
      - PG_MODE=primary
      - PG_PRIMARY_USER=primaryuser
      - PG_PRIMARY_PASSWORD=password
      - PG_DATABASE=testdb
      - PG_USER=testuser
      - PG_PASSWORD=password
      - PG_ROOT_PASSWORD=password
      - PG_PRIMARY_PORT=5432
    volumes:
      - pg-primary-vol:/pgdata
    ports:
      - '5432'
    networks:
      - crunchynet
    deploy:
      placement:
        constraints:
          - node.labels.type == primary
          - node.role == worker
  replica:
    image: crunchydata/crunchy-postgres:centos7-10.3-1.8.2
    environment:
      - PGHOST=/tmp
      - MAX_CONNECTIONS=10
      - MAX_WAL_SENDERS=5
      - PG_MODE=replica
      - PG_PRIMARY_HOST=primary
      - PG_PRIMARY_PORT=5432
      - PG_PRIMARY_USER=primaryuser
      - PG_PRIMARY_PASSWORD=password
      - PG_DATABASE=testdb
      - PG_USER=testuser
      - PG_PASSWORD=password
      - PG_ROOT_PASSWORD=password
    volumes:
      - pg-replica-vol:/pgdata
    ports:
      - '5432'
    networks:
      - crunchynet
    deploy:
      placement:
        constraints:
          - node.labels.type != primary
          - node.role == worker
networks:
  crunchynet:

volumes:
  pg-primary-vol:
  pg-replica-vol:
</code></pre></figure><p>Notice that the <code>primary</code> service defines a <code>hostname</code> but the <code>replica</code> service does not. Replicas require a <code>hostname</code> to start replication. By providing a static <code>hostname</code> to the primary the replicas can connect without having to discover the primary container.<p>The replicas, however, do not have a <code>hostname</code>. This allows the replica service to be scaled beyond a single replica (as shown later).<p>The only real difference between the <code>primary</code> and <code>replica</code> services is the <code>PG_MODE</code> environment variable. This configures the containers to either be a <code>primary</code> or <code>replica</code>.<h2 id=deploying-the-stack><a href=#deploying-the-stack>Deploying the Stack</a></h2><p>After saving this file to <code>docker-compose.yml</code>, we can deploy the stack with Docker:<pre><code class=language-shell>docker stack deploy --compose-file=./docker-compose.yml pg-stack
</code></pre><p>This stack deployment will create a PostgreSQL cluster similar to this diagram:<p><img alt="PostgreSQL Docker Swarm Primary Replica Cluster"loading=lazy src=https://cdn2.hubspot.net/hubfs/2283855/Diagram4.png title="PostgreSQL Docker Swarm Primary Replica Cluster"><h2 id=testing-the-cluster><a href=#testing-the-cluster>Testing the Cluster</a></h2><p>To check if services are running, run the following commands:<pre><code class=language-shell>docker service ls
docker service ps pg-stack_primary
docker service ps pg-stack_replica
</code></pre><p>To increase the number of replicas, run the following:<pre><code class=language-shell>docker service scale pg-stack_replica=2
docker service ps pg-stack_replica
</code></pre><p>To verify the replicas are streaming, query the PostgreSQL primary on the <code>worker1</code> host using the following command:<pre><code class=language-shell>docker exec -it $(docker ps -q) psql -U postgres -x -c 'table pg_stat_replication' postgres
</code></pre><p>You should see a row for each replica along with its replication status.<h2 id=example-code><a href=#example-code>Example Code</a></h2><p>The example described above is provided in the Crunchy Containers Suite GitHub in the following location:<p><a href=https://github.com/CrunchyData/crunchy-containers/tree/master/examples/docker/swarm-service>https://github.com/CrunchyData/crunchy-containers/tree/master/examples/docker/swarm-service</a><h2 id=conclusion><a href=#conclusion>Conclusion</a></h2><p>Docker and Docker Swarm provide tools to take container deployments to the next level. We hope that this demonstration proves how easy it is to get up and running with Crunchy PostgreSQL containers. ]]></content:encoded>
<category><![CDATA[ Postgres Tutorials ]]></category>
<author><![CDATA[ Jason.O'Donnell@crunchydata.com (Jason O'Donnell) ]]></author>
<dc:creator><![CDATA[ Jason O'Donnell ]]></dc:creator>
<guid isPermalink="false">https://blog.crunchydata.com/blog/an-easy-recipe-for-creating-a-postgresql-cluster-with-docker-swarm</guid>
<pubDate>Tue, 27 Mar 2018 05:00:00 EDT</pubDate>
<dc:date>2018-03-27T09:00:00.000Z</dc:date>
<atom:updated>2018-03-27T09:00:00.000Z</atom:updated></item>
<item><title><![CDATA[ pgAudit: Auditing Database Operations Part 2 ]]></title>
<link>https://www.crunchydata.com/blog/pgaudit-auditing-database-operations-part-2</link>
<description><![CDATA[ A discussion of how to configure pgAudit to use an auditing role to watch only specific objects. This is helpful when managing or supporting PostgreSQL installations. ]]></description>
<content:encoded><![CDATA[ <p>In the <a href=/blog/pgaudit-auditing-database-operations-part-1>last blog post</a>, pgAudit was configured to audit entire classes of statements (session auditing). Session auditing works great, but it can generate a lot of logs and not every administrator needs all that information. In this blog post pgAudit will be configured to use an auditing role to watch only specific objects.<h2 id=getting-started><a href=#getting-started>Getting Started</a></h2><p>This guide assumes pgAudit has already been installed on the target DB server. For more instructions on installing pgAudit, see the <a href=https://github.com/pgaudit/pgaudit#compile-and-install>official documentation here</a>.<p>pgAudit auditor role only supports the following commands against objects:<ul><li><code>SELECT</code><li><code>INSERT</code><li><code>UPDATE</code><li><code>DELETE</code></ul><h2 id=setup><a href=#setup>Setup</a></h2><p>First, create a role to designate as the auditing role:<pre><code class=language-pgsql>CREATE ROLE auditor NOLOGIN;
</code></pre><p>Next, configure pgAudit to use that role for auditing:<pre><code class=language-pgsql>ALTER SYSTEM SET pgaudit.role TO 'auditor';
SELECT pg_reload_conf();
</code></pre><p>That's it! pgAudit is now configured to use an auditing role.<h2 id=auditor-example><a href=#auditor-example>Auditor Example</a></h2><p>With pgAudit configured to use an auditing role, <code>auditor</code> can be assigned objects to audit.<p>First, lets create a test table and insert some data:<pre><code class=language-pgsql>CREATE TABLE public.pgauditExample(id SERIAL, name TEXT, secret TEXT, age INT);
INSERT INTO public.pgauditExample(name, secret, age) VALUES ('crunchy', 'my-secret', 30);
</code></pre><p>Next, grant the operations to the auditing role that should be monitored:<pre><code class=language-pgsql>GRANT SELECT (name, secret), UPDATE (secret) ON public.pgauditExample TO auditor;
</code></pre><p>Notice how <code>SELECT</code> is granted to <code>name</code> and <code>secret</code>, however, auditor is also granted <code>UPDATE</code> on <code>secret</code>.<p>Next, trigger some audit logging on the objects that were just configured:<pre><code class=language-pgsql>SELECT name FROM public.pgauditExample;
SELECT secret FROM public.pgauditExample;
UPDATE public.pgauditExample SET secret = 'new-secret' WHERE 'name' = 'crunchy';
SELECT age FROM public.pgauditExample;
</code></pre><p>Finally, check <code>pg_log</code> for the audit entries:<pre><code class=language-bash>$ grep AUDIT postgresql-Fri.log | grep OBJECT
2016-10-12 13:54:54.836 UTC postgres postgres LOG: AUDIT: OBJECT,5,1,READ,SELECT,TABLE,public.pgauditexample,SELECT name FROM pgauditExample;,&#60none>
2016-10-12 13:54:54.837 UTC postgres postgres LOG: AUDIT: OBJECT,6,1,READ,SELECT,TABLE,public.pgauditexample,SELECT secret FROM pgauditExample;,&#60none>
2016-10-12 13:54:54.838 UTC postgres postgres LOG: AUDIT: OBJECT,7,1,WRITE,UPDATE,TABLE,public.pgauditexample,UPDATE pgauditExample SET secret = 'new-secret' WHERE 'name' = 'crunchy';,&#60none>
</code></pre><p>Notice how there is no audit log for the SELECT on <code>age</code>. The auditor role has not been granted privileges on that column, thus it does not watch the object.<h2 id=multiple-auditor-roles><a href=#multiple-auditor-roles>Multiple Auditor Roles</a></h2><p>In the last example pgAudit was configured to use the auditor role to watch specific objects. Although pgAudit can only be assigned one master auditor role, multiple roles can be configured to audit objects by granting them to the master role.<p>First, create new roles to also be used for auditing:<pre><code class=language-pgsql>CREATE ROLE read_auditor NOLOGIN;
CREATE ROLE write_auditor NOLOGIN;
CREATE ROLE delete_auditor NOLOGIN;
</code></pre><p>Next, grant these roles to the <code>auditor</code> role:<pre><code class=language-pgsql>GRANT read_auditor TO auditor;
GRANT write_auditor TO auditor;
GRANT delete_auditor TO auditor;
</code></pre><p>With the auditor role configured to use multiple roles, create a test table:<pre><code class=language-pgsql>CREATE TABLE public.pgauditAuditorExample(id SERIAL, name TEXT, secret TEXT, age INT);
INSERT INTO public.pgauditAuditorExample(name, secret, age) VALUES ('crunchy', 'my-secret', 30);
</code></pre><p>Next, grant the new roles access to various operations on the test table:<pre><code class=language-pgsql>GRANT SELECT (name, secret, age) ON public.pgauditAuditorExample TO read_auditor;
GRANT INSERT(secret), UPDATE (secret) ON public.pgauditAuditorExample TO write_auditor;
GRANT DELETE ON public.pgauditAuditorExample TO delete_auditor;
</code></pre><p>With the new roles assigned to their objects to audit, trigger the audit logging:<pre><code class=language-pgsql>SELECT secret FROM public.pgauditAuditorExample;
INSERT INTO public.pgauditAuditorExample(name, secret, age) VALUES ('postgres', 'new-secret', 100);
UPDATE public.pgauditAuditorExample SET secret = 'new-secret' WHERE name = 'crunchy';
DELETE FROM public.pgauditAuditorExample WHERE age = 100;
</code></pre><p>Finally, check <code>pg_log</code> for the audit entries:<pre><code class=language-bash>$ grep AUDIT postgresql-Wed.log | grep OBJECT
2016-10-12 14:22:01.416 UTC postgres postgres LOG: AUDIT: OBJECT,4,1,READ,SELECT,TABLE,public.pgauditauditorexample,SELECT secret FROM public.pgauditAuditorExample;,&#60none>
2016-10-12 14:22:05.447 UTC postgres postgres LOG: AUDIT: OBJECT,5,1,WRITE,INSERT,TABLE,public.pgauditauditorexample,"INSERT INTO public.pgauditAuditorExample(name, secret, age) VALUES ('postgres', 'new-secret', 100);",&#60none>
2016-10-12 14:22:12.299 UTC postgres postgres LOG: AUDIT: OBJECT,6,1,WRITE,UPDATE,TABLE,public.pgauditauditorexample,UPDATE public.pgauditAuditorExample SET secret = 'new-secret' WHERE name = 'crunchy';,&#60none>
2016-10-12 14:22:17.198 UTC postgres postgres LOG: AUDIT: OBJECT,7,1,WRITE,DELETE,TABLE,public.pgauditauditorexample,DELETE FROM public.pgauditAuditorExample WHERE age = 100;,&#60none>
</code></pre><h2 id=wrap-up><a href=#wrap-up>Wrap up</a></h2><p>These examples show how pgAudit can be tuned to watch specific objects using auditor role(s). This gives administrators the ability to finely tune what is audited in their database by either using <a href=https://www.crunchydata.com/blog/pgaudit-auditing-database-operations-part-1>session auditing</a> or object auditing.<p>pgAudit is shipped with the <a href=https://www.crunchydata.com/products/crunchy-certified-postgresql>Crunchy Certified PostgreSQL</a> distribution and can also be obtained from the <a href=https://github.com/pgaudit/pgaudit>project repository</a>. ]]></content:encoded>
<category><![CDATA[ Production Postgres ]]></category>
<author><![CDATA[ Jason.O'Donnell@crunchydata.com (Jason O'Donnell) ]]></author>
<dc:creator><![CDATA[ Jason O'Donnell ]]></dc:creator>
<guid isPermalink="false">https://blog.crunchydata.com/blog/pgaudit-auditing-database-operations-part-2</guid>
<pubDate>Fri, 14 Oct 2016 05:00:00 EDT</pubDate>
<dc:date>2016-10-14T09:00:00.000Z</dc:date>
<atom:updated>2016-10-14T09:00:00.000Z</atom:updated></item>
<item><title><![CDATA[ pgAudit: Auditing Database Operations Part 1 ]]></title>
<link>https://www.crunchydata.com/blog/pgaudit-auditing-database-operations-part-1</link>
<description><![CDATA[ Using pgAudit for enhanced logging and auditing of PostgreSQL ]]></description>
<content:encoded><![CDATA[ <p>The PostgreSQL Audit extension (pgaudit) provides detailed session and/or object audit logging via the standard PostgreSQL logging facility.<p>Basic statement logging can be provided by the standard logging facility in PostgreSQL. Out of the box logging provided by PostgreSQL is acceptable for monitoring and other usages but does not provide the level of detail generally required for an audit.<p>pgAudit enhances PostgreSQL's logging abilities by allowing administrators to audit specific classes of operations or choosing specific objects to monitor.<h2 id=getting-started><a href=#getting-started>Getting Started</a></h2><p>This guide assumes pgAudit has already been installed on the target DB server. For more instructions on installing pgAudit, see the <a href=https://github.com/pgaudit/pgaudit#compile-and-install>official documentation here</a>.<h2 id=session-auditing><a href=#session-auditing>Session Auditing</a></h2><p>Session auditing allows administrators to choose classes of statements to log:<ul><li><strong>READ</strong> (<code>SELECT</code> and <code>COPY</code> when the source is a relation or a query)<li><strong>WRITE</strong> (<code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code>, <code>TRUNCATE</code>, and <code>COPY</code> when the destination is a relation)<li><strong>FUNCTION</strong> (Functions and <code>DO</code> blocks)<li><strong>ROLE</strong> (<code>GRANT</code>, <code>REVOKE</code>, <code>CREATE/ALTER/DROP</code> ROLE)<li><strong>DDL</strong> (All DDL not included in <code>ROLE</code>)<li><strong>MISC</strong> (<code>DISCARD</code>, <code>FETCH</code>, <code>CHECKPOINT</code>, <code>VACUUM</code>)</ul><h3 id=read-example><a href=#read-example>READ Example</a></h3><p>First, create a test table and insert some data:<pre><code class=language-pgsql>CREATE TABLE pgauditExample(id SERIAL, name TEXT);
INSERT INTO pgauditExample(name) VALUES ('crunchy');
</code></pre><p>Next, configure pgAudit to audit the <code>read</code> class by altering the <code>pgaudit.log</code> parameter:<pre><code class=language-pgsql>ALTER SYSTEM SET pgaudit.log TO 'read';
SELECT pg_reload_conf();
</code></pre><p>With pgAudit set to audit the <code>read</code> class, <code>SELECT</code> from our test table:<pre><code class=language-pgsql>SELECT name FROM pgauditExample;
</code></pre><p>Finally, check <code>pg_log</code> for an audit entry:<pre><code class=language-shell>$ grep AUDIT postgresql-Fri.log | grep READ
2016-09-30 00:16:24.688 UTC postgres postgres LOG: AUDIT: SESSION,1,1,READ,SELECT,,,SELECT name FROM pgauditExample;,&#60none>
</code></pre><h3 id=write-example><a href=#write-example>WRITE Example</a></h3><p>In the last example we configured pgAudit to audit the <code>READ</code> class of statements. Building on the previous example, add <code>WRITE</code>:<pre><code class=language-pgsql>ALTER SYSTEM SET pgaudit.log TO 'read, write';
SELECT pg_reload_conf();
</code></pre><p>With pgAudit set to audit the <code>read</code> and <code>write</code> classes, <code>INSERT</code>, <code>UPDATE</code> and <code>DELETE</code> from our test table:<pre><code class=language-pgsql>INSERT INTO pgauditExample(name) VALUES ('postgres');
UPDATE pgauditExample SET name = 'awesome' WHERE name = 'postgres';
DELETE FROM pgauditExample WHERE name = 'awesome';
</code></pre><p>Finally, check <code>pg_log</code> for the audit entries:<pre><code class=language-shell>$ grep AUDIT postgresql-Fri.log | grep WRITE

2016-09-30 00:25:05.785 UTC postgres postgres LOG: AUDIT: SESSION,2,1,WRITE,INSERT,,,INSERT INTO pgauditExample(name) VALUES ('postgres');,&#60none>
2016-09-30 00:25:05.787 UTC postgres postgres LOG: AUDIT: SESSION,3,1,WRITE,UPDATE,,,UPDATE pgauditExample SET name = 'awesome' WHERE name = 'postgres';,&#60none>
2016-09-30 00:25:06.476 UTC postgres postgres LOG: AUDIT: SESSION,4,1,WRITE,DELETE,,,DELETE FROM pgauditExample WHERE name = 'awesome';,&#60none>
</code></pre><h3 id=function-example><a href=#function-example>Function Example</a></h3><p>So far we've configured pgAudit to audit <code>READ</code> and <code>WRITE</code>. Next, add <code>FUNCTION</code> to the watch list.<pre><code class=language-pgsql>ALTER SYSTEM SET pgaudit.log TO 'read, write, function';
SELECT pg_reload_conf();
</code></pre><p>With pgAudit set to audit the <code>function</code> class, execute an anonymous function:<pre><code class=language-pgsql>DO $$
BEGIN
 RAISE NOTICE 'pgAudit rocks!';
END
$$;
</code></pre><p>Finally, check <code>pg_log</code> for the audit entries:<pre><code class=language-shell>$ tail -5 postgresql-Fri.log

2016-09-30 14:51:19.036 UTC postgres postgres LOG: AUDIT: SESSION,1,1,FUNCTION,DO,,,"DO $$
 BEGIN
 RAISE NOTICE 'pgAudit rocks!';
 END
 $$;",&#60none>
</code></pre><h3 id=role-example><a href=#role-example>Role Example</a></h3><p>In the last example we configured pgAudit to audit the <code>READ</code>, <code>WRITE</code> and <code>FUNCTION</code> classes of statements. Building on the previous example, add <code>ROLE</code>. Instead of adding <code>role</code> to the <code>pgaudit.log</code> parameter, notice the configuration is different this time:<pre><code class=language-pgsql>ALTER SYSTEM SET pgaudit.log TO 'all, -misc, -ddl';
SELECT pg_reload_conf();
</code></pre><p>This time the configuration specifies all classes except <code>misc</code> and <code>ddl</code> (read, write, function, role).<p>With pgAudit set to audit the <code>role</code> class, create, alter and drop some roles:<pre><code class=language-pgsql>CREATE ROLE bob;
CREATE ROLE alice;
ALTER ROLE bob LOGIN;
ALTER ROLE alice LOGIN CONNECTION LIMIT 1;
GRANT ALL ON TABLE pgauditExample TO bob;
GRANT SELECT ON TABLE pgauditExample TO alice;
REVOKE ALL ON TABLE pgauditExample FROM bob;
REVOKE ALL ON TABLE pgauditExample FROM alice;
DROP ROLE bob;
DROP ROLE alice;
</code></pre><p>Finally, check <code>pg_log</code> for the audit entries:<pre><code class=language-shell>2016-09-30 15:03:11.522 UTC postgres postgres LOG: AUDIT: SESSION,2,1,ROLE,CREATE ROLE,,,CREATE ROLE bob;,&#60none>
2016-09-30 15:03:11.523 UTC postgres postgres LOG: AUDIT: SESSION,3,1,ROLE,CREATE ROLE,,,CREATE ROLE alice;,&#60none>
2016-09-30 15:03:11.524 UTC postgres postgres LOG: AUDIT: SESSION,4,1,ROLE,ALTER ROLE,,,ALTER ROLE bob LOGIN;,&#60none>
2016-09-30 15:03:11.526 UTC postgres postgres LOG: AUDIT: SESSION,5,1,ROLE,ALTER ROLE,,,ALTER ROLE alice LOGIN CONNECTION LIMIT 1;,&#60none>
2016-09-30 15:03:11.528 UTC postgres postgres LOG: AUDIT: SESSION,6,1,ROLE,GRANT,,,GRANT ALL ON TABLE pgauditExample TO bob;,&#60none>
2016-09-30 15:03:11.529 UTC postgres postgres LOG: AUDIT: SESSION,7,1,ROLE,GRANT,,,GRANT SELECT ON TABLE pgauditExample TO alice;,&#60none>
2016-09-30 15:03:11.531 UTC postgres postgres LOG: AUDIT: SESSION,8,1,ROLE,REVOKE,,,REVOKE ALL ON TABLE pgauditExample FROM bob;,&#60none>
2016-09-30 15:03:11.532 UTC postgres postgres LOG: AUDIT: SESSION,9,1,ROLE,REVOKE,,,REVOKE ALL ON TABLE pgauditExample FROM alice;,&#60none>
2016-09-30 15:03:11.534 UTC postgres postgres LOG: AUDIT: SESSION,10,1,ROLE,DROP ROLE,,,DROP ROLE bob;,&#60none>
2016-09-30 15:03:11.876 UTC postgres postgres LOG: AUDIT: SESSION,11,1,ROLE,DROP ROLE,,,DROP ROLE alice;,&#60none>
</code></pre><h3 id=ddl-example><a href=#ddl-example>DDL Example</a></h3><p>In the last example we configured pgAudit to audit all classes except <code>misc</code> and <code>ddl</code>. Building on the previous example, add <code>ddl</code>:<pre><code class=language-pgsql>ALTER SYSTEM SET pgaudit.log TO 'all, -misc';
SELECT pg_reload_conf();
</code></pre><p>With pgAudit set to audit the <code>ddl</code> class, have some fun with tables:<pre><code class=language-pgsql>CREATE TABLE pgauditDDLExample(id SERIAL);
ALTER TABLE pgauditDDLExample ADD COLUMN name text;
CREATE POLICY namePolicy ON pgauditDDLExample FOR ALL USING (current_user = 'postgres');
DROP POLICY namePolicy on pgauditDDLExample;
DROP TABLE pgauditDDLExample;
</code></pre><p>Finally, check <code>pg_log</code> for the audit entries:<pre><code class=language-bash>2016-09-30 15:07:02.776 UTC postgres postgres LOG: AUDIT: SESSION,2,1,DDL,CREATE TABLE,,,CREATE TABLE pgauditDDLExample(id SERIAL);,&#60none>
2016-09-30 15:08:18.054 UTC postgres postgres LOG: AUDIT: SESSION,3,1,DDL,ALTER TABLE,,,ALTER TABLE pgauditDDLExample ADD COLUMN name text;,&#60none>
2016-09-30 15:09:18.095 UTC postgres postgres LOG: AUDIT: SESSION,4,1,DDL,CREATE POLICY,,,CREATE POLICY namePolicy ON pgauditDDLExample FOR ALL USING (current_user = 'postgres');,&#60none>
2016-09-30 15:09:37.562 UTC postgres postgres LOG: AUDIT: SESSION,5,1,DDL,DROP POLICY,,,DROP POLICY namePolicy on pgauditDDLExample;,&#60none>
2016-09-30 15:09:45.378 UTC postgres postgres LOG: AUDIT: SESSION,6,1,DDL,DROP TABLE,,,DROP TABLE pgauditDDLExample;,&#60none>
</code></pre><h3 id=misc-example><a href=#misc-example>MISC Example</a></h3><p>The last class is <code>MISC</code>, configure pgAudit to audit all classes:<pre><code class=language-pgsql>ALTER SYSTEM SET pgaudit.log TO 'all';
SELECT pg_reload_conf();
</code></pre><p>With pgAudit set to audit all classes, here's a demonstration of the misc class:<pre><code class=language-pgsql>CHECKPOINT;
VACUUM pgauditExample;
</code></pre><p>Finally, check <code>pg_log</code> for the audit entries:<pre><code class=language-bash>$ grep AUDIT postgresql-Fri.log | grep MISC
2016-09-30 15:17:45.214 UTC postgres postgres LOG: AUDIT: SESSION,3,1,MISC,CHECKPOINT,,,CHECKPOINT;,&#60none>
2016-09-30 15:17:47.474 UTC postgres postgres LOG: AUDIT: SESSION,4,1,MISC,VACUUM,,,VACUUM pgauditExample;,&#60none>
</code></pre><h2 id=wrap-up><a href=#wrap-up>Wrap Up</a></h2><p>From the examples above, pgAudit was configured to session audit entire classes of SQL using pgAudit. Session auditing is a great feature, however, a lot of logs can be generated using session auditing. In part 2 of this series on pgAudit, an auditor role will be configured to watch specific objects instead of classes. Stay tuned! ]]></content:encoded>
<category><![CDATA[ Production Postgres ]]></category>
<author><![CDATA[ Jason.O'Donnell@crunchydata.com (Jason O'Donnell) ]]></author>
<dc:creator><![CDATA[ Jason O'Donnell ]]></dc:creator>
<guid isPermalink="false">https://blog.crunchydata.com/blog/pgaudit-auditing-database-operations-part-1</guid>
<pubDate>Mon, 03 Oct 2016 05:00:00 EDT</pubDate>
<dc:date>2016-10-03T09:00:00.000Z</dc:date>
<atom:updated>2016-10-03T09:00:00.000Z</atom:updated></item></channel></rss>