Can't get access to streaming replication stats in PostgreSQL - 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.

Related

Execute Permission on Function not Enough in PostgreSQL?

New to Pg from MS SQL side where to restrict access simply grant EXE permission to Functions and SPs. So created a user/role, set its search_path to a dedicated schema of a database, grant EXECUTE ON ALL FUNCTIONS IN SCHEMA myschema. Tried execute a function got
permission denied for schema myschema
Ok, grant usage on schema myschema to role. The function does a select ... from mytable so now
permission denied for table mytable
To grant SELECT on my table? Wait, purpose of this function is to restrict the role from exploring tables.
Your situation is: User a owns a table mytable in a schema myschema. User b initially has no permissions on either. Now you want to allow b limited access to mytable. Granting SELECT on the table would be too much — you want to grant access only through a special function myfunction.
Then you need a function that does not run with the permissions of the caller (SECURITY INVOKER), which would be the default, but with the permissions of the function owner (SECURITY DEFINER). Then user a should run:
CREATE FUNCTION public.read_mytable(...) RETURNS ...
LANGUAGE ...
/* runs with the privileges of the owner */
SECURITY DEFINER
/* important: force "search_path" to a fixed order */
SET search_path = pg_catalog,pg_temp
AS $$...$$;
/* by default, everybody can execute a function */
REVOKE EXECUTE ON FUNCTION public.read_mytable FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.read_mytable TO b;
Note that I created the function in schema public, to which b has access (don't forget to REVOKE CREATE ON SCHEMA public FROM PUBLIC;!).
Setting a search_path for user b is not enough, since this can always be changed dynamically with the SET command. You don't want b to run a privilege escalation attack.

Create readonly user on all 1000 postgres databases and schema's

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.

PostgreSQL: insufficient privilege

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

Granting SELECT on all tables to a user in Firebird 2.1

I've added a user to a Firebird 2.1 instance using gsec, but now I wanted to grant SELECT on all tables to this new user. I could find how to grant this permission on specific tables, but not to them all:
GRANT SELECT ON TABLE table TO USER user;
If I try to use the new user I get the following error on isql:
no permission for read/select access to TABLE table
Is there a way to do that on Firebird 2.1?
Something like this:
EXECUTE BLOCK
AS
DECLARE VARIABLE tablename VARCHAR(32);
BEGIN
FOR SELECT rdb$relation_name
FROM rdb$relations
WHERE rdb$view_blr IS NULL
AND (rdb$system_flag IS NULL OR rdb$system_flag = 0)
INTO :tablename DO
BEGIN
EXECUTE STATEMENT ('GRANT SELECT ON TABLE ' || :tablename || ' TO USER TEST');
END
END
I could find how to grant this permission on specific tables, but not to them all
You can grant specific privileges to all users, already existing and yet to be created. That is made by granting privilege to "PUBLIC" pseudo-user. Or you could grant privileges to some ROLE, then grant this ROLE to given users, then during database connection specify the option, that the user impersonate this ROLE in this session.
However there is no syntax to grant privileges to all generators or all procedures or all views or all tables, etc.
See the GRANT statement syntax in documentation.
What you can do though, is creating a simple script (anonymous and volatile aka EXECUTE BLOCK or persistent and named as STORED PROCEDURE) and that script of yours would be querying system tables to list all non-system ( COALESCE(RDB$SYSTEM_FLAG,0)=0 ) tables (together with views, or tables but not views: check if RDB$RELATION_TYPE is 0 or is 0 or 1) and then form a sequence of GRANT statements and execute them one by one.
See documentation about EXECUTE BLOCK and FOR SELECT ... INTO ... DO ... and EXECUTE STATEMENT ( or maybe FOR EXECUTE STATEMENT also, but I do not think it already was there in FB 2.1. There is unofficial wiki tracking which statements were available since which FB versions, but it is not thorough and may contain errors: http://firebirdsql.su/doku.php ).
https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-security-privs.html#fblangref25-security-privs-grant
https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref-appx04-relations.html
https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql-coding.html#fblangref25-psql-execstmt
https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-dml-execblock.html

Postgresql ignores lack of EXECUTE permission if USAGE permission is granted on containing schema

From http://www.postgresql.org/docs/9.1/static/sql-grant.html:
USAGE
...
For schemas, allows access to objects contained in the specified schema (assuming that the objects' own privilege requirements are also met). Essentially this allows the grantee to "look up" objects within the schema. Without this permission, it is still possible to see the object names, e.g. by querying the system tables. ...
...
However, running the following script through psql seems to show that although the lack of a SELECT permission causes an error, lack on an EXECUTE permission does not, which contradicts the documentation as the 'own privilege requirement' is not met.
CREATE DATABASE testdb WITH OWNER postgres ENCODING 'UTF8';
\connect testdb
CREATE ROLE testrole;
CREATE SCHEMA testschema;
GRANT USAGE ON SCHEMA testschema TO testrole;
SET search_path TO testschema;
CREATE FUNCTION testfunc ()
RETURNS VOID
AS $$
BEGIN
RAISE NOTICE 'IN TESTFUNC';
RAISE NOTICE 'Current user: %', current_user;
END;
$$
LANGUAGE plpgsql;
CREATE TABLE testtable
(
testrow INT
);
INSERT INTO testtable (testrow) VALUES (1), (2), (3);
SET ROLE testrole;
SELECT testfunc();
SELECT * FROM testtable;
RESET ROLE;
Output:
$ psql -f usage.sql
CREATE DATABASE
You are now connected to database "testdb" as user "postgres".
CREATE ROLE
CREATE SCHEMA
GRANT
SET
CREATE FUNCTION
CREATE TABLE
INSERT 0 3
SET
psql:usage.sql:27: NOTICE: IN TESTFUNC
psql:usage.sql:27: NOTICE: Current user: testrole
testfunc
----------
(1 row)
psql:usage.sql:28: ERROR: permission denied for relation testtable
RESET
Have I missed something or am using the permissions incorrectly?
The schema is not what matters here, what you're seeing is the default execute permissions of functions.
Consider this excerpt from the CREATE FUNCTION documentation:
Another point to keep in mind is that by default, execute privilege is
granted to PUBLIC for newly created functions (see GRANT for more
information). Frequently you will wish to restrict use of a security
definer function to only some users. To do that, you must revoke the
default PUBLIC privileges and then grant execute privilege
selectively.