Row level security does not work for table owner - postgresql

I have a table, customer on which I did the following:
ALTER TABLE customer FORCE ROW LEVEL SECURITY;
CREATE POLICY customer_rls ON customer USING (false);
However, doing SELECT * FROM customer still returns all the rows.
The current role is myrole
\dg myrole
List of roles
Role name | Attributes | Member of
-----------+------------+-----------
my_role | | {}
As you can see it's not a superuser and it RLS isn't disabled on it.
What am I doing wrong?

You forgot to enable row level security for the table.
ALTER TABLE customer enable ROW LEVEL SECURITY;
force only makes sure that RLS is applied if enabled, it does not enable RLS on the table.
Online example: https://rextester.com/TCLZ82421

Related

Using PostgreSQL row level security (RLS) policies with current_setting() function

I've applied RLS policy to the "users" table and expect only records with tenant_id=2 to be retrieve:
CREATE TABLE "users" ("name" text UNIQUE NOT NULL, "tenant_id" int NOT NULL DEFAULT current_setting('app.current_tenant')::int);
--Enable Row Security Policies
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE users FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_policy ON users USING (tenant_id = current_setting('app.current_tenant')::int);
--Set "111" as the current tenant.
SET app.current_tenant TO 1;
INSERT INTO users VALUES ('admin');
INSERT INTO users VALUES ('bob');
--Set "222" as the current tenant.
SET app.current_tenant TO 2;
INSERT INTO users VALUES ('alice');
--Data output
SELECT * FROM users;
But I get all users in the result:
name tenant_id
admin 1
bob 1
alice 2
Why is this happening?
Here is the dbFiddle of what I am stuck with:
https://www.db-fiddle.com/f/iFktvVsDNYKggUNT2oDJBV/0
There are four reasons why row level security can be bypassed:
The user is the owner of the table.
You can subject the table owner to row level security with
ALTER TABLE users FORCE ROW LEVEL SECURITY;
The user is a superuser.
The user was created with BYPASSRLS.
The database parameter row_security is set to off.
Note that using row level security with a placeholder parameter is inherently insecure: if an attacker can issue an SQL statement (say, through SQL injection), they can just change the value to what they like.

PostgreSQL RLS policy to specific user is not working as expected and applies to all users

