Allow postgres user to only list his own database - 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.

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).

Related

Postgres on Linux, Need passwordless way of granting access to a table

Hi: I have an interesting problem that needs a clever solution :-)
PG 9.6.5 on RHEL6_64
Constraints I have to live with:
- I'm not a superuser, but I do have createrole.
- Password is required for login
Let's say I have a table called "approvals"...
create table approvals (approval_id text);
And another table called "granters"...
create table granters (linux_uid text);
I need a perl/DBI script that must allow all users select, but only those linux_uids in the "granters" table can insert/update/delete into the "approvals" table. But when someone who is in the "granters" table runs the perl script, I do not want them to have to enter a password. The perl script can validate that an authorized user is running it using 'getlogin' and that's good enough.
I can create roles for the granters and grant them select/insert/update/delete on the "approvals" table, but they'd have to enter a password to log in first (one of the constraints on the DB is that pawwsords are required). So I can't see how this traditional method would work.
I could create before insert, update and delete triggers that can check the id of the person running the perl script against the list of uids in the "granters" table, then allow/reject the attempt on that basis. But I can't pass the linux uid into the stored procedure associated with the trigger(s). Something like perlplu might allow me to sniff out the linux uid, but again, I'm not a superuser, so (I believe) I can't create something like that.
Any ideas ?
Thanks in Advance !
Users are authenticated before any permissions inside the database are checked, so you cannot do that just within the database. Also, since you need to do something outside the database, you need superuser privileges.
First, if I were you, I'd rethink that requirement. It looks complicated and cumbersome; there should be a better solution.
You could use trust or ident authentication for password-less authentication, but you cannot make that dependent on the contents of a table in the database. It is governed by the pg_hba.conf file.
So what you'd have to do is to create a trigger on the table that runs with superuser privileges through SECURITY DEFINER and edits (and reloads!) pg_hba.conf whenever the table is modified. This is complicated, error-prone and dangerous.

PostgreSQL Revoke All Permissions from All Users and Roles

I have several tables in Postgres 9.2.23 that I need to update privileges on regularly (daily or multiple times per day). I have a master list maintained elsewhere of who can select that I use to grant privileges. However, I want to make sure that anyone who used to be on the list but has dropped off has all their privileges revoked.
My thought was to do a clean sweep with something like:
revoke all privileges on my_table from [all users/roles]
before I grant select to the authorized users. However, I have not been able to figure out how to do a bulk revoke on all users/roles like that.
I know I can generate a list of users/roles that currently have privileges and go from there, but I am hoping there is a shortcut to bypass this.
There is a canonical answer to this provided by postgres documentation - also related to this post. I had to do a lot of trial and error myself to understand why there isn't a simple REVOKE ALL FROM role solution and the reason comes down to the answer:
REASSIGN OWNED BY doomed_role to surviving_role;
DROP OWNED BY doomed_role;
The reason (or so I have gathered) why this is the solution and you cannot just revoke all permissions, is that the "doomed_role" may be the owner of some entities and therefore they need to be reassigned first before revoking their permissions. So long as you do these commands in this order, it will do what you need to do.
There is a way, but it is not supported and dangerous, because it directly updates the system catalogs.
As superuser, you can do the following:
UPDATE pg_catalog.pg_class
SET relacl = NULL
WHERE oid = 'my_table'::regclass;
That resets the permissions on the object.
The problem is that this leaves some orphaned entries in pg_shdepend, where (among others) dependencies between users and tables are stored, so that you cannot drop a user that still has permissions on an object.
If I made no mistake, you can delete them as follows:
DELETE FROM pg_catalog.pg_shdepend sd
USING pg_catalog.pg_database d, pg_catalog.pg_authid a
WHERE sd.dbid = d.oid
AND sd.refobjid = a.oid
AND d.datname = current_database()
AND sd.classid = 'pg_class'::regclass
AND sd.refclassid = 'pg_authid'::regclass
AND sd.objid = 'my_table'::regclass
AND deptype = 'a';
If that seems too dangerous for you, right you are.
It is better to write a PL/pgSQL procedure that reads out pg_class.relacl, uses aclexplode to get the entries, loops though them and issues individual REVOKE statements.
Or simpler, just loop through all roles and revoke all privileges for everyone except for the owner.
You could safe yourself the whole mess if you don't grant privileges directly to the users, but use roles. Grant privileges to objects to the roles, and assign roles to the users.
That way it is much simpler to manage privileges.

Best way to change the owner of a PostgreSQL database and their tables?

