PRODSoeverein Europees BaaS-platformOpen Dashboard →

Techniek · 10 min gelezen

RLS versus een speciale database per project

Affane Daylami · Fondateur · 27 juli 2026

Terug naar blog

Zoek naar “RLS multi-tenant postgres” en je komt bijna overal hetzelfde schema tegen: een gedeelde database, een tenant_idcolumn, een beleid dat de rijen filtert. Dit is niet het model dat Aurabase gebruikt om haar projecten van elkaar te isoleren. Elk project krijgt zijn eigen Postgres 16-database, die nooit met een andere klant wordt gedeeld. De RLS blijft daar, maar op een andere verdieping: in uw database, voor uw eigen gebruikers.

Deze Engelse tekst is automatisch gegenereerd op basis van het Franse origineel en is nog niet beoordeeld.
Deze pagina is automatisch vertaald. De Engelse versie is gezaghebbend.

“RLS” en “multi-tenant” worden naast elkaar aangetroffen in bijna alle inhoud die al over dit onderwerp is gepubliceerd – een legitieme keuze voor veel SaaS-architecturen, maar wat niet de keuze van Aurabase is om zijn eigen klanten van elkaar te scheiden. In dit bericht wordt het verschil uitgelegd met de echte provisioning-engine en het daadwerkelijk toegepaste RLS-beleid, en niet een vereenvoudigde marketingbeschrijving. Voor een overzicht van wat de Managed Postgres-engine van Aurabase verder omvat dan isolatie, zie de databasedocumentatie.

De essentie

  • Tussen projecten door isoleert Aurabase door speciale Postgres-basis, nooit door RLS alleen – elk project heeft zijn eigen fysieke basis, op een speciaal CNPG-cluster (bedrijfsniveau) of op het CNPG-cluster van zijn eigen organisatie (gratis/pro/team), nooit gedeeld met een andere organisatie.
  • De RLS (auth.uid(), auth.role(), auth.jwt()) blijft actief en wordt aanbevolen in uw-basis, om uw eigen gebruikers te isoleren — dezelfde conventie als Supabase.
  • service_role en projectgebaseerde beheerrollen omzeilen RLS by design (BYPASSRLS): een veronderstelde architecturale keuze voor serverbewerkingen, geen fout.
  • Een regressie die al in deze repository is gecorrigeerd - PUBLIC rechten die ten onrechte zijn verleend op oude gedeelde schema's - illustreert concreet waarom een grens op het basisniveau beter weerstand biedt dan een puur applicatiegrens.
#
Het standaardmodel

De snelkoppeling die de meeste RLS-handleidingen voor meerdere tenants gebruiken

Het meest gedocumenteerde patroon voor Postgres met meerdere tenants bestaat uit drie regels: een enkele basis, een kolom tenant_id in elke tabel, een RLS-beleid dat deze kolom vergelijkt met een waarde die is geëxtraheerd uit de JWT. Het is economisch – een verzameling verbindingen, een diagram, één exemplaar om uit te voeren – en het werkt goed als de huurders talrijk, klein en met een lage individuele inzet zijn.

Het compromis is reëel: de grens tussen twee clients wordt een SQL-expressie, tabel voor tabel geëvalueerd. Een beleid dat is vergeten op een nieuwe tafel, een verbinding die draait met een superuser-rol, een debug-script dat live wordt gelanceerd - elk van deze incidenten, hoe triviaal ook in werking, kan in stilte de regels van alle tenants tegelijkertijd blootleggen. De veiligheidsgrens en de technische grens (de basis) zijn dan precies hetzelfde.

Info

Dit is op zichzelf geen slechte keuze; het is het juiste compromis voor veel producten. Het punt van dit bericht ligt ergens anders: dit is niet het compromis dat Aurabase heeft gesloten om zijn eigen klanten (hele projecten, mogelijk met verschillende compliance-eisen) van elkaar te scheiden.

#
In de code

Twee architecturen, nooit een gedeelde basis tussen projecten

