Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow postgres user to only list his own database

Tags:

postgresql

I'm using a postgresql server and I want to forbid my users to see what other databases are on the same server.

Essentially a \l should only list his own database.

I'm pretty sure that there is a right which I need to revoke from the user but I can't find it in the docs.

like image 410
Kai Avatar asked Feb 07 '11 01:02

Kai


People also ask

Does Postgres user have access to all databases?

Yes: When you create any new DB User in PostgreSQL, It has a default CONNECT privileges. But It cannot access any Table or data of Databases, yes It can create new Table in any Database. To prevent a new User for connecting any existing Database, we should run REVOKE command on particular User or Role.

What is Postgres single user mode?

In the single-user mode, the session user will be set to the user with ID 1, and implicit superuser powers are granted to this user. This user does not actually have to exist, so the single-user mode can be used to manually recover from certain kinds of accidental damage to the system catalogs.


3 Answers

This seems to work but might have unforeseen consequences. It requires tinkering with system catalogues, which isn't really a good idea!

First off, you have to permit superusers to update system catalogues by adding this to your postgresql config:

allow_system_table_mods = on

and restart.

Now, you can use DDL statements to modify system catalogues (you should be afraid). Connect to one of the user databases (a test one would be a good idea) and:

alter table pg_catalog.pg_database rename to pg_database_catalog;
create view pg_catalog.pg_database as
  select oid, 1262::oid as tableoid, pg_database_catalog.*
  from pg_catalog.pg_database_catalog
  where has_database_privilege(pg_database_catalog.oid, 'connect');    
grant select on pg_catalog.pg_database to public;

You should now find that if you connect to that database as a low-priv user, the \l command will just list the databases that that user can connect to.

The problem is you now need to guess which database the users connect to initially to fetch their database list from. If they connect to their own database initially, then you're probably done at this point. If they connect to postgres or template1 first, then you need to make this change on that database instead.

It seems to me that this should work, since the pg_database catalog is referred to by postgres backends directly by oid, rather than by name, so moving it out of the way and changing which rows are shown in it should be invisible to them. In particular, you can't stop the server distinguishing to the user between a database not existing and them not having connection privilege.

I'm not going to make any promises that this sort of change doesn't screw something else up down the line. If it breaks, you get to keep the pieces.

You probably want to make this change in a template database, and create user databases from that in future, and deactivate the allow_system_table_mods setting when you're done (which requires a server restart, remember).

Also, I tested this on 9.0: it seems to me it should work on some earlier versions too, caveat emptor.

like image 93
araqnid Avatar answered Nov 12 '22 13:11

araqnid


There's no such setting in pgsql. There are settings to prevent users from connecting to databases that they shouldn't (grant / revoke connect). Being able to see there's a database is no big deal. Being able to connect / have edit rights etc. is.

like image 36
Scott Marlowe Avatar answered Nov 12 '22 13:11

Scott Marlowe


I would imagine this might have negative repercussions for the user, such as not being able to connect to the database since the system does not have access to the sytem tables, not sure though. But as far as figuring out what table to revoke - this a good general way to see what the psql meta commands are doing:

To see what \l is doing you can also use the -E flag from the command line with psql.

~$ psql -E -c '\l'
********* QUERY **********
SELECT d.datname as "Name",
       pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
       pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
       d.datcollate as "Collation",
       d.datctype as "Ctype",
       pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_database d
ORDER BY 1;
**************************

So if the user does not have access to pg_database they will not be able to use the \l command.

like image 21
nate c Avatar answered Nov 12 '22 14:11

nate c