多区域行级别的安全性
Multi-Region Row Level Security in CockroachDB

原始链接: https://www.cockroachlabs.com/blog/fine-grained-access-control-row-level-security/

CockrouckDB 25.2引入了行级安全性(RLS),可以直接在数据库中进行细粒度的访问控件,这对于现代数据基础架构至关重要。 RLS超出了表级权限,可以通过用户角色或属性来控制数据可见性,非常适合敏感工作负载和多租户应用程序。 RLS通过根据身份将租户数据隔离在共享表中,从而简化了多租户,从而降低了基础架构成本和运营复杂性。结合多区域功能,RLS和区域性通过行表执行数据居住法,从而限制基于地理环境的访问。这样可以确保像GDPR一样的合规性,而无需进行广泛的应用程序修改。 CockacredDB的RLS与多区域数据放置的唯一配对允许基于用户身份和地理上下文进行访问控制。最终,cockrouckdb中的RLS赋予了安全有效的缩放,从而简化了复杂部署的数据访问管理。

该黑客新闻线程讨论了CockroachdB的多区域行级别安全性(RLS)功能。提出的关键点是在提供的示例中潜在的副本错误,其中显示的租户ID与已配置的ID不匹配。用户与单个数据库中的多租户相比,辩论多租户的利弊,与一位租户的方法进行了辩论,称量为灾难恢复,成本和查询策划者统计信息等因素。如果RLS依靠会话变量来识别租户,则对SQL注入漏洞提出了担忧。 Cockleadb的团队承认这些担忧,并提到将每个租户角色作为替代方案的可能性。他们还计划引入不变的会话变量以增强安全性,尽管其客户通常认为应用程序层护栏足够。对话涉及在多租户环境中数据隔离和存储页面共享的注意事项。
相关文章

原文

Controlling who can access what data is more important than ever as organizations scale and modernize their data infrastructure. 

Enterprises are modernizing their critical databases, and they need fine-grained, built-in access controls that go beyond table-level permissions. That’s why, with the 25.2 release of CockroachDB, we’re introducing Row-Level Security — a powerful feature that allows you to define and enforce access policies at the row level, directly within the database.

This form of mandatory access control enables developers and operators to tightly govern data visibility based on user roles or attributes. That makes it a natural fit for securing sensitive workloads and building robust multi-tenant applications. 

In this article we’ll explore how CockroachDB makes it simple to isolate data, comply with regulatory requirements, and reduce application-side complexity, all while maintaining performance at scale and with minimal application changes using Row Level Security.

Why row-level security mattersCopy Icon

CockroachDB already has robust support for managing privileges at the table, database or the schema level. While table-level permissions provide essential control, they often lack the granularity needed to handle nuanced access scenarios effectively. 

For instance, consider a multi-tenancy situation where multiple customers or tenants share a single table within an application. Without granular, row-level access control, ensuring that each tenant can access only their own data becomes challenging and risky.

Another critical scenario, multi-region, involves data restriction based on geography. Consider an enterprise where users should only have access to rows related to a specific geographical region: Table-level privileges alone would not be sufficient to enforce this policy without complicating the data model.

Next we'll explore these two scenarios in greater detail, and show you how Row Level Security in CockroachDB effectively addresses each use case.

Multi-Tenancy + Row Level SecurityCopy Icon

Multi-tenancy in databases is a design where a single database instance serves multiple customers (tenants), with each tenant's data kept logically isolated. This approach helps reduce infrastructure costs, streamline operations, and scale more easily, which is especially valuable for SaaS applications.

There are two common approaches to implementing multi-tenancy:

  • A separate database or cluster per tenant provides strong isolation but is expensive and operationally complex as the number of tenants increases.

  • Separate schema or table per tenant lowers infrastructure costs but becomes hard to manage at scale, requiring duplicated schema changes and inconsistent performance tuning.

A third and more scalable option is Row-Level Security (RLS). With RLS in CockroachDB, all tenant data lives in shared tables, and access is controlled at the row level based on tenant identity. This model combines strong logical isolation with lower infrastructure and operational costs, making it well suited for high-scale multi-tenant environments.

From an operational perspective, Row-Level Security (RLS) makes management simpler. Instead of maintaining a separate schema or table for each tenant, you use a single shared schema. 

This means schema changes, like adding a column or modifying an index, only need to be applied once. It reduces the risk of inconsistencies between tenants and eliminates the need for complex migration processes or tenant-specific logic in deployment pipelines.

On the infrastructure side, RLS allows for more efficient resource usage. By consolidating tenant data into shared tables, CockroachDB can optimize performance across tenants without duplicating storage or compute resources. This lets you host more tenants on the same cluster, improving hardware utilization and reducing the need for additional infrastructure to support growth.