I have 2 users in my DB, one is "strong" and one is "weak".
I want to apply RLS policy only for one of them, the weak user.
Meaning, when strong user queries the table, it should get all rows. But when weak user queries the table, the policy will be applied and it will return only allowed rows.
I have created a table, and applied the RLS policy only to the weak user.
But even when querying with the strong user, the policy is executed and prevents me from getting all rows.
I'm using PostgreSQL version 11.4.
Here is how I created the policy (I've created the policy with another 3rd user which is an admin and the owner of the table)
CREATE TABLE account_test
(
id bigserial not null,
description varchar(200),
tenant_id UUID not null
);
ALTER TABLE account_test ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_policy ON account_test TO weak_user
USING (tenant_id = current_setting('rls.tenant_id')::uuid);
account=# select * from pg_policies;
schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check
------------+--------------+---------------+------------+---------------+-----+--------------------------------------------------------------+------------
account | account_test | tenant_policy | PERMISSIVE | **{weak_user}** | ALL | (tenant_id = (current_setting('rls.tenant_id'::text))::uuid) |
Now, inserting and selecting with admin user always works, because it is the owner:
insert into account_test (description, tenant_id) VALUES ('desc111', '11111111-c929-462e-ade4-074c81643191');
select * from account_test;
no problem here and all rows returned.
When trying to login with weak_user and select, I get no rows as expected:
select * from account_test;
-- returns 0 rows as expected (weak_user).
If I set the parameter, policy applies and I get the data as expected:
select set_config('rls.tenant_id', '11111111-c929-462e-ade4-074c81643191',true);
select * from account_test;
-- returns 1 row as expected
Now, when I login with strong_user and perform the select * from account_test query, I expect all rows to be returned because policy applies only for weak_user.
However, I get the same behavior as for weak_user and no rows return.
Also the query with set_config does not return anything.
What am I missing?
Is that the expected behavior?
Can someone explain?
The moment you enable RLS on a table with ALTER TABLE account_test ENABLE ROW LEVEL SECURITY, a default-deny all policy is used for all users.
When [RLS] is enabled on a table, all normal access to the table for selecting rows or modifying rows must be allowed by a row security policy. (However, the table's owner is typically not subject to row security policies.) If no policy exists for the table, a default-deny policy is used, meaning that no rows are visible or can be modified.
– https://www.postgresql.org/docs/current/ddl-rowsecurity.html
Each policy has a name and multiple policies can be defined for a table. As policies are table-specific, each policy for a table must have a unique name. Different tables may have policies with the same name.
In order for strong_user to have access, you will need to add another rule, e.g.
CREATE POLICY tenant_policy ON account_test TO strong_user USING (true);
When multiple policies apply to a given query, they are combined using either OR (for permissive policies, which are the default) or using AND (for restrictive policies). This is similar to the rule that a given role has the privileges of all roles that they are a member of. Permissive vs. restrictive policies are discussed further below.

postgres "force row level security" executed, but associated policies disabled [duplicate]

I have a table, customer on which I did the following:
ALTER TABLE customer FORCE ROW LEVEL SECURITY;
CREATE POLICY customer_rls ON customer USING (false);
However, doing SELECT * FROM customer still returns all the rows.
The current role is myrole
\dg myrole
List of roles
Role name | Attributes | Member of
-----------+------------+-----------
my_role | | {}
As you can see it's not a superuser and it RLS isn't disabled on it.
What am I doing wrong?
You forgot to enable row level security for the table.
ALTER TABLE customer enable ROW LEVEL SECURITY;
force only makes sure that RLS is applied if enabled, it does not enable RLS on the table.
Online example: https://rextester.com/TCLZ82421

How Do I Insert Into a Table With Row Level Security?

I have the following table:
Table "api_v1.person"
Column | Type | Modifiers
---------------+--------+-------------------------------------------------------
person_id | bigint | not null default...
name | text | not null
date_of_birth | date |
api_user | text | not null default "current_user"()
That has the following policy:
POLICY "api_user_only" FOR ALL
USING ((api_user = ("current_user"())::text))
WITH CHECK ((api_user = ("current_user"())::text))
My understanding is that the FOR ALL portion of the policy means that it covers inserts and the WITH CHECK ensures that the value inserted into api_user is the same as the current user, eg the role name. The USING clause should only effect SELECTS or other data that is returned. However, when I try to insert I get the following results:
demo=> INSERT INTO api_v1.person (name, api_user) VALUES ('Greg', current_user);
ERROR: query would be affected by row-level security policy for table "person"
How do I do this insert?
I'm running PostgreSQL 9.6.8.
Here is the SQL necessary to reproduce:
BEGIN;
CREATE SCHEMA api_v1;
CREATE TABLE api_v1.person (
person_id BIGSERIAL PRIMARY KEY,
"name" TEXT NOT NULL,
date_of_birth DATE,
api_user TEXT NOT NULL DEFAULT current_user
);
ALTER TABLE api_v1.person ENABLE ROW LEVEL SECURITY;
CREATE POLICY
api_user_only
ON
api_v1.person
USING
(api_user = CURRENT_USER)
WITH CHECK
(api_user = CURRENT_USER)
;
CREATE ROLE test_role;
GRANT USAGE ON SCHEMA api_v1 TO test_role;
GRANT ALL ON api_v1.person TO test_role;
GRANT USAGE ON SEQUENCE api_v1.person_person_id_seq TO test_role;
COMMIT;
SET ROLE test_role;
INSERT INTO api_v1.person ("name") VALUES ('Greg');
There is a setting in postgresql.conf, row_security. If this is set to off then any query that would be effected by a row level security policy fails with the error: ERROR: query would be affected by row-level security policy for table "table_name". However, queries from superusers, the table owner (if you don't force RLS), and roles with bypassrls will work.
The row_security setting needs to be set to on and then PostgreSQL needs to be restarted for regular user statements to be processed against tables with row level security policies.
From the source code:
/*
* We should apply RLS. However, the user may turn off the row_security
* GUC to get a forced error instead.
*/
if (!row_security && !noError)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("query would be affected by row-level security policy for table \"%s\"",
get_rel_name(relid)),
amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0));

Alter default privileges for a group role in PostgreSQL

I have created two group roles in Postgres 9.2: one is called admins and the other is called readers.
The idea is very simple: admins create tables and readers have read access to these tables.
After granting privileges to both group roles everything worked as expected for exisintg objects. But now what about new objects?
So after reading this post I altered the default privileges to grant SELECT privileges to readers for any new table that admins create:
ALTER DEFAULT PRIVILEGES FOR ROLE admins IN SCHEMA public GRANT SELECT ON TABLES TO readers;
ALTER DEFAULT PRIVILEGES FOR ROLE admins IN SCHEMA public GRANT SELECT ON SEQUENCES TO readers;
But apparently, ALTER DEFAULT PRIVILEGES only affects the role itself but not the members of the role. Let me show you.
If I login as userX (a member of admins) and create a new table, no default privileges are granted (and therefore, readers cannot access this table):
test=# CREATE TABLE table1 (name VARCHAR(10)); -- Creating table as userX
test=# \dp table1
Access privileges
Schema | Name | Type | Access privileges | Column access privileges
--------+--------+-------+-------------------+--------------------------
public | table1 | table | |
However, the default privileges are granted if I create the table as admins (readers can access this table):
test=# SET ROLE admins;
test=# CREATE TABLE table2 (name VARCHAR(10)); -- Creating table as admins
test=# \dp table2
Access privileges
Schema | Name | Type | Access privileges | Column access privileges
--------+--------+-------+-----------------------+--------------------------
public | table2 | table | readers=r/admins +|
| | | admins=arwdDxt/admins |
Is there a way to alter the default privileges for ALL members of a group role? Or should I just alter default privileges for each user?
UPDATE: In this PostgreSQL forum someone asked a very similar question and the answer was:
Unfortunately I can't see a way to achieve what you want without granting default privileges to everybody involved.
However this question was asked 2 years ago. Is there a solution now?
If a user creates a table then this user becomes the owner of the table. So in your case any default privileges for userX apply, not those of admins. the solution is to SET ROLE admins before creating your table:
SET ROLE admins;
CREATE TABLE ... -- This now applies default privileges of admins
;
RESET ROLE;
More in general, you would want to do this always: Create all tables and views through a group role or some other role not used in daily operations and grant access to the relations to another group role whose privileges are inherited by regular login roles (users). This greatly facilitates security management.
Cheers,
Patrick