Row-Level Security (RLS) в многоклиентской базе данных обеспечивает изоляцию данных разных клиентов на уровне базы данных. Это достигается применением политик безопасности, которые автоматически фильтруют строки в зависимости от прав доступа пользователя или идентификатора клиента (tenant_id). Реализация требует добавления поля tenant_id во все таблицы и создания соответствующих правил фильтрации для каждого уровня доступа.

Ровный доступ (Row-Level Security) в многоклиентской базе данных

Ровный доступ (Row-Level Security, RLS) — это механизм безопасности в PostgreSQL, который позволяет изолировать данные разных клиентов друг от друга на уровне базы данных. Это критически важно для многоклиентских архитектур (multi-tenant), где одна инстанция БД обслуживает множество организаций или пользователей. Без RLS администратору пришлось бы вручную создавать отдельные базы данных для каждого клиента, что неэффективно при большом количестве арендаторов. RLS автоматически фильтрует строки в зависимости от контекста выполнения запроса, гарантируя, что пользователь видит только те записи, которые ему разрешены политикой безопасности.

Основная задача RLS — обеспечить соблюдение требований конфиденциальности и изоляции данных без необходимости писать сложную логику фильтрации в каждом прикладном коде. Когда пользователь выполняет запрос, база данных проверяет привязанные к таблицам политики (Policies). Если политика возвращает false для конкретной строки, запись исключается из результата, даже если у пользователя есть права на чтение всей таблицы. Это защищает от утечек данных через SQL-инъекции или ошибки в коде приложения, где разработчик мог забыть добавить фильтр по tenant_id.

Для корректной работы RLS необходимо определить модель доступа. Чаще всего используется подход с уникальным идентификатором клиента (tenant_id), который хранится во всех таблицах. В этом случае политика привязывает доступ к значению tenant_id текущего пользователя, полученному из контекста сессии или роли. Альтернативный вариант — группировка пользователей по ролям внутри одной организации, когда политики проверяют принадлежность пользователя к группе, имеющей доступ к данным конкретного клиента.

В PostgreSQL политика RLS реализуется через объект SECURITY POLICY. Он содержит выражение (expression), которое возвращает true или false для каждой строки таблицы. Это выражение может использовать системные функции безопасности, такие как current_setting('request.jwt.claim') для JWT-токенов, или переменные сессии. Важно понимать, что политики применяются ко всем таблицам, включая промежуточные результаты подзапросов и представления. Если в представлении используется таблица без RLS, а само представление защищено, фильтрация всё равно сработает, но если подзапрос возвращает строки из незащищённой таблицы, утечка может произойти до того, как будет применена политика верхнего уровня.

При проектировании схемы для RLS критически важно хранить tenant_id во всех таблицах, связанных с данными пользователя. Если в таблице заказов есть поле client_id, а в таблице пользователей — user_tenant_id, то политики должны ссылаться на эти поля. Ошибка здесь приводит к тому, что пользователь может увидеть заказы других клиентов, если политика не охватывает все таблицы. Также необходимо учитывать, что RLS работает только для операций SELECT, INSERT, UPDATE и DELETE. Для этих операций выражение политики должно возвращать true только для строк, которые пользователь имеет право изменить или удалить.

Тестирование RLS требует выполнения запросов от разных пользователей с разными правами. Используйте функцию set_role() или переключение контекста пользователя в psql для проверки изоляции. Запустите тестовые запросы: попытка прочитать данные другого клиента должна вернуть пустой набор или только свои строки. Проверьте также поведение при вставке данных: если политика запрещает вставку строк с tenant_id, отличным от текущего, база должна отклонить операцию с ошибкой доступа.

При миграции или изменении структуры базы необходимо проверять влияние новых полей на существующие политики безопасности. Если вы добавили новую таблицу для хранения истории действий, убедитесь, что к ней применены соответствующие политики RLS. Устаревшие правила могут привести к утечкам данных, если они не учитывают новые требования конфиденциальности. Регулярный аудит политик помогает выявлять уязвимости и корректировать логику доступа по мере роста бизнеса.

Практический пример: представьте систему CRM с таблицей клиентов (clients) и таблицей сделок (deals). У каждой записи в deals есть поле client_id. Создайте политику, которая разрешает чтение только тех сделок, где client_id совпадает с tenant_id текущего пользователя. При попытке доступа к данным другого клиента база данных автоматически отфильтрует строки. Это работает даже если пользователь имеет права суперпользователя на уровне базы — RLS применяется после проверки прав суперпользователя, но до возврата результата.

