-
Notifications
You must be signed in to change notification settings - Fork 470
Add docs for Row-level security (RLS) #19527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Files changed:
|
✅ Deploy Preview for cockroachdb-interactivetutorials-docs canceled.
|
✅ Deploy Preview for cockroachdb-api-docs canceled.
|
❌ Deploy Preview for cockroachdb-docs failed. Why did it fail? →
|
dc16ae3
to
2cb3af9
Compare
56780c5
to
f07bb90
Compare
181509f
to
0046485
Compare
Fixes DOC-10439, DOC-12948 Summary of changes: - Add 'Row-level security' overview page - Add or update SQL statement docs for: - `CREATE POLICY` - `ALTER POLICY` - `DROP POLICY` - `SHOW POLICIES` - `ALTER TABLE {ENABLE,DISABLE} ROW LEVEL SECURITY` - `ALTER TABLE {FORCE,UNFORCE} ROW LEVEL SECURITY` - `CREATE ROLE ... WITH BYPASSRLS` - `ALTER ROLE ... WITH BYPASSRLS`
0046485
to
20ddc91
Compare
@@ -2,6 +2,7 @@ Privilege | Levels | Description | |||
----------|--------|------------ | |||
`ALL` | System, Database, Schema, Table, Sequence, Type | For the object to which `ALL` is applied, grants all privileges at the system, database, schema, table, sequence, or type level. | |||
`BACKUP` | System, Database, Table | Grants the ability to create [backups]({% link {{ page.version.version }}/backup-and-restore-overview.md %}) at the system, database, or table level. | |||
<a name="bypassrls"></a> `BYPASSRLS` | **New in v25.2** Grants the ability for a role to bypass [row-level security (RLS)]({% link {{ page.version.version }}/row-level-security.md %}) policies on a table using the `BYPASSRLS`/`NOBYPASSRLS` role options, granting unrestricted read and write access to all rows. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talk about BYPASSRLS
as a role option. Yet, this is the page for system privileges. I treat them separately from role options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok thanks for clearing up my confusion there; removed this from the system privs and it's now only in the role options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to clarify that BYPASSRLS can be used as a system privilege. What tripped me up was referring to it as a role option. I think we should include it on the privileges page, but with slightly adjusted wording to reflect this distinction.
src/current/v25.2/drop-policy.md
Outdated
|
||
### Replace a Policy | ||
|
||
This example demonstrates dropping an existing policy before replacing it with a new one that has a different fundamental behavior (e.g., changing from `PERMISSIVE` to `RESTRICTIVE`), which cannot be done using [`ALTER POLICY`]({% link {{ page.version.version }}/alter-policy.md %}). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this is an overly complicated example. I wouldn't expect changing the type from PERMISSIVE to RESTRICTIVE (or vice-versa) be a common occurrence that it requires an example. Can we have a simpler example here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to just be a single, simple DROP POLICY
example stmt
src/current/v25.2/create-role.md
Outdated
|
||
{% include_cached copy-clipboard.html %} | ||
~~~ sql | ||
SELECT rolname FROM pg_roles WHERE rolbypassrls = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't aware of this column in pg_roles. Currently, it always returns false. Even if the role has the BYPASSRLS
option. I'm glad you included this example. I have opened issue cockroachdb/cockroach#146228
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, glad it was useful. Do you think we should remove this for now and add back in after the issue is addressed?
src/current/v25.2/alter-role.md
Outdated
|
||
{% include_cached copy-clipboard.html %} | ||
~~~ sql | ||
SELECT rolname FROM pg_roles WHERE rolbypassrls = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment here about bug cockroachdb/cockroach#146228.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same q re: should we remove for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, let's remove it. When we fix it, I'll make sure there is a release note included with that change so we can remember to add this back.
thanks for the review @spilchen, i've tried to address your feedback, this is RFAL - changes are in latest commit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good.
src/current/v25.2/alter-role.md
Outdated
|
||
{% include_cached copy-clipboard.html %} | ||
~~~ sql | ||
SELECT rolname FROM pg_roles WHERE rolbypassrls = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, let's remove it. When we fix it, I'll make sure there is a release note included with that change so we can remember to add this back.
src/current/v25.2/alter-table.md
Outdated
For examples, see [Force and unforce row-level security](#force-and-unforce-row-level-security). | ||
|
||
{{site.data.alerts.callout_danger}} | ||
Users with the `BYPASSRLS` [role option]({% link {{ page.version.version }}/security-reference/authorization.md %}#role-options) can still bypass RLS even when `ALTER TABLE ... FORCE ROW LEVEL SECURITY` is enabled. To see the roles with this option, run: `SELECT rolname FROM pg_roles WHERE rolbypassrls = true;`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably leave the last bit about looking up in pg_roles
until we fix that issue.
|
||
When [views]({% link {{ page.version.version }}/views.md %}) are accessed, RLS policies on any underlying [tables]({% link {{ page.version.version }}/schema-design-table.md %}) are applied. [Policies]({% link {{ page.version.version }}/create-policy.md %}) can also be defined directly on views. | ||
|
||
Views use the [role]({% link {{ page.version.version }}/security-reference/authorization.md %}#roles) of the view owner to determine row-level security filters, **not** the role of the user executing the view. This can cause issues because the view owner may have entirely different policies than the user executing the view. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the design document is wrong sorry. This found out late in the cycle. We tried to capture the current behaviour here: cockroachdb/cockroach#138918 (comment)
TO public | ||
USING (tenant_id = split_part(current_setting('application_name', true), '.', 2)::UUID); | ||
|
||
CREATE POLICY tenant_isolation ON invoices |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not quite sure what the second policy is intended to show. At first glance, the expressions in both policies appear to be identical. While tenant_isolation_permissive doesn’t include an explicit WITH CHECK, it will still enforce the USING expression for INSERT and UPDATE, and that expression is the same as the WITH CHECK in the other policy.
The only notable difference is the role: one policy is for public, and the other is for app_dev. However, in the example, only the app_dev role is used—so we never really see a scenario where only the first policy applies.
If the goal is to demonstrate how multiple policies with different roles interact, a more illustrative example might involve a role that can read all tenants' data but cannot modify it. In contrast, the app_dev role would enforce tenant-level isolation for both reads and writes. Then, if a user had both roles—say, two users from app_dev and one of them also has this broader-access role—we could observe the combined effect of overlapping policies in action. This may still be a contrived example.
Another idea is to use the role for public to only show results for a default tenant. That way if the application_name is omitted, we still see results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
went with the latter suggestion b/c it seemed simpler. here is what i updated to, PTAL
CREATE POLICY tenant_isolation_permissive ON invoices
AS PERMISSIVE
FOR ALL
TO public
USING (tenant_id = COALESCE(
-- Attempt to extract UUID after the last '.' in application_name
-- Handle cases where setting is missing, empty, or has no '.'
NULLIF(split_part(current_setting('application_name', true), '.', 2), '')::UUID,
-- Default Tenant ID
'ad7b4529-f5d6-4d0e-8b0f-91af455a70e5'
)
);
with the following extra explanation
- `AS PERMISSIVE`: Necessary because you need at least one permissive policy. The permissive policy above has logic to show results for a default tenant ID if the `application_name` is omitted or improperly formatted.
@@ -1,5 +1,6 @@ | |||
Role option | Description | |||
------------|------------- | |||
<a name="bypassrls"></a> `BYPASSRLS`/`NOBYPASSRLS` | **New in v25.2**: Grants the ability to bypass [row-level security (RLS)]({% link {{ page.version.version }}/row-level-security.md %}) policies on a table, granting unrestricted read and write access to all rows. This option controls the access from an RLS perspective only; the user also needs sufficient [`GRANT`]({% link {{ page.version.version }}/grant.md %}) privileges to read or write to the table. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should mention BYPASSRLS in system privileges. You had it in earlier that I commented on. But I think there was a misunderstanding of my comment, and the entire mention was removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah my bad
went back and looked at your previous comment which was about us not talking about role options in the system privilege description. here's the updated version
BYPASSRLS
| Table | New in v25.2 Grants the ability to bypass [row-level security (RLS)]({% link {{ page.version.version }}/row-level-security.md %}) policies on a table. This privilege controls the access from an RLS perspective only; the user also needs sufficient [GRANT
]({% link {{ page.version.version }}/grant.md %}) privileges to read or write to the table.
thanks for the additional comments @spilchen , this is RFAL again! |
Fixes DOC-10439, DOC-12948
Summary of changes:
CREATE POLICY
ALTER POLICY
DROP POLICY
SHOW POLICIES
ALTER TABLE {ENABLE,DISABLE} ROW LEVEL SECURITY
ALTER TABLE {FORCE,UNFORCE} ROW LEVEL SECURITY
CREATE ROLE ... WITH BYPASSRLS
ALTER ROLE ... WITH BYPASSRLS