PRODСуверенная европейская платформа BaaSОткрыть панель управления →

Инженерное дело · 10 минута чтения

RLS против выделенной базы данных для каждого проекта

Affane Daylami · Fondateur · 27 июля 2026 г.

Вернуться в блог

Поищите «RLS multi-tenant postgres», и вы почти везде встретите одну и ту же схему: общую базу данных, tenant_idcolumn, политику, которая фильтрует строки. Это не та модель, которую Aurabase использует для изоляции своих проектов друг от друга. Каждый проект получает свою собственную базу данных Postgres 16, которая никогда не используется другим клиентом — RLS остается там, но на другом этаже: в вашей базе данных, для ваших собственных пользователей.

Этот текст на английском языке был создан автоматически на основе французского оригинала и еще не проверялся.
Эта страница была переведена автоматически. Английская версия является авторитетной.

«RLS» и «мультиарендатор» встречаются рядом почти во всем контенте, уже опубликованном по этой теме – законный выбор для многих архитектур SaaS, но это не выбор Aurabase для отделения своих клиентов друг от друга. В этом посте объясняется разница между реальным механизмом обеспечения и фактически применяемыми политиками RLS, а не упрощенное маркетинговое описание. Обзор того, что обеспечивает механизм Managed Postgres от Aurabase, помимо изоляции, см. в документации по базе данных .

Самое необходимое

  • Между проектами Aurabase изолирует выделенную базу Postgres , а не только RLS — каждый проект имеет свою собственную физическую базу в выделенном кластере CNPG (уровень компании) или в кластере CNPG своей собственной организации (бесплатный/профессиональный/командный), которая никогда не используется совместно с другой организацией.
  • RLS (auth.uid(), auth.role(), auth.jwt()) остается активным и рекомендуется внутри вашей базы, чтобы изолировать ваших собственных пользователей — то же соглашение, что и в Supabase.
  • service_role и роли администрирования на основе проектов намеренно обходят RLS (BYPASSRLS): предполагаемый архитектурный выбор для операций сервера, а не недостаток.
  • Регрессия, уже исправленная в этом репозитории — права PUBLIC, предоставленные по ошибке на старых общих схемах — конкретно иллюстрирует, почему граница на базовом уровне сопротивляется лучше, чем чисто граница приложения.
#
Модель по умолчанию

Ярлык, который используют большинство руководств по многопользовательскому RLS

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

Компромисс реален: граница между двумя клиентами становится выражением SQL , вычисляемым таблица за таблицей. Забытая политика для новой таблицы, соединение, запущенное с ролью суперпользователя, запущенный в реальном времени сценарий отладки — каждый из этих инцидентов, каким бы тривиальным он ни был в работе, может незаметно раскрыть строки всех арендаторов одновременно. Граница безопасности и техническая граница (основа) — это одно и то же.

Информация

Это неплохой выбор сам по себе — это правильный компромисс для многих продуктов. Суть этой статьи в другом: это не тот компромисс, на который пошла Aurabase, чтобы отделить своих собственных клиентов (целые проекты, потенциально с разными требованиями соответствия) друг от друга.

#
В коде

Две архитектуры, никогда не общая база между проектами

После недавней консолидации поставщика услуг (отмеченной в коде как «Задача 12») активный проект Postgres в Aurabase подпадает ровно под две архитектуры — старые модели с базой, фактически используемой несколькими проектами, были удалены из пути обеспечения.

provisioning/instances.rsrust
pub enum ProjectInstanceKind {
    /// Кластер CNPG, посвященный ТОЛЬКО ЭТОМУ проекту (уровень компании).
    FullyDedicated,

    /// База данных по кластеру CNPG проектной ОРГАНИЗАЦИИ —
    /// совместно с ДРУГИМИ проектами ТОЙ ЖЕ организации,
    /// никогда со сторонней организацией.
    SharedClusterDedicated,
}

Уровень проекта решает, какой из двух вариантов применим — и решает код, а не флажок, установленный на панели мониторинга:

provisioning/rules.rsrust
fn should_provision_dedicated(engine: &str, db_url_encrypted: &Option<String>, plan: &str) -> bool {
    matches!(engine, "postgres" | "postgresql")
        && plan == "enterprise"
        && !non_empty(db_url_encrypted)
}