Ограничения RLS включают необходимость правильного управления контекстом пользователя и сложность поддержки при динамических ролях. Если политика зависит от внешних данных или сложных выражений, производительность может снизиться из-за дополнительных проверок на каждой строке. Также важно помнить, что RLS не заменяет аутентификацию и авторизацию на уровне приложения — он дополняет их, обеспечивая дополнительный уровень защиты. Для максимальной безопасности комбинируйте RLS с ролевой моделью (RBAC) и шифрованием чувствительных данных.

Выбор модели доступа для реализации Row-Level Security

Перед созданием политик безопасности необходимо выбрать одну из двух фундаментальных архитектурных моделей. Первая модель строится вокруг уникального идентификатора клиента (tenant_id). Вторая опирается на группировку пользователей по ролям внутри единой организации. Выбор между ними зависит от того, как организована ваша бизнес-модель и где хранятся данные о принадлежности.

Модель на основе tenant_id подходит для архитектуры многозаимствования (multi-tenancy), где каждый клиент — это отдельная логическая единица. В этом случае уникальный идентификатор клиента должен присутствовать в каждой таблице, содержащей бизнес-данные: заказы, пользователи, настройки и т.д. При выполнении любого запроса система автоматически добавляет фильтр WHERE tenant_id = текущий_идентификатор. Это гарантирует, что пользователь никогда не увидит данные другого клиента, даже если у него есть широкие права на таблицу.

Модель на основе ролей внутри организации актуальна, когда база данных обслуживает одну компанию или группу компаний, где доступ определяется должностными обязанностями. Здесь tenant_id может отсутствовать в таблицах, а фильтрация строится через атрибуты пользователя (например, department_id или role). Пользователь видит только те строки, которые соответствуют его роли. Например, менеджер отдела продаж видит заказы своего отдела, а директор — все заказы компании.

При выборе модели на основе tenant_id критически важно обеспечить нормализацию схемы данных. Идентификатор клиента должен дублироваться во всех связанных таблицах или поддерживаться через внешние ключи, которые автоматически наследуют значение родителя. Если в таблице заказов хранится только ID клиента, а не сам ID, политика RLS должна ссылаться на эту связь. Иначе при попытке фильтрации по заказам система не сможет определить принадлежность строки конкретному клиенту.

Для реализации модели ролей необходимо правильно настроить контекст пользователя. В PostgreSQL это делается через переменные окружения или специальные функции, которые передают роль в запросы. Если роль меняется динамически (например, пользователь временно получает права администратора), политика RLS должна учитывать этот момент. Иначе могут возникнуть ситуации, когда пользователь с повышенной ролью увидит данные, к которым у него нет доступа по бизнес-логике.

Сравнение двух моделей показывает, что tenant_id проще в поддержке при масштабировании на множество клиентов, но требует больше места в таблицах и более сложной миграции данных. Модель ролей гибче для внутренних систем, но сложнее в управлении при изменении структуры организации. Если вы планируете предоставлять доступ к базе внешним клиентам, выбирайте модель tenant_id. Если база используется внутри одной компании с четкой иерархией отделов — модель ролей.

Независимо от выбранной модели, всегда проверяйте, что политика RLS применяется ко всем таблицам, включая промежуточные результаты подзапросов и представления. Иначе пользователь может обойти защиту через косвенные запросы. Например, если представление возвращает данные без фильтра по tenant_id, а затем этот результат используется в другом запросе с фильтром, утечка данных становится возможной.

Перед внедрением политики безопасности протестируйте её на разных сценариях: запрос от пользователя с низкой ролью, запрос от администратора, запрос через подзапрос и запрос к представлению. Убедитесь, что в каждом случае возвращается только нужный набор строк. Если политика зависит от внешних данных или сложных выражений, производительность может снизиться из-за дополнительных проверок на каждой строке. В таких случаях рассмотрите возможность упрощения логики фильтрации или использования материализованных представлений.

Регулярный аудит политик RLS помогает выявлять устаревшие правила и потенциальные уязвимости в модели доступа. Если структура базы меняется, например, добавляется новое поле или таблица, проверьте, что политика безопасности корректно работает с новыми данными. Иначе могут появиться дыры в защите, через которые пользователи смогут получить доступ к несанкционированным данным.

