Question :
The Problem
I am trying to setup Hashicorp vault and Postgres in Google Cloud.
I am using liquibase to manage the schema and when that does a migration, it retrieves a username/password from vault and then runs under that user to perform the migration.
However, the default postgres user, or any other user that tries to use that table can’t and receives the ERROR: permission denied for relation
.
If I understand correctly, the users are being created while logged into as the role of postgres
. This all works locally but the google cloud stuff seems to be setup in a vastly different way.
Here are some outputs to see what the database is setup as:
du
List of roles
Role name | Attributes | Member of
----------------------------------+------------------------------------
------------------------+---------------------
cloudsqladmin | Superuser, Create role, Create DB,
Replication, Bypass RLS | {}
cloudsqlagent | Create role, Create DB
| {cloudsqlsuperuser}
cloudsqlreplica | Replication
| {}
cloudsqlsuperuser | Create role, Create DB
| {}
postgres | Create role, Create DB
| {cloudsqlsuperuser}
test | Create role, Create DB
| {cloudsqlsuperuser}
v-token-power-watc-p079t4r13s85w | Password valid until
| {cloudsqlsuperuser}
dt
List of relations
Schema | Name | Type | Owner
--------+-----------------------+-------+------------------------------
public | databasechangelog | table | v-token-power-watc-p079t4r13s85w
public | databasechangeloglock | table | v-token-power-watc-p079t4r13s85w
public | test | table | v-token-power-watc-p079t4r13s85w
The vault creation SQL looks something like this:
CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{{name}}";
GRANT cloudsqlsuperuser to "{{name}}";"
I’ve tried playing with the roles in a number of ways but it seems all of the new roles that I create are always put into their own sandbox and can’t play with any other roles. I’m at a lost at this point of exactly what to do next, it seems that no matter the grant
options I can pass, it doens’t help.
Current Questions
Is it that the owner needs to be the cloudsqlsuperuser group instead of the temp ownwer?
Is there a way to make that the default instead of having to make sure all my tables are created with the right owner?
Replicate with a local docker instance
Docker Command to start Postgres Database
docker run --rm --name database -v $(pwd)/setup.sql:/docker-entrypoint-initdb.d/setup.sql -e POSTGRES_USER=temp -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=mydb postgres:9.6
Contents of setup.sql
ALTER ROLE postgres RENAME TO cloudsqladmin;
CREATE ROLE cloudsqlsuperuser WITH CREATEDB CREATEROLE;
ALTER DATABASE mydb OWNER TO cloudsqlsuperuser;
CREATE ROLE "postgres" WITH LOGIN CREATEDB CREATEROLE IN ROLE cloudsqlsuperuser;
Creating user under postgres simulating vault user creation
docker exec -ti database psql -U postgres -d mydb -c "CREATE ROLE testuser WITH LOGIN IN ROLE cloudsqlsuperuser"
Create test table from testuser
docker exec -ti database psql -U testuser -d mydb -c "CREATE TABLE test (col1 text)"
Try and select table demonstrating error
docker exec -ti database psql -U postgres -d mydb -c "SELECT * FROM test"
Answer :
Short Summary
You have to run an REASSIGN OWNED BY "$VAULT_USERNAME" TO "cloudsqlsuperuser"
after the table/sequence/function creation process to reassign back to a the common role.
Longer Answer
Postgres 9.6 CREATE TABLE
will by default create the table under the user’s role. Because the vault user is designed to be a temporary credential, we need to also make sure that the objects created under the temporary roles are reassigned back to a common role of some kind. In the specific case I outlined in Google Cloud, we are going to reassign back to cloudsqlsuperuser
.
The one command I found to work is the REASSIGN OWNED BY current_user to "cloudsqlsuperuser"
command that will then take all of the objects owned by that temporary user and assign back to that common role.
Bonus: liquibase config
I am using liquibase to do my migrations and this is in my change log to fix the permissions everytime we run a migration:
<databaseChangeLog>
<changeSet author="elindblom" id="fix-permissions" context="" logicalFilePath="fix-permissions" runAlways="true" runOrder="last">
<sqlFile path="baselines/sql/fix-permissions.sql" splitStatements="false" relativeToChangelogFile="true" stripComments="false"/>
</changeSet>
... rest of your change logs after ...
</databaseChangeLog>
The SQL looks like this:
REASSIGN OWNED BY current_user TO "cloudsqlsuperuser";
The changeSet
runs after everything comes to completion and runs every-time. The very important bits are the runAlways
and runOrder
elements.