// must_route_to_fleet(): любой уровень, не относящийся к компании (бесплатный/профессиональный/командный)
// маршрут к кластеру CNPG ВАШЕЙ СОБСТВЕННОЙ организации, а не к одному
// из другого — см. миграцию 073, объединение на 2 архитектуры.
РазмерыПолностью выделенный (компания)SharedClusterDedicated (бесплатно/для профессионалов/для команд)
Кластер CNPGПосвящается этому проектуСовместно, но никогда между двумя организациями
База данных Postgresприложение, проецируйте только на негоproject_<uuid>, по одному на проект в кластере
Вход в PostgreSQLЕдиный проект в кластере: нет риска перекрестного членстваВход для каждого проекта (F-013), член его единственной роли tenant_<uuid>

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

Такое объединение двух архитектур произошло недавно: ранее код содержал два дополнительных пути — «общий мастер», где несколько проектов сосуществовали в одной базе данных, изолированных только диаграммой, и вариант postgrest_dedicated_shared_db. Специальная миграция удалила их и ужесточила ограничение таблицы projects только до двух оставшихся значений именно потому, что модель общей схемы была источником описанной ниже ошибки.

#
Граница безопасности

Почему выделенная база лучше АРБ, совместно используемого клиентами

Отдельная база данных Postgres — это граница на уровне соединения, а не граница на уровне строк. Роль приложения, подключенная к базе данных проекта А, просто не может запрашивать таблицы проекта Б — у нее нет открытой для нее сессии. Это свойство сохраняется, даже если политика RLS написана плохо, отсутствует в таблице или обойдена высокой ролью: в худшем случае политика RLS остается ограниченной одной базой данных.

Этот депозит также несет в себе следы реальной ошибки, которая иллюстрирует противоположный риск. В соответствии со старой моделью общей схемы (которая была отменена) provision_postgres_schema по ошибке предоставил GRANT ALL ... TO PUBLIC права на каждую схему проекта - PUBLIC применил к все роли в базе данных без условий членства, вход для входа, изолированный для каждого проекта, мог читать и писать в схеме другого. Корректирующая миграция (066) удалила эти права из существующего.

Урок усвоен

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

Патч от 23 августа 2026 года идет в том же направлении: REVOKE ALL ON SCHEMA public, безоговорочно размещенный поставщиком услуг, был поставлен в зависимость от топологии, поскольку он обеспечивал реальную изоляцию только в старой модели общей базы данных — на двух текущих архитектурах он без всякой пользы блокировал импорт дампов SQL, которые явно ссылаются на public.<table>.

#
СБН на практике

АРБ остается там — в вашей базе данных, для ваших пользователей.

Ничто из вышеперечисленного не делает АРБ бесполезным — он просто меняет этажи. Попав в вашей базы данных проекта, Aurabase предоставляет именно то соглашение PostgREST, которое используется в Supabase: три функции SQL, которые считывают утверждения JWT, установленные шлюзом в request.jwt.claims.

libs/aura-migrations/golden/auth_global.sqlsql
create function auth.uid() returns uuid
    language sql stable security definer
    set search_path to 'pg_catalog', 'pg_temp'
    as $$
    select nullif(nullif(current_setting('request.jwt.claims', true), '')::json->>'sub', '')::uuid
$$;

Эти помощники используются в реальных политиках самой Aurabase, а не только в вашей документации. Вот политика, защищающая storage_objectsв том виде, в котором она размещена в репозитории (переформатирована на несколько строк для чтения):

libs/aura-migrations/golden/storage.sqlsql
alter table storage_objects enable row level security;

create policy storage_objects_select on storage_objects
  for select using (
    (owner_id = auth.uid())
    or (
      exists (
        select 1 from storage_buckets b
        where (b.name = storage_objects.bucket_name and b.public)
      )
    )
  );

В вашей собственной схеме project_<uuid>, которая содержит таблицы вашего приложения, Aurabase намеренно не размещает никаких политик от вашего имени — код документирует это как «модель Supabase»: RLS ваших таблиц остается под вашей ответственностью, с теми же функциями и тем же синтаксисом.

#
Предполагается ОБХОД BYPASSRLS

service_role обходит RLS — намеренно, а не случайно

Postgres изначально предлагает атрибут роли , BYPASSRLS, который игнорирует все политики. Aurabase добровольно использует его в двух семействах ролей: aura_service_role (роль сервера, никогда не отображаемая на стороне браузера) и роль администрирования, специфичная для каждого проекта, используемая во время операций DDL, таких как ALTER SCHEMA ... OWNER TO.

provisioning/roles.sqlsql
create role aura_service_role nologin bypassrls;
-- Роль сервера: никогда не отображается на стороне браузера, никогда не является членом
-- роли из другого проекта.

