#34200 closed New feature (fixed)
Allow setting postgres role during connection setup
Reported by: | Mike Crute | Owned by: | Mike Crute |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 4.1 |
Severity: | Normal | Keywords: | |
Cc: | Florian Apolloner | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
When attempting to mutate database objects (tables, enumerations, etc...) Postgres requires that the logged-in role match the owning role for those objects. Typically the owning role of an object is the role that created it. This is a problem when using systems such as Hashicorp Vault which create ephemeral single-use roles for applications since those roles will, by definition, never match the creating/owning role. This results in permission errors during structural changes such as migrations. These errors are of the general form "sequence must have same owner as table it is linked to".
The solution to this problem is pretty straightforward. First create a grouping role for the application (roles do not distinguish between being a user or a group) that can become the owner of the database objects. Then create the temporary roles generated by the credential management system as members of this grouping role. Finally, assume that grouping role before performing actions on the database using the SET ROLE statement during connection setup. This will cause all of the temporary roles to act as the grouping role which has ownership of all of these objects and eliminate the permission issues.
I propose adding a PostgreSQL-specific driver option in the settings called role
which, if present, will cause the connection setup code to issue the appropriate SET ROLE
statement at connection creation.
I have a patch for this that I'm testing and will submit soon.
Change History (16)
comment:1 by , 2 years ago
Description: | modified (diff) |
---|
comment:2 by , 2 years ago
Type: | New feature → Bug |
---|
comment:3 by , 2 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
follow-up: 5 comment:4 by , 2 years ago
Cc: | added |
---|---|
Resolution: | → needsinfo |
Status: | assigned → closed |
Type: | Bug → New feature |
follow-up: 8 comment:5 by , 2 years ago
Replying to Mariusz Felisiak:
Thanks for the ticket, however I don't see anything "straightforward" about this solution, it seems complicated and quite niche. All the role-juggling looks like something the DBA should do, not something that framework is responsible for.
Agreed that the role setup should be done by whomever is responsible for managing the database but I do think it's the job of the framework to assume the correct role at runtime. There's no way for an administrator to force an application to assume a role by default in postgres. They can assign a role to a role but it's up to the connection owner to actually assume that role, which has to be done by executing a statement on the newly opened connection.
As for niche: in my experience, using ephemeral credentials that are leased from a (often proprietary) credential management system is pretty common in really large companies that have strong controls around accounts and security. Also this is definitely a problem for anyone using Hashicorp Vault for credential management which I would suggest is not particularly niche.
Also, is it not already possible to
SET ROLE
inDATABASE["OPTIONS"]
? (see docs)
Perhaps I'm missing something but I do not see how this is possible in the current settings. The connection setup logic must execute SET ROLE <role>
against a cursor to set the role. It's not a client option that can just be passed through as far as I'm aware.
comment:6 by , 2 years ago
I've submitted a https://github.com/django/django/pull/16360 pull request for this.
follow-up: 11 comment:7 by , 2 years ago
Resolution: | needsinfo |
---|---|
Status: | closed → new |
Triage Stage: | Unreviewed → Accepted |
I think this makes sense and is hard to achieve otherwise short of overriding the backend. I am wondering if explicitly supporting role makes sense though. Would a more generic approach like init_command
in mysql help? Then again we never had the need for this in postgresql so I rather explicitly support role
(or assume_role
maybe) -- this way we can easily shuffle it around in the backend code if needed.
follow-up: 10 comment:8 by , 2 years ago
Replying to Mike Crute:
Also, is it not already possible to
SET ROLE
inDATABASE["OPTIONS"]
? (see docs)
Perhaps I'm missing something but I do not see how this is possible in the current settings. The connection setup logic must execute
SET ROLE <role>
against a cursor to set the role. It's not a client option that can just be passed through as far as I'm aware.
It's possible to set e.g. search_path
via DATABASE["OPTIONS"]
:
DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "OPTIONS": { "options": "-c search_path=custom_path", }, },
Is it not possible to set ROLE
in the same way?
DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "OPTIONS": { "options": "-c role=custom_role", }, },
comment:9 by , 2 years ago
Ha, I didn't know of options either. To be honest I find it rather ugly, but if it indeed fixes the problem let's add some docs about how to set the role with it and accept the doc fix as a fix for the ticket? On a related note; I am not sure how well -c role=my_role
plays with connection poolers, might be worth to investigate (but if that is broken, setting the search_path
like that is broken as well I guess)
comment:10 by , 2 years ago
Replying to Mariusz Felisiak:
Is it not possible to set
ROLE
in the same way?
Unfortunately this does not work for roles, the postgres docs list all of the parameters supported for this configuration method and roles are not one of them. I'm pretty certain the only way to assume a role is to issue a SET ROLE
statement on the connection.
comment:11 by , 2 years ago
Replying to Florian Apolloner:
I am wondering if explicitly supporting role makes sense though. Would a more generic approach like
init_command
in mysql help? Then again we never had the need for this in postgresql so I rather explicitly supportrole
(orassume_role
maybe) -- this way we can easily shuffle it around in the backend code if needed.
I don't think this is a general concept, it's pretty specific to the postgres security model. I'm happy to call it either role
or assume_role
if you have a preference (I do not). Given that it's pretty postgres specific that would mean it should stay in OPTIONS
right?
follow-up: 13 comment:12 by , 2 years ago
Yes it would probably stay in OPTIONS
and I'd slightly prefer assume_role
since one can login into roles normally and as such might confuse it with USER
.
comment:13 by , 2 years ago
Replying to Florian Apolloner:
I'd slightly prefer
assume_role
since one can login into roles normally and as such might confuse it withUSER
.
I've updated my PR to use assume_role
Thanks for the ticket, however I don't see anything "straightforward" about this solution, it seems complicated and quite niche. All the role-juggling looks like something the DBA should do, not something that framework is responsible for. Also, is it not already possible to
SET ROLE
inDATABASE["OPTIONS"]
? (see docs)