Механизм реализации RLS в PostgreSQL

В базе данных PostgreSQL политика Row-Level Security (RLS) настраивается через объект безопасности, который называется политикой. Это не отдельный тип таблицы или права доступа, а правило, которое привязывается к конкретной таблице и срабатывает автоматически при каждом запросе SELECT, INSERT, UPDATE или DELETE.

Политика состоит из двух частей: условия фильтрации (WHERE) и действия (ALLOW или RESTRICT). Когда пользователь отправляет запрос, система проверяет его роль и текущий контекст. Если политика позволяет доступ, строка возвращается; если запрещает — строка игнорируется или ошибка зависит от настройки действия.

Самый распространенный сценарий — привязка фильтрации к идентификатору клиента (tenant_id). В этом случае в условии политики используется переменная приложения $1, которая подставляется динамически. Например, если пользователь входит в систему с tenant_id = 5, то политика автоматически добавит условие WHERE tenant_id = 5 ко всем запросам к этой таблице.

Для работы RLS необходимо создать политику через оператор CREATE POLICY. При этом важно указать имя политики и таблицу, к которой она применяется. Политика может быть привязана только к одной таблице, но на одну таблицу можно повесить несколько политик с разными условиями доступа.

После создания политики её нужно активировать командой ALTER TABLE … ENABLE ROW LEVEL SECURITY. Без этого шага правила останутся в базе данных, но не будут применяться к запросам. Также стоит убедиться, что у пользователей есть права на выполнение функций безопасности, если политика использует сложные выражения.

Если вы хотите запретить доступ ко всем строкам для определённой группы пользователей, используйте действие RESTRICT. В этом случае даже если условие фильтрации выполняется, запрос будет отклонён с ошибкой доступа. Это полезно для защиты критических данных, к которым нельзя получить доступ ни при каких обстоятельствах.

При работе с подзапросами и представлениями RLS применяется автоматически ко всем уровням вложенности. Если вы создадите представление на основе таблицы с политикой, то данные внутри представления будут фильтроваться так же, как и в исходной таблице. Это предотвращает утечку данных через обходные пути.

Важно помнить, что политики RLS не заменяют проверку прав пользователя на уровне приложения. Они работают как дополнительный слой защиты на стороне базы данных. Если приложение по ошибке передаст запрос без фильтрации tenant_id, политика всё равно заблокирует доступ к чужим данным.

Проектирование схемы с обязательным хранением идентификатора клиента

Центральным элементом архитектуры многоклиентской базы данных является наличие уникального идентификатора клиента (tenant_id) в каждой таблице, содержащей бизнес-данные. Это требование вытекает из принципа изоляции данных: если поле tenant_id отсутствует хотя бы в одной таблице, связанной с пользователем или организацией, политика Row-Level Security не сможет корректно отфильтровать строки при сложных запросах, использующих JOIN между таблицами.

В отличие от монолитных систем, где данные одного клиента физически разделены на разные базы или схемы, в shared-schema архитектуре (одна база для всех клиентов) идентификатор становится ключом к изоляции. Без его повсеместного наличия возникает риск «просачивания» данных через промежуточные результаты подзапросов. Например, если запрос агрегирует данные из таблицы заказов и таблицы пользователей, а поле tenant_id есть только в таблице заказов, база данных не сможет применить фильтр безопасности на уровне строк для итоговой выборки.

При создании новых таблиц необходимо сразу включать колонку tenant_id как обязательное поле (NOT NULL). Для существующих таблиц требуется провести инвентаризацию всех сущностей: пользователей, ролей, настроек, логов и метаданных. Если в таблице хранятся только системные данные или общие справочники, которые не привязаны к конкретному клиенту, добавление tenant_id может быть избыточным, но для таблиц, связанных с бизнес-логикой, это критично.

Тип данных для tenant_id следует выбирать исходя из ожидаемого количества клиентов. Для большинства проектов достаточно стандартного INTEGER или BIGINT. Если планируется масштабирование до десятков тысяч клиентов, целесообразно использовать UUID, однако это усложнит работу с индексами и требует более тщательной настройки политик безопасности. Важно также обеспечить наличие уникального индекса по tenant_id в каждой таблице, чтобы ускорить фильтрацию строк при проверке прав доступа.

