I'm using postgreSQL as a database for an application that I build. When I run the application, I open dbeaver and typed the SQL query:
select * from pg_stat_activity
in order to see a list of SQL queries that has been processed during the application running.
In "query" column I got "insufficient privilege" for some queries. Do you know what I can do to see the actual SQL query?
You would have to run the query as a root-user to see all the queries. This is usually the postgres user.
On certain managed postgresql services (AWS), access to the root-user is restricted, and it is not possible to see queries executed by that user by design.
In general, the more privileges your user has, the more queries you can see in pg_stat_activity
If you are using Postgresql 10 and above, you can grant pg_read_all_stats role to your user from superuser account (postgres)
GRANT pg_read_all_stats TO username;
You do not necessarily have to run the query as a root-user. Giving someone root user access could have very bad security implications, as they could literally do anything. There is a workaround where you can create a function as root that has access to pg_stat_statements, and then grant the read-only user access to the function. For example:
CREATE OR REPLACE FUNCTION your_schema.get_stat_statements(showtext boolean = true) RETURNS SETOF pg_stat_statements AS
$$
/* pganalyze-collector */ SELECT * FROM public.pg_stat_statements(showtext) LIMIT 100;
$$ LANGUAGE sql VOLATILE SECURITY DEFINER;
GRANT USAGE on SCHEMA your_schema TO your_readonly_user;
GRANT EXECUTE on FUNCTION your_schema.get_stat_statements(boolean) TO your_readonly_user;
Thanks to https://pganalyze.com/docs/install/amazon_rds/02_create_monitoring_user
You can find an example of a similar approach here.
One possible workaround to this is defining a MATERIALIZED VIEW using a user that has enough privileges to view the data, and granting access to the view to the use that does not have the privileges.
One downside to this is that the view will have to be refreshed periodically using REFRESH MATERIALIZED VIEW, also by the owner of the view (or some other user with privileges).
Related
Using Vault I'm trying to create an on-demand temporary read-only users in a Postgres 11.8 instance.
I will have:
1000+ databases (one per customer aka tenant)
Each database has a public and reporting schema.
So I'm trying to find a way to grant this read-only user access to every database, and on all tables in both schemas.
While I came up with the following snippet:
-- Create a new user
CREATE ROLE "my-readonly-user" WITH LOGIN PASSWORD 'test123';
-- Grant access to the two schema's we have
GRANT USAGE ON SCHEMA public TO "my-readonly-user";
GRANT USAGE ON SCHEMA reporting TO "my-readonly-user";
-- Grant access to all tables in our two schema's
GRANT SELECT ON ALL TABLES IN SCHEMA public TO "my-readonly-user";
GRANT SELECT ON ALL TABLES IN SCHEMA reporting TO "my-readonly-user";
-- Grant access to sequences
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO "my-readonly-user";
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA reporting TO "my-readonly-user";
-- Grant access to future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "my-readonly-user";
ALTER DEFAULT PRIVILEGES IN SCHEMA reporting GRANT SELECT ON TABLES TO "my-readonly-user";
It will only apply to 1 single database (the current one). If I login with the readonly user and switch to some databases, I don't see any tables except in the first database.
A cluster holds many databases, which hold many schemas. Schemas (even
with the same name) in different DBs are unrelated. Granting
privileges for a schema only applies to this particular schema in the
current DB (the current DB at the time of granting).
https://stackoverflow.com/a/24923877/1409047
Because of that limitation, it seems to make the grant snippet from above more complex. Should I iterate all my databases somehow and run the snippet? How would I do the database switch? Is it even possible in plain SQL (as required by Vault's API)? Anyone did this before?
Note: doing the same in MySQL only requires 2 lines of code, using wildcards *.* which is not supported in Postgres afaik:
CREATE USER '{{name}}'#'10.0.0.0/255.0.0.0' IDENTIFIED BY '{{password}}';
GRANT SELECT, SHOW DATABASES, SHOW VIEW ON *.* TO '{{name}}'#'10.0.0.0/255.0.0.0';
It is a deliberate design decision that an SQL statement cannot affect objects in a database different from the one that you are connected to.
Yes, you will have to iterate through all databases in your cluster and run your script there.
Note that there is a mistake in your script: you shouldn't grant a read-only user USAGE on sequences, else they can modify the sequence value. SELECT is fine.
What I would do is create a read_only_group (with NOLOGIN) and grant all these permissions to that role. Then, when there is a request for a read-only user, create a user and add it to that group, so that it inherits the group privileges. Don't grant anything to the user itself, so that you can easily DROP it when it is no longer needed.
If you want to grant readonly access to an user using only a Postgres script, you can do that:
CREATE EXTENSION IF NOT EXISTS dblink;
DO
$$
DECLARE nome_banco TEXT;
DECLARE template_conexao TEXT;
DECLARE string_conexao TEXT;
DECLARE nome_usuario TEXT;
BEGIN
template_conexao = 'user=foo password=bar dbname=';
nome_usuario = 'baz';
FOR nome_banco IN
SELECT datname FROM pg_database
WHERE datistemplate = false
LOOP
string_conexao = template_conexao || nome_banco;
perform dblink_exec(string_conexao, 'GRANT CONNECT ON DATABASE "' || nome_banco || '" TO ' || nome_usuario);
perform dblink_exec(string_conexao, 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO ' || nome_usuario);
END LOOP;
END
$$
--DROP EXTENSION IF EXISTS dblink;
It connects to each database, and executes the script; it can be easily adapted for other situations where you need to execute a database-local command on all of the databases.
Remember that, for security purposes, after executing the script above, you should drop the dblink extension created by the script above, unless, of course, you are already using the extension for other purposes.
So I am having an issue that I wanted to clear up. I am granting access to a user in postgres, but I only want that user to be able to execute functions.
I have the following code:
GRANT USAGE ON SCHEMA not_public TO test_id;
GRANT USAGE ON SCHEMA public TO test_id;
GRANT EXECUTE ON FUNCTION testFunction TO web_reporting_id;
When this is ran, though, and I try to run the function under that user, I get a permissions error on the first table the function tries to read from.
I know some db languages you only need to grant permissions for the functions/procedures and not the underlying objects. In postgres, do I need to grant permissions on the tables too? Or do I need to update my grant scripts?
Thanks!
Normally, functions run with the privileges of the user calling them, so all SQL statements in them will be executed by that user.
You could define the function as SECURITY DEFINER to have it run with the privileges of the function owner, but then you must use the SET clause of CREATE FUNCTION to fix the search_path for the duration of the function execution for security reasons.
Also note that by default, everybody (PUBLIC) has execute privileges on functions, so you might want to revoke that.
I'm building an app using AWS RDS PostgreSQL.
I need to allow one group to use CREATE, INSERT INTO and UPDATE statements, but at the same time not allow them to use DROP TABLE or DROP DATABASE.
Is there a way to do this on the database layer, as I would prefer not to do it on server-side layer? Thank you.
You can create one privileged user that can do all these things – let's call it creator – and a normal user luser that is used to log into the database.
creator then defines PL/pgSQL functions with SECURITY DEFINER that perform the required CREATE statements (probably using dynamic SQL with an EXECUTE statement).
Then creator runs
REVOKE EXECUTE ON FUNCTION ... FROM PUBLIC;
GRANT EXECUTE ON FUNCTION ... TO luser;
so that luser can execute the functions.
INSERT and UPDATE privileges are granted to luser inside the functions.
Documentation links:
Language PL/pgSQL to write functions in PostgreSQL
Executing dynamic statements in PL/pgSQL
CREATE FUNCTION SQL sommand
Privileges in PostgreSQL
I have streaming replication which I need to monitor. So there is a special user for Zabbix. I don't want to use pg_mongz and decided to set my own queries to pg_catalog schema's view pg_stat_replication to get replication state.
When I use query:
select *
from pg_stat_replication;
it returns replication state record for admin. But when I logged in as monitoring user it returns just:
pid, usesysid, usename, application_name
So such parameters as client_addr, client_hostname, client_port, backend_start, state, sent_location, write_location, etc. are empty.
First I granted rights to my user on schema and tables:
grant usage on schema pg_catalog to usrmonitor;
grant select on all tables in schema pg_catalog to usrmonitor;
but it didn't help. When I looked at view I found that query uses functions and granted execution:
grant execute on function pg_stat_get_wal_senders() to usrmonitor;
grant execute on function pg_stat_get_activity(integer) to usrmonitor;
But the select query still returns empty columns. What maybe the problem?
Since PostgreSQL 10, it is as simple as :
GRANT pg_monitor TO monitoring_user;
(Source: https://pganalyze.com/blog/whats-new-in-postgres-10-monitoring-improvements)
Yes, access to these fields is intentionally restricted to superusers.
As a workaround, you may use a function as a proxy with the SECURITY DEFINER attribute:
SECURITY DEFINER specifies that the function is to be executed with
the privileges of the user that created it.
So as a superuser (typically the postgres user), do:
CREATE FUNCTION func_stat_replication() RETURNS SETOF pg_stat_replication as
$$ select * from pg_stat_replication; $$
LANGUAGE sql SECURITY DEFINER;
Then revoke/grant the permission to use that function so that only the monitoring
user is allowed to execute it:
REVOKE EXECUTE ON FUNCTION func_stat_replication() FROM public;
GRANT EXECUTE ON FUNCTION func_stat_replication() to usrmonitor;
Then usrmonitor should execute:
SELECT * FROM func_stat_replication();
and it will have the same results as if it was superuser.
I want to create some roles in my Postgresql DB and grant some access.
I have student role and i want to grant this user type : can edit only record a bout him/her in student table and can not edit other rows
how can i do it?
thanks
Create a view on the table with an appropriate where clause, then grant access to that:
create view students_view as
select col1, col2, col3 -- limit column access here
from mytable
where <whatever>; -- limit row access here
-- limit what he can do here
grant update, select to student_role;
BTW It is a commonly held misconception that you can't update a view, but that is only true if the view is a join or similarly complicated query.
PostgreSQL doesn't have row-level declarative security (yet, there's ongoing work into it) so if you can't just create a view - say, if you have many different people who need this access - you will probably need a SECURITY DEFINER helper function or trigger.
You've got a couple of options:
Write a SECURITY DEFINER function that lets them make only the permitted changes and limit their access to the table to SELECT, revoking UPDATE, DELETE, TRUNCATE and INSERT rights; or
write a trigger that tries to restrict them from making changes you don't want them to make and GRANT them write access to the table.
Of the two, the function and restricted rights approach is by far the safest option so long as you follow the SECURITY DEFINER secure coding guidelines set above - setting search_path for the function, avoiding dynamic SQL (EXECUTE) with string substitutions, etc.
The view approach given above can work quite nicely if it's a view that filters by current_user. You may also want to look at the new SECURITY BARRIER views; see this post for a useful discussion of them.
GRANT UPDATE(column) on tabela to user_name;