Sinds een recente consolidatie van de provisioner (in de code gemarkeerd als "Taak 12") valt een actief Postgres-project bij Aurabase onder precies twee architecturen: de oude modellen met een basis die feitelijk door verschillende projecten wordt gedeeld, zijn uit het provisioningpad verwijderd.

provisioning/instances.rsrust
pub enum ProjectInstanceKind {
    /// CNPG-cluster gewijd aan DIT ALLEEN-project (bedrijfsniveau).
    FullyDedicated,

    /// Database over de CNPG-cluster van het project ORGANISATIE —
    /// gedeeld met ANDERE projecten van DEZELFDE organisatie,
    ///nooit met een derde partij.
    SharedClusterDedicated,
}

Het projectniveau bepaalt welke van de twee van toepassing is – en het is de code die beslist, niet een vakje dat is aangevinkt in een dashboard:

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)
}

// Should_route_to_fleet(): elk niet-bedrijfsniveau (gratis/pro/team)
// route naar de CNPG-cluster van UW EIGEN organisatie, nooit die ene
// van een andere — zie migratie 073, consolidatie naar 2 architecturen.
AfmetingenFullyDedicated (bedrijf)SharedClusterDedicated (gratis/pro/team)
CNPG-clusterToegewijd aan dit ene projectGedeeld, maar nooit tussen twee organisaties
Postgres-databaseapp, projecteer er alleen opproject_<uuid>, één per project in het cluster
PostgreSQL-aanmeldingEén project op het cluster: geen risico op kruislidmaatschapInloggen per project (F-013), lid van de enige rollen tenant_<uuid>

Op beide architecturen huisvest de basis of het cluster nooit twee verschillende organisaties. De vraag is dus niet “zijn uw gegevens geïsoleerd” maar “heeft uw project de beschikking over CloudNativePG geheel voor zichzelf, of deelt het deze met andere projecten in dezelfde organisatie.”

Deze consolidatie van twee architecturen is recent: de code kende voorheen twee extra paden: een ‘gedeelde master’ waarbij verschillende projecten naast elkaar in dezelfde database bestonden, alleen geïsoleerd door middel van een diagram, en een postgrest_dedicated_shared_db-variant. Een speciale migratie verwijderde deze en verscherpte de beperking van de tabel projects tot alleen de twee resterende waarden, juist omdat het gedeelde schemamodel de bron was van de hieronder beschreven bug.

#
Beveiligingsgrens

Waarom een toegewijde basis beter is dan een EPIRB die door klanten wordt gedeeld

Een afzonderlijke Postgres-database is een grens op verbindingsniveau, geen grens op rijniveau. Een applicatierol die is verbonden met de database van project A kan simpelweg geen query uitvoeren op de tabellen van project B; er is geen sessie voor open. Deze eigenschap geldt zelfs als een RLS-beleid slecht is geschreven, ontbreekt in een tabel of wordt omzeild door een hoge rol: in het ergste geval blijft het beperkt tot één database.

Deze aanbetaling vertoont ook de sporen van een echte bug, wat het tegenovergestelde risico illustreert. Onder het oude gedeelde schemamodel (sindsdien ingetrokken), heeft provision_postgres_schema ten onrechte GRANT ALL ... TO PUBLIC rechten toegekend aan elk projectschema - PUBLIC door op alle rollen in de database toe te passen zonder lidmaatschapsvoorwaarden, kon een geïsoleerde login per project het schema van een ander project lezen en schrijven. Een corrigerende migratie (066) verwijderde deze rechten uit de bestaande.

De geleerde les

De patch voegde niet nog een RLS-beleid toe om het lek te dichten; het elimineerde de mogelijkheid dat twee projecten een database zouden delen. Over de twee huidige architecturen wordt het in een commentaar van provisioning.rs zwart op wit gedocumenteerd: “elk project heeft al zijn eigen fysieke Postgres-database”. Een grens op basisniveau maakt een hele klasse van dergelijke bugs eenvoudigweg onbereikbaar, in plaats van erop te vertrouwen dat elk beleid altijd correct wordt geschreven.