Существует распространенная ошибка проектировщиков: хранение tenant_id только в таблицах пользователей и заказов, но не в таблицах настроек или кэша. Если настройки функционала привязаны к клиенту (например, лимиты на операции), отсутствие поля tenant_id в таблице настроек сделает невозможным применение RLS к этим данным. При проектировании схемы нужно задавать вопрос: «Кому принадлежат эти данные?». Если ответ — конкретному клиенту, поле tenant_id должно присутствовать.

Для упрощения поддержки рекомендуется использовать единый тип данных и формат для tenant_id во всех таблицах. Смешивание идентификаторов разных типов (например, числовой ID в одних таблицах и строковый UUID в других) усложняет написание универсальных политик безопасности и может привести к ошибкам при миграции или изменении структуры базы. Также стоит предусмотреть механизм обработки клиентов, которые еще не имеют ID в системе: такие записи должны либо отсутствовать, либо иметь значение по умолчанию, которое явно блокируется политикой RLS.

При работе с внешними источниками данных или импортом информации важно проверять наличие tenant_id в поступающих наборах. Если данные поступают без привязки к клиенту, их необходимо либо отклонить, либо помечать как общие для всех клиентов (если это допустимо по бизнес-логике). В последнем случае политика RLS должна быть настроена так, чтобы разрешать доступ только тем пользователям, у которых tenant_id совпадает с текущим контекстом или равен нулю/пустому значению, если такая модель поддерживается политикой.

Использование tenant_id во всех таблицах также упрощает процесс аудита безопасности. При проверке логов или анализе аномалий доступа можно сразу отфильтровать записи по этому полю, не прибегая к сложным запросам с JOIN. Это особенно актуально при расследовании инцидентов утечки данных, когда необходимо быстро определить, какие именно данные мог увидеть несанкционированный пользователь.

В заключение, проектирование схемы с обязательным наличием tenant_id — это не просто техническое требование, а фундамент безопасности многоклиентской системы. Игнорирование этого правила на этапе разработки приводит к необходимости переписывать архитектуру или внедрять сложные обходные пути, которые часто менее надежны, чем правильная реализация RLS с самого начала.

Применение политик безопасности ко всем уровням данных

Политика доступа должна распространяться не только на базовые таблицы, но и на результаты подзапросов, представления и временные структуры. Это требование продиктовано тем, что в SQL-движках фильтрация происходит последовательно: если пользователь получит доступ к промежуточному результату, содержащему данные других клиентов, он сможет использовать его для извлечения полной информации о системе.

Угроза утечки через подзапросы возникает при использовании операторов JOIN или UNION. Если одна часть запроса возвращает строки клиента А, а другая — клиента Б, итоговый результат будет содержать смешанные данные. Без соответствующих политик на уровне базы данных СУБД не сможет отфильтровать такие комбинации автоматически.

Представления (views) часто используются для упрощения доступа к данным или создания стандартных отчетов. Однако если в представлении отсутствует политика RLS, любой пользователь с доступом к этому представлению может получить доступ ко всем строкам, которые оно описывает. Это делает представление «дырой» в безопасности, даже если сами таблицы защищены.

Временные таблицы и структуры, создаваемые внутри процедур или триггеров, также требуют контроля. Если скрипт создает временную таблицу для агрегации данных без привязки к контексту текущего пользователя, она может стать хранилищем утечек. Политики безопасности должны проверять права доступа не только при чтении из постоянных таблиц, но и при работе с любыми объектами, созданными в рамках запроса.

Для реализации этого требования в PostgreSQL необходимо явно указывать политики на уровне базы данных для каждого объекта, который может быть использован в запросе. Это включает как таблицы, так и представления, которые возвращают данные из нескольких источников. Игнорирование этого аспекта приводит к тому, что защита строится только вокруг «периметра» базы, но не вокруг логики обработки данных внутри нее.

Проверка корректности политик на промежуточных результатах требует выполнения тестовых запросов, которые имитируют реальные сценарии работы приложения. Необходимо убедиться, что даже при использовании сложных подзапросов и объединений данных фильтрация происходит корректно для каждого пользователя отдельно.

При разработке архитектуры многоклиентской системы важно учитывать, что безопасность — это свойство всей системы обработки запросов, а не только хранения данных. Это означает, что каждый шаг трансформации данных должен быть защищен политикой RLS, если он может привести к изменению видимости набора строк для пользователя.

Тестирование и валидация политик безопасности