Роль, обслуживающая ваши запросы anon/authenticated — tenant_<uuid> — не имеет никакого BYPASSRLS: RLS к ней применяется нормально, без исключений. В качестве бонуса системные диаграммы, специфичные для каждого проекта (_auth, _storage, _platform), получают активированный RLS без политики — поэтому по умолчанию полный отказ для любой роли без обхода, глубокая защита на случай, если путь приложения однажды по ошибке получит к нему доступ.

Информация

Обход RLS с помощью роли сервера с повышенными правами не уникален для Aurabase — это та же конструкция, что и service_role на стороне Supabase. Дело не в том, чтобы избегать BYPASSRLS, а в том, чтобы никогда не предоставлять его роли, доступной из клиента, и ограничивать его одним проектом.

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

#
Глубокоэшелонированная защита

В общем кластере база данных не выполняет всю работу в одиночку.

На уровне SharedClusterDedicatedнесколько проектов одной организации сосуществуют в одном кластере CNPG. Физическая база данных уже отделяет проекты друг от друга, но роли PostgreSQL — они — являются глобальными объектами для кластера, а не для базы данных. Поэтому Aurabase добавляет уровень: отдельный логин PostgreSQL для каждого проекта.

libs/aura-core/src/lib.rsrust
pub fn project_authenticator_role(project_id: &str) -> String {
    format!("project_{}_authenticator", project_id.replace('-', '_'))
}

// Активен по умолчанию (F013_PER_PROJECT_AUTHENTICATOR), деактивируемый
// явно как аварийный выход.

Каждый проект подключается со своей собственной учетной записью, являясь членом только своих собственных ролей tenant_<uuid> / tenant_<uuid>_admin, а не ролей другого проекта в том же кластере. База данных уже изолирует данные; Этот вход для каждого проекта также изолирует удостоверение, которое с ним связано, так что инцидент в проекте не дает его входу какого-либо членства, которое можно было бы наследовать другому.

#
Для вашей архитектуры

Один RLS или выделенная база: как выбрать собственный SaaS

Выбор Aurabase — не универсальное правило — это компромисс для конкретного случая: изоляции клиентов друг от друга, потенциально с разными требованиями комплаенса, на платформе, которую они не контролируют. Если вы строите свой собственный SaaS, перед вами возникает тот же вопрос, но в другом масштабе.

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

Уровень цен Aurabase применяет тот же арбитраж к своим клиентам: общая база по умолчанию для организации, выделенный кластер, когда это оправдано сложностью проекта. Для шаблонов RLS внутри вашей собственной базы данных — владение, мультитенантность по организации, иерархия ролей — в руководстве RLS в рабочей среде подробно описаны три случая с тестами pgTAP. А если автоматически сгенерированный API на вашей диаграмме интересует вас за пределами REST, сравнение pg_graphql с Hasura и PostGraphile охватывает другую половину поверхности Postgres, предоставляемой Aurabase.

#
Часто задаваемые вопросы

Часто задаваемые вопросы

Достаточно ли одного RLS для изоляции клиентов в одной базе данных Postgres?+
Технически да, если каждая таблица имеет правильную политику и ни одно соединение не выходит из-под роли необхода. Это именно тот операционный риск, который Aurabase решила не брать на себя при переходе между различными проектами — каждая ошибка политики останется ограниченной одной базой. В рамках одного проекта для ваших собственных арендаторов RLS + tenant_id остается законным и широко используемым выбором.
Почему в бесплатных проектах нет выделенного кластера Postgres, как на корпоративном уровне?+
Выделенный кластер CloudNativePG для каждого проекта, в том числе для бесплатных проектов, умножит зарезервированные вычислительные ресурсы без какой-либо связи с фактическим использованием большинства из них. Вместо этого Aurabase предоставляет каждому некорпоративному проекту собственную физическую базу данных Postgres в кластере CNPG своей организации, которая никогда не используется совместно с другой организацией, а не схему в общей базе данных нескольких клиентов, неизвестных друг другу.
Откуда auth.uid() узнает, кто я такой технически?+
Шлюз Aurabase проверяет ваш JWT, а затем помещает свои утверждения в параметр сеанса Postgres request.jwt.claims на время транзакции. auth.uid() не делает ничего, кроме извлечения подполя из этого JSON и преобразования его в uuid — без заявлений (анонимный запрос или прямое соединение за пределами шлюза) current_setting() возвращает NULL, а auth.uid() поэтому возвращает NULL, что закрывает доступ к политике с использованием (owner_id = auth.uid()).

ГОТОВЫ К РАЗВЕРТЫВАНИЮ?

Ваш бэкэнд за пять минут.

Кредитная карта не требуется · 500 МБ бесплатно · 50 000 MAU