Een patch van 23 augustus 2026 gaat in dezelfde richting: een REVOKE ALL ON SCHEMA public die onvoorwaardelijk door de provisioner werd geplaatst, werd voorwaardelijk gemaakt op basis van de topologie, omdat deze alleen echte isolatie bood op het oude gedeelde databasemodel - op de twee huidige architecturen blokkeerde deze zonder enig voordeel de import van SQL-dumps die expliciet verwijzen naar public.<table>.

#
RLS in de praktijk

EPIRB blijft daar — in uw database, voor uw gebruikers

Geen van de bovenstaande dingen maakt EPIRB nutteloos; het verandert alleen de vloer. Eenmaal in uw projectdatabase, geeft Aurabase precies de PostgREST-conventie weer die wordt gebruikt door Supabase: drie SQL-functies die de JWT-claims lezen die zijn ingesteld door de gateway in 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
$$;

Deze helpers worden gebruikt in het daadwerkelijke beleid van Aurabase zelf – en niet alleen gedocumenteerd voor dat van u. Hier is het beleid dat storage_objectsbeschermt, zoals het in de repository is geplaatst (opnieuw geformatteerd op verschillende regels om te kunnen lezen):

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)
      )
    )
  );

In uw eigen project_<uuid>-schema, dat uw applicatietabellen bevat, plaatst Aurabase opzettelijk geen beleid namens u – de code documenteert het als een “Supabase-model”: de RLS van uw tabellen blijft uw verantwoordelijkheid, met dezelfde functies, dezelfde syntaxis.

#
BYPASSRLS aangenomen

service_role omzeilt RLS - door ontwerp, niet per ongeluk

Postgres biedt standaard een rolkenmerk, BYPASSRLS, dat al het beleid negeert. Aurabase gebruikt het vrijwillig voor twee rollenfamilies: aura_service_role (de serverrol, nooit zichtbaar aan de browserzijde) en de beheerrol die specifiek is voor elk project, gebruikt tijdens DDL-bewerkingen zoals ALTER SCHEMA ... OWNER TO.

provisioning/roles.sqlsql
create role aura_service_role nologin bypassrls;
-- Serverrol: nooit zichtbaar aan de browserzijde, nooit lid
-- rollen uit een ander project.

De rol die uw verzoeken vervult anon/authenticated — tenant_<uuid> — heeft geen BYPASSRLS: de RLS is er normaal gesproken op van toepassing, zonder uitzondering. Als bonus ontvangen de systeemdiagrammen die specifiek zijn voor elk project (_auth, _storage, _platform) een geactiveerde RLS zonder beleid - dus standaard een totale weigering voor elke niet-bypass-rol, een diepgaande verdediging voor het geval een applicatiepad er op een dag per ongeluk toegang toe krijgt.

Info

Het omzeilen van de RLS met een verhoogde serverrol is niet uniek voor Aurabase – het is dezelfde constructie als service_role aan de Supabase-kant. Het punt is niet om BYPASSRLSte vermijden, maar om het nooit toe te kennen aan een rol die toegankelijk is vanaf een client, en om het te beperken tot één enkel project.

Deze serverrol maakt deel uit van een bredere structuur – vooraf gedefinieerde rollen, aangepaste RBAC, auditlogboeken – gedetailleerd op de pagina Beveiliging en RBAC.

#
Verdediging in de diepte

Op een gedeeld cluster doet de database niet al het werk alleen

Op het SharedClusterDedicated-niveau bestaan verschillende projecten van dezelfde organisatie naast elkaar op één CNPG-cluster. De fysieke database scheidt de projecten al van elkaar, maar de PostgreSQL-rollen (zij) zijn globale objecten voor het cluster, niet voor de database. Aurabase voegt daarom een ​​laag toe: een aparte PostgreSQL login per project.

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

// Standaard actief (F013_PER_PROJECT_AUTHENTICATOR), deactiveerbaar
// uitdrukkelijk als nooduitgang.