Проверка корректности работы Row-Level Security (RLS) невозможна без имитации реальных сценариев доступа. Теоретическая настройка правил фильтрации не гарантирует их эффективность, если они не работают в контексте конкретных прав пользователей и типов запросов. Тестирование начинается с создания тестовых аккаунтов, соответствующих разным ролям: администратор системы, менеджер конкретного клиента, внешний аудитор или злоумышленник с минимальными правами.

Ключевой этап — выполнение SQL-запросов от каждого из этих пользователей. Администратор должен видеть все строки таблицы, включая данные всех клиентов. Менеджер организации (tenant) должен получать только те записи, где значение tenant_id совпадает с идентификатором его компании. Внешний пользователь или гость не должен получить ни одной строки данных, даже если запрос возвращает пустой набор результатов.

Особое внимание уделяется косвенным утечкам через промежуточные результаты и представления. Если у пользователя есть доступ к таблице пользователей, но нет прямого доступа к таблицам заказов, проверка должна подтвердить, что он не может получить информацию о заказах других клиентов через JOIN с таблицей пользователей или через подзапрос. Например, запрос SELECT * FROM orders WHERE user_id IN (SELECT id FROM users) должен вернуть только заказы текущего пользователя, если политика RLS применена ко всем участвующим таблицам.

Необходимо проверять поведение при использовании агрегатных функций и группировки. Пользователь не должен видеть суммы или средние значения по всем клиентам в отчете, даже если он видит свои собственные данные. В PostgreSQL это достигается применением политики RLS к результатам подзапросов, что предотвращает вычисление глобальных метрик на основе данных всех клиентов.

Сценарий тестирования также включает проверку на ошибки и аномалии. Если политика настроена неверно, пользователь может получить ошибку доступа или увидеть данные другого клиента вместо своих. Важно зафиксировать все случаи, когда фильтрация не сработала корректно. Это часто происходит при изменении структуры базы данных: добавление нового поля в таблицу заказов без обновления соответствующей политики RLS может привести к тому, что фильтр перестанет применяться автоматически.

При миграции или обновлении схемы базы данных необходимо заново проверять влияние новых полей на существующие правила безопасности. Если в таблице появляется новое поле, которое ранее не учитывалось при фильтрации, политика RLS может начать работать иначе, чем планировалось. Регулярный аудит политик помогает выявлять такие устаревшие правила и потенциальные уязвимости до того, как они будут использованы злоумышленниками или приведут к утечке данных.

Практический пример: представьте, что вы добавили поле region в таблицу заказов. Если политика RLS фильтрует только по tenant_id, а не учитывает region, то пользователь из региона А может увидеть заказы из региона Б, если они принадлежат его клиенту. Это недопустимо. Поэтому при изменении схемы базы данных необходимо пересматривать логику фильтрации и обновлять политики безопасности.

Итогом тестирования является документированный отчет о том, что каждый пользователь видит только свои данные, а любые попытки доступа к чужим данным блокируются на уровне базы данных. Этот отчет служит доказательством соответствия системы требованиям конфиденциальности и защиты персональных данных.

Проверка влияния изменений структуры базы на политики безопасности

Изменение схемы базы данных или миграция часто приводят к появлению новых колонок, изменению типов данных или переструктурированию связей между таблицами. Если при этом не проверить воздействие этих изменений на существующие правила RLS, можно создать уязвимости, которые позволят пользователям видеть данные других клиентов.

При добавлении нового поля в таблицу необходимо убедиться, что оно включено в условия фильтрации правил безопасности. Например, если вы создали колонку `region_id` для хранения региональной принадлежности клиента, но забыли добавить её в условие политики `CHECK (tenant_id = current_setting('app.current_tenant')::uuid)`, то запросы без указания этого поля могут вернуть данные из всех регионов.

Особое внимание следует уделить таблицам-связям и промежуточным результатам. Если вы создали новое представление для агрегации данных, в котором забыли добавить фильтр по `tenant_id` в подзапросе, то итоговое представление станет доступным всем пользователям с правами на чтение этой таблицы, что нарушит изоляцию данных.

При изменении типа данных существующего поля (например, переход от `varchar` к `uuid`) важно проверить, не нарушат ли новые ограничения совместимость с логикой фильтрации. В некоторых случаях это может привести к тому, что правило RLS перестанет корректно сравнивать значения и вернет пустой набор или, наоборот, неверные строки.