To demonstrate how Row Level Security can enable multi-tenant setups with minimal application changes, let’s look at the following example:

First, we create an in-memory cluster for demo purposes.

cockroach demo --no-example-database --insecure

Then we will create our database and a multi-tenant table, note the tenant_id column.

CREATE DATABASE store; CREATE TABLE tenant_data (     id UUID PRIMARY KEY DEFAULT gen_random_uuid(),     tenant_id UUID NOT NULL,     data TEXT NOT NULL );

Next, we’ll create an index on the data column, prefixed with tenant_id. This is a crucial step because including tenant_id at the beginning of the index ensures that queries scoped to a specific tenant can efficiently locate relevant rows. 

Without these indexes, the database may need to scan large portions of the table, which can degrade performance as your dataset grows.

CREATE INDEX ON tenant_data(tenant_id, data);

Next we will create a user that we will later login as:

CREATE ROLE secureuser LOGIN; GRANT ALL ON store.* TO secureuser;

Now we can enable row level security. Note the FORCE option below, this enforces the row level security policy for everyone including the table owner:

ALTER TABLE tenant_data ENABLE ROW LEVEL SECURITY;

Next we will create our Row Level Security Policy which checks the value of a current_tenant session variable against the values in the tenant_id column:

CREATE POLICY tenant_isolation_policy ON tenant_data FOR secureuser USING (tenant_id = current_setting('app.current_tenant')::UUID); INSERT INTO tenant_data (tenant_id, data) VALUES ('11111111-1111-1111-1111-111111111111', 'abc'), ('11111111-1111-1111-1111-111111111111', 'abcdef'), ('22222222-2222-2222-2222-222222222222', 'abc'), ('22222222-2222-2222-2222-222222222222', 'abcdef');

Now we will login as our secureuser that we created earlier. From another terminal run:

cockroach sql --insecure --url 'postgres://[email protected]:26257/store?sslmode=disable'

Then we can set the tenant context using a session variable:

SET app.current_tenant = '11111111-1111-1111-1111-111111111111';

If we query the data, we will only see the rows that belong to tenant:  '11111111-1111-1111-1111-111111111111':

SELECT * FROM tenant_data WHERE data = 'abc';                    id                  |              tenant_id               | data ---------------------------------------+--------------------------------------+-------   e511fc8e-5621-48b6-b792-c4b12a07b2ca | 22222222-2222-2222-2222-222222222222 | abc (1 row)

In fact even if we were to select everything in that table we would only see data for tenant '11111111-1111-1111-1111-111111111111':

SELECT * FROM tenant_data;                    id                  |              tenant_id               |  data ---------------------------------------+--------------------------------------+---------   4143c0da-4fda-4f09-9149-5b7d2988d206 | 11111111-1111-1111-1111-111111111111 | abc   4d39e12e-5e86-4c93-868f-41771ba46137 | 11111111-1111-1111-1111-111111111111 | abcdef (2 rows)

Similarly, if we switch the current_tenant we can only see data for '22222222-2222-2222-2222-222222222222': 

SET app.current_tenant = '22222222-2222-2222-2222-222222222222'; SELECT * FROM tenant_data WHERE data = 'abc';                    id                  |              tenant_id               | data ---------------------------------------+--------------------------------------+-------   e511fc8e-5621-48b6-b792-c4b12a07b2ca | 22222222-2222-2222-2222-222222222222 | abc (1 row)

This highlights the strength of Row-Level Security. With a few well-defined policies and session variables, and without any changes to the underlying application queries: 

  • We can present each tenant with a logical view of the data. 

  • To each tenant, it appears as if they have exclusive access to their own data, even though it is stored in the same physical table as other tenants' data. 

  • This keeps the application layer simple while ensuring mandatory access control at the database level.

Multi-region + row-level securityCopy Icon

CockroachDB supports easy declarative syntax to home data using REGIONAL BY ROW (RBR) tables. In a regional by row table, each row in a single logical table can be stored in a specific geographic region. But what about safeguarding who has access to those rows? 

Colocating data is only part of the story – when applications span multiple regions, access control must follow suit. This is where Row-Level Security and RBR complement each other perfectly.

Let’s imagine a scenario where data residency laws restrict users in the US from accessing data stored in the EU. Let’s walk through how we can help enforce compliance with this by combining RBR tables and Row Level Security together. First we create a multi-region in memory cluster for demo purposes:

cockroach demo --nodes=9 --no-example-database --insecure

Then we create our schema:

CREATE DATABASE store   PRIMARY REGION "us-east1"   REGIONS "us-west1", "europe-west1"; USE store; CREATE ROLE secureuser LOGIN; GRANT ALL ON store.* TO secureuser; CREATE TABLE user_profiles (     user_id UUID PRIMARY KEY,     name STRING,     email STRING ) LOCALITY REGIONAL BY ROW; INSERT INTO user_profiles (user_id, name, email, crdb_region) VALUES   (gen_random_uuid(), 'Alice', '[email protected]', 'us-east1'),   (gen_random_uuid(), 'Bob', '[email protected]', 'us-west1'),   (gen_random_uuid(), 'Clara', '[email protected]', 'europe-west1');

Now we enable row level policy and attach a policy to the table. If we look closely at our policy we have a USING clause that is checking if the crdb_region in the table is the same as the gateway region (ie; the region of the node in which the user is logged into):

ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY; CREATE POLICY regional_access ON user_profiles USING (crdb_region = gateway_region()::crdb_internal_region);

Now to demonstrate the power of Row Level Security we will connect to nodes in different regions and see how it affects our query results. 

When running cockroach demo, each node is accessible via a different port, starting from 26257. We can see the nodes and their region with port info by running the following from a new terminal window:

cockroach node status --insecure | awk 'NR > 1 && NF { print $3, $13 }' 127.0.0.1:26257 region=us-east1,az=b 127.0.0.1:26258 region=us-east1,az=c 127.0.0.1:26259 region=us-east1,az=d 127.0.0.1:26260 region=us-west1,az=a 127.0.0.1:26261 region=us-west1,az=b 127.0.0.1:26262 region=us-west1,az=c 127.0.0.1:26263 region=europe-west1,az=b 127.0.0.1:26264 region=europe-west1,az=c 127.0.0.1:26265 region=europe-west1,az=d

Now let’s connect to  a node in us-east1 as secureuser and try to query some data:

cockroach sql --insecure --url 'secureuser://@127.0.0.1:26257/store?sslmode=disable' [email protected]:26257/demoapp/store> SELECT *, crdb_region FROM user_profiles;                 user_id                | name  |      email       | crdb_region ---------------------------------------+-------+------------------+---------------   D955be6a-aacf-4ccb-930b-b6294f304f57 | Alice | [email protected] | us-east1 (1 row)

As we can see, since we are connected to a node in us-east1 we are only seeing rows that are available in the us-east1 region. Similarly if we connect to a node in europe-west1, we will only see data in the europe-west1 region:

cockroach sql --insecure --url 'postgres://[email protected]:26263/store?sslmode=disable' [email protected]:26263/demoapp/store> SELECT *, crdb_region FROM user_profiles;                 user_id                | name  |      email       | crdb_region ---------------------------------------+-------+------------------+---------------   64adf753-9a6b-479e-b706-b65661bd2690 | Clara | [email protected] | europe-west1 (1 row)

Combining Row-Level Security with Regional by Row tables gives you precise control over both data location and access. Row-Level Security can also be used to enforce not just read access but also writes, ensuring that nodes in the US-east cannot insert or update EU-related data. This helps maintain strict regional boundaries and prevents accidental cross-region writes. You can also set up alerting around rejected writes to catch and respond to misrouted traffic early. This is crucial for applications that operate across regions with data residency rules, such as GDPR. 

It allows you to keep data physically in the required location, and ensures users can only access what they are allowed to see based on their connection region. 

All of this is done at the database level, which reduces complexity in the application.

Scale Your Data Securely and EfficientlyCopy Icon

Row-Level Security (RLS) in CockroachDB is a major advancement for modern data access control. It provides a powerful, built-in way to enforce fine-grained access policies directly within the database. 

What sets CockroachDB apart is its ability to pair RLS with multi-region data placement, enabling access control based not just on user identity but also on geographic context. 

Whether you're building a multi-tenant SaaS platform or enforcing regional data boundaries, CockroachDB is how to scale securely and efficiently without rewriting application logic.

Security Quick StartCopy Icon

Ready to try out Row-Level Security in CockroachDB? These resources will get you started with implementing fine-grained access control in your applications:

  • Row-Level Security documentation: The official docs cover everything you need to know about enabling RLS, writing policies, and understanding how access control is enforced at the row level.

  • Demo: A video from Cockroach Labs Technical Evenagelist Rob Reid that walks through setting up RLS in a multi-tenant and multi-region environment, showing how access can be scoped cleanly and securely.

Spin up your first CockroachDB Cloud cluster in minutes. Start with $400 in free credits. Or get a free 30-day trial of CockroachDB Enterprise on self-hosted environments.

Dikshant Adhikari is a Senior Product Manager for Cockroach Labs.

Matt Spilchen is a Senior Staff Software Engineer at Cockroach Labs

联系我们 contact @ memedata.com