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 rev开发者_运维知识库oke from the user but I can't find it in the docs.
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.
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.
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.
@araqnid 's answer above seems to be the way to go except for one problem: select oid, 1262::oid as tableoid, pg_database_catalog.*
will have the oid
column defined twice in its results, once as expicitly given via select oid
and once taken from pg_database_catalog.*
. At least on Postgresql 12 create view pg_catalog.pg_database
will complain that the column oid
is being defined twice and will abort.
Thus the corrected code would be:
alter table pg_catalog.pg_database rename to pg_database_catalog;
create view pg_catalog.pg_database as
select 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;
Please refer to the original answer for all other information.
I'd be glad if somebody could confirm that my findings here are correct (or refute them).
精彩评论