Рекомендуется внедрить автоматизированный процесс проверки политик безопасности после каждого изменения схемы. Это можно реализовать через скрипты, которые сканируют все таблицы и представления на наличие колонок, участвующих в фильтрации, и сравнивают их со списком всех существующих полей.

Также стоит проводить ручную проверку сложных запросов, использующих JOIN между таблицами с разными политиками RLS. Иногда при объединении данных из нескольких источников правила безопасности могут конфликтовать или не применяться корректно к результирующей таблице.

Если вы используете внешние таблицы или подключаете данные через расширения (например, `pgvector` или интеграции с другими сервисами), убедитесь, что политики RLS распространяются и на эти источники. В противном случае утечка данных может произойти через побочные каналы доступа.

Документируйте каждое изменение схемы вместе с описанием проверенных политик безопасности. Это позволит быстро отследить, какие правила были затронуты, и упростит восстановление системы в случае возникновения проблем.

Регулярный аудит политик RLS

Политики безопасности не остаются статичными. Структура базы данных меняется, появляются новые роли пользователей, обновляются требования к конфиденциальности или меняются бизнес-процессы. Если политика доступа была настроена корректно на старте, со временем она может стать устаревшей из-за изменений в схеме или появления новых векторов атак.

Регулярный аудит позволяет выявлять ситуации, когда правила фильтрации перестали соответствовать реальности. Например, после добавления нового поля в таблицу заказов политика RLS могла не охватить его, что создаст риск утечки данных через подзапрос. Также стоит проверять, не стали ли слишком широкими права доступа у служебных пользователей или скриптов автоматизации.

Процесс аудита начинается с анализа текущего состояния политик и сравнения их с актуальной схемой базы. Необходимо проверить каждую таблицу на наличие правил фильтрации и убедиться, что они корректно используют идентификатор клиента (tenant_id) или роль пользователя. Особое внимание уделите представлениям и материализованным представлениям — часто именно через них происходит обход ограничений.

Используйте инструменты проверки целостности политик. В PostgreSQL можно запустить скрипт, который эмулирует запросы от разных пользователей с разными уровнями доступа и проверяет, возвращаются ли только ожидаемые строки. Если в результате теста появляются данные, которые пользователь не должен видеть, это сигнал к немедленной корректировке правил.

При выявлении устаревших политик важно не просто удалить их, а понять причину появления проблемы. Возможно, изменилась логика работы приложения или появились новые типы пользователей. Корректировка должна сопровождаться обновлением документации и повторным тестированием. Не игнорируйте предупреждения от системы безопасности — они часто указывают на потенциальные уязвимости в конфигурации политик.

Аудит также помогает оптимизировать производительность. Устаревшие или избыточные правила могут замедлять выполнение запросов, особенно если они содержат сложные условия фильтрации. Удаляйте неиспользуемые политики и упрощайте логику там, где это возможно без потери безопасности.

Внедряйте автоматизированные проверки политик в процесс разработки и поддержки базы данных. Это позволит及时发现 проблемы до того, как они повлияют на работу системы или приведут к инцидентам безопасности. Регулярность таких проверок зависит от частоты изменений в базе, но минимальный интервал — раз в квартал или после каждого крупного релиза.

Вопросы и ответы

Как проверить, что политика RLS работает корректно?

Создайте тестовые аккаунты с разными ролями (администратор, менеджер клиента, внешний пользователь) и выполните отладочные запросы. Используйте функцию `pg_catalog.pg_has_role` для проверки прав доступа и анализируйте логи БД на предмет попыток фильтрации строк.

Что делать, если политика RLS не фильтрует строки в JOIN-запросах?

Убедитесь, что поле `tenant_id` присутствует во всех таблицах участвующих в соединении. Если оно отсутствует в одной из таблиц, добавьте его или измените политику так, чтобы она учитывала контекст соединения через внешние ключи.

Можно ли использовать RLS для изоляции данных между разными версиями PostgreSQL?

Нет. РLS работает только внутри одной инстансы базы данных. Для разделения по версиям или окружениям используйте отдельные кластеры или контейнеры.

-- Создаём политику для таблицы orders
-- Она фильтрует строки по tenant_id текущего пользователя
CREATE POLICY tenant_isolation_policy ON orders
USING (tenant_id = current_setting('app.current_tenant_id', true)::integer);

-- Включаем RLS для таблицы
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

-- Пример запроса от пользователя с tenant_id = 10
-- Система автоматически добавит условие: WHERE tenant_id = 10
SELECT * FROM orders;