Elk project maakt verbinding met zijn eigen login, die alleen lid is van zijn eigen tenant_<uuid> / tenant_<uuid>_admin rollen - nooit die van een ander project in hetzelfde cluster. De database isoleert de gegevens al; Deze login per project isoleert ook de identiteit die ermee verbonden is, zodat een incident in een project de login geen lidmaatschap geeft dat kan worden overgenomen van een ander project.

#
Voor uw architectuur

RLS alleen of een dedicated basis: hoe u kiest voor uw eigen SaaS

De keuze voor Aurabase is geen universele regel – het is een compromis voor een specifiek geval: klanten van elkaar isoleren, mogelijk met verschillende compliance-eisen, op een platform waarover zij geen controle hebben. Als u uw eigen SaaS bouwt, rijst voor u dezelfde vraag, op een andere schaal.

  • RLS met tenant_id in een gedeelde basis — relevant wanneer uw huurders talrijk zijn, individueel met een lage inzet, en de kosten van een basis per huurder onevenredig zouden zijn. Test elk beleid met pg_proveop elke tafel, zonder uitzondering.
  • Speciale basis of diagram — relevant wanneer een huurder zijn eigen nalevingsprobleem heeft (gezondheid, HR, publieke sector), een volume dat prestatie-isolatie rechtvaardigt, of de kosten van een lek tussen twee specifieke clients niet in verhouding staan tot de kosten van extra infrastructuur.

Het prijsniveau van Aurabase past dezelfde arbitrage toe op zijn eigen klanten: standaard een gedeelde basis per organisatie, een speciaal cluster wanneer de uitdaging van het project dit rechtvaardigt. Voor RLS-patronen binnen uw eigen database (eigendom, multi-tenant per organisatie, rollenhiërarchie) beschrijft de RLS-handleiding in productie de drie gevallen met pgTAP-tests. En als de automatisch gegenereerde API in uw diagram u verder interesseert dan REST, dekt de vergelijking op pg_graphql versus Hasura en PostGraphile de andere helft van het Postgres-oppervlak dat door Aurabase wordt belicht.

#
Veelgestelde vragen

Veelgestelde vragen

Is RLS alleen voldoende om tenants in één Postgres-database te isoleren?+
Technisch gezien wel, als elke tabel het juiste beleid heeft en er geen verbinding is die aan de niet-bypass-rol ontsnapt. Dit is precies het operationele risico dat Aurabase tussen verschillende projecten niet wilde nemen: elke beleidsfout zou beperkt blijven tot één enkele basis. Binnen één project blijft RLS + tenant_id voor uw eigen tenants een legitieme en veelgebruikte keuze.
Waarom hebben gratis projecten geen speciaal Postgres-cluster zoals het ondernemingsniveau?+
Een speciaal CloudNativePG-cluster per project, ook voor gratis projecten, zou de gereserveerde rekenkracht vermenigvuldigen zonder enige relatie met het daadwerkelijke gebruik van de meerderheid ervan. Aurabase geeft in plaats daarvan elk niet-ondernemingsproject zijn eigen fysieke Postgres-database op het CNPG-cluster van zijn eigen organisatie – nooit gedeeld met een andere organisatie – in plaats van een schema in een gemeenschappelijke database van verschillende klanten die elkaar niet kennen.
Hoe weet auth.uid() technisch gezien wie ik ben?+
De Aurabase-gateway verifieert uw JWT en plaatst vervolgens zijn claims in de Postgres-sessieparameter request.jwt.claims, voor de duur van de transactie. auth.uid() doet niets anders dan het subveld uit deze JSON extraheren en naar uuid casten - zonder gemaakte claims (anoniem verzoek of directe verbinding buiten een gateway), current_setting() retourneert NULL en auth.uid() retourneert daarom NULL, waardoor de toegang tot een beleid wordt gesloten met behulp van (owner_id = auth.uid()).

KLAAR VOOR IMPLEMENTATIE?

Uw backend in vijf minuten.

Geen creditcard vereist · 500 MB gratis · 50.000 MAU