EnableInPostgreSQL creates the PgBouncer user, schema, and SECURITY DEFINER function that allows it to authenticate clients using their password stored in PostgreSQL.
( ctx context.Context, exec postgres.Executor, clusterSecret *corev1.Secret, )
| 122 | // function that allows it to authenticate clients using their password stored |
| 123 | // in PostgreSQL. |
| 124 | func EnableInPostgreSQL( |
| 125 | ctx context.Context, exec postgres.Executor, clusterSecret *corev1.Secret, |
| 126 | ) error { |
| 127 | log := logging.FromContext(ctx) |
| 128 | |
| 129 | stdout, stderr, err := exec.ExecInAllDatabases(ctx, |
| 130 | strings.Join([]string{ |
| 131 | // Quiet NOTICE messages from IF NOT EXISTS statements. |
| 132 | // - https://www.postgresql.org/docs/current/runtime-config-client.html |
| 133 | `SET client_min_messages = WARNING;`, |
| 134 | |
| 135 | // Do not wait for changes to be replicated. [Since PostgreSQL v9.1] |
| 136 | // - https://www.postgresql.org/docs/current/runtime-config-wal.html |
| 137 | `SET synchronous_commit = LOCAL;`, |
| 138 | |
| 139 | // Create the following objects in a transaction so that permissions |
| 140 | // are correct before any other session sees them. |
| 141 | // - https://www.postgresql.org/docs/current/ddl-priv.html |
| 142 | `BEGIN;`, |
| 143 | |
| 144 | // Create the PgBouncer user if it does not already exist. |
| 145 | // Permissions are granted later. |
| 146 | strings.TrimSpace(` |
| 147 | SELECT pg_catalog.format('CREATE ROLE %I NOLOGIN', :'username') |
| 148 | WHERE NOT EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = :'username') |
| 149 | \gexec`), |
| 150 | |
| 151 | // Ensure the user can only access the one schema. Revoke anything |
| 152 | // that might have been granted on other schemas, like "public". |
| 153 | strings.TrimSpace(` |
| 154 | SELECT pg_catalog.format('REVOKE ALL PRIVILEGES ON SCHEMA %I FROM %I', nspname, :'username') |
| 155 | FROM pg_catalog.pg_namespace |
| 156 | WHERE pg_catalog.has_schema_privilege(:'username', oid, 'CREATE, USAGE') |
| 157 | AND nspname NOT IN ('pg_catalog', :'namespace') |
| 158 | \gexec`), |
| 159 | |
| 160 | // Create the one schema and lock it down. Only the one user is |
| 161 | // allowed to use it. |
| 162 | strings.TrimSpace(` |
| 163 | CREATE SCHEMA IF NOT EXISTS :"namespace"; |
| 164 | REVOKE ALL PRIVILEGES |
| 165 | ON SCHEMA :"namespace" FROM PUBLIC, :"username"; |
| 166 | GRANT USAGE |
| 167 | ON SCHEMA :"namespace" TO :"username";`), |
| 168 | |
| 169 | // The "get_auth" function returns the appropriate credentials for |
| 170 | // a user's password-based authentication and works with PgBouncer's |
| 171 | // "auth_query" setting. Only the one user is allowed to execute it. |
| 172 | // - https://www.pgbouncer.org/config.html#auth_query |
| 173 | sqlAuthenticationQuery(`:"namespace".get_auth`), |
| 174 | strings.TrimSpace(` |
| 175 | REVOKE ALL PRIVILEGES |
| 176 | ON FUNCTION :"namespace".get_auth(username TEXT) FROM PUBLIC, :"username"; |
| 177 | GRANT EXECUTE |
| 178 | ON FUNCTION :"namespace".get_auth(username TEXT) TO :"username";`), |
| 179 | |
| 180 | // Remove "public" from the PgBouncer user's search_path. |
| 181 | // - https://www.postgresql.org/docs/current/perm-functions.html |