I am trying to change the owner of a PostgreSQL database (version > 8.2) and its tables.
I read this solution:
Modify OWNER on all tables simultaneously in PostgreSQL
But is this the best way to do it (for recent versions of PostgreSQL)?. It seems that there is a function REASSIGN OWNED which is better, but this one changes every database owned by the old_role, doesn't it? I only want it for one database.
Like this post:
REASSIGN OWNED BY for 1 specified database
I am not going to change the owner postgres, which is the best way nowadays?
Thank you in advance
According to the manual:
Because REASSIGN OWNED does not affect objects within other databases, it is usually necessary to execute this command in each database
which would seem to meet your requirements, although it also says the command would affect table spaces (which are not specific to the current database).
The second SO answer you linked applies to the special case of the postgres user, which owns the system catalogs. You cannot change the ownership of these.
The two methods that spring to mind for me are:
1) First alter the database name, and then perhaps right a quick script which changes the owner on the current tables to the old tables.
ALTER DATABASE <dbname> OWNER TO <newowner>;
\o altertable.sql
SELECT 'ALTER TABLE ' || table_name || ' OWNER TO <newowner>; ' FROM information_schema WHERE table_schema = <oldowner> and table_catalog = '<dbname>';
\o
\i altertable.sql
The above will generate your commands, then just pop them into a file and execute it.
2) The other option would be to use pg_dump to dump your database in plain text mode and then alter the appropriate strings using search and replace:
pg_dump -Fp -f dbbackup.dmp <dbname>
vi dbbackup.dmp
:%s/oldowner/newowner/g
save and exit
Hope this helps.

What select statement can I use to determine 1) the roles that a user belongs to and 2) the tables that a user has access to using PostgreSQL?

Using psql, \dg and \du tells me the roles that each user (role) belongs to. I want to determine this programmatically for a single user but cannot find which system tables are used to generate the results returned for \dg.
Will I need special privileges to execute this query?
As a related question, I want to determine what tables a role can update.
I've read the documentation on CREATE ROLE and GRANT and surrounding documentation which tell me how to set up roles and privileges, but not how to test for role membership or table access privileges.
There are built-in system information functions for both. See pg_has_role, etc.
You can also query the information_schema to get role membership data, in particular information_schema.applicable_roles, information_schema.enabled_roles and information_schema.administrable_role_authorizations.
Finally, for any psql \d command you can find out what exactly psql is doing to get the information by running psql with the -E option to print the sql it runs. I don't recommend using psql's sql when there's a builtin function or an information_schema view for the same information though. psql's approach may be faster but it's also more PostgreSQL-version-specific and may not work with PostgreSQL versions other than the one you're running. In the case of \du and \dg psql will be querying pg_catalog.pg_roles as part of a join against other tables. It's much simpler and more portable to use the information_schema views instead.
This really should be the documentation entries on role membership; I'll post a patch that adds appropriate cross-references.

PostgreSQL: making a schema restricted/unchangable?

We like our production environment with a restricted/unchangable schema -- the development side can be owned by the developers and changed as they like -- and we like to vet changes as they are promoted.
I'm wondering if this may be a solution to making that happen:
postgres% create proddb with owner=postgres;
unixside% pg_restore --dbname=devdb [--schema-only] --no-owner proddb
/* grants to users on schema objects appear to remain intact */
/* here's the magic, I hope... */
postgres% revoke create on schema public from public;
postgres% grant usage on schema public to produser(s);
Some testing seems to show that a user in this new proddb can interact with tables normally (with appropriate grants) and cannot alter the schema (alter table, create table, drop table, etc). But I'm paranoid and very new to Postgres, so...
Q: Is this correct?
Q: Am I missing anything?
Thanks muchly.
Yes, that is correct. The only addition is that the owner of a table can always delete or modify it. So it may not work if you have existing tables in the schema.
Discovered a missing element: sequences.
The user was finding errors in his scripts; similar errors appeared in the logs:
ERROR: permission denied for sequence <sequence>
The production schema showed that although sequences were created, they were owned by postgres and no explicit grants were given to the users. As per the GRANT documentation:
Granting permission on a table does not automatically extend permissions to any sequences used by the table, including sequences tied to SERIAL columns. Permissions on sequence must be set separately.
Our fix (verbose for this demonstration) was to find all sequences:
unixside% pg_dump --schema-only proddb > proddb.schema
unixside% grep -i 'create sequence' proddb.schema
...and apply appropriate grants (select to prevent table scans, update to prevent the above errors):
postgres% grant select,update on <sequence> to produser(s);
So far, the user says it's working and errors to the log have stopped...
I forget what version PostgreSQL added the syntax, but one of the easiest ways to administer permissions in PostgreSQL is through the "GRANT foo, priv ON ALL something IN SCHEMA" syntax.
BEGIN;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA my_schema TO my_role;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA my_schema TO my_role;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA my_schema TO my_role;
COMMIT;
Very handy for making sure that permissions are always set correctly.
The EXECUTE for FUNCTIONS may seem spooky, but shouldn't be unless your functions were created with the SECURITY DEFINER attribute (and if you are using SECURITY DEFINER, you'd better be cautious since you're playing around with the PostgreSQL version of a "setuid" function). If you space out your TABLES across different SCHEMAS based on the expected permissions, then this becomes a pretty handy convention when used with the search_path variable.
ALTER ROLE my_role SET search_path = my_schema, auth_schema, public;
-- Avoid using the public schema (pretty please)
Where auth_schema has a collection of tables that my_role shouldn't have direct read or write privileges on. Assigning privs to GROUPS is also useful.
Here are some relevant docs:
http://developer.postgresql.org/pgdocs/postgres/sql-grant.html
Don't forget you can use "\h GRANT" in psql to easily figure out the syntax or remember what can be done on all objects in a schema (search for "IN SCHEMA").