Tenant Isolation Tiers: Pool Versus Silo Controls on Shared AWS Accounts

Tenant Isolation Tiers: Pool Versus Silo Controls on Shared AWS Accounts

Not every SaaS customer needs a dedicated account—but enterprise tenants do need stronger than WHERE tenant_id = ?. The unfair advantage is an explicit tier ladder (pool → bridge → silo) with IAM, KMS, and network controls that escalate with contract risk.

⚡ TL;DR: Pool = shared tables + enforced tenant predicates + app authz; Bridge = per-tenant KMS CMK + IAM path + noisy-neighbor limits; Silo = dedicated account/VPC/cell. Encode the tier in provisioning, not wiki tribal knowledge. Pair with Cell-Based Architecture on AWS and Bedrock Retrieval Filters: Tenant Isolation.

Tier definitions that survive sales calls

Tier Compute Data Keys When
Pool Shared ECS/Lambda Shared table, tenant_id Account CMK SMB / free
Bridge Shared + reserved concurrency Shared table + per-tenant S3 prefix Per-tenant CMK Mid-market
Silo Dedicated cell/account Dedicated DB/bucket Tenant-owned CMK Enterprise / regulated
// ✅ Central guard — every data access carries TenantContext
export type Tier = "pool" | "bridge" | "silo";

export interface TenantContext {
  tenantId: string;
  tier: Tier;
  kmsKeyArn: string;
}

export function assertTenantRow(row: { tenant_id: string }, ctx: TenantContext) {
  if (row.tenant_id !== ctx.tenantId) {
    throw new Error("tenant_isolation_violation"); // ✅ fail closed
  }
}

IAM and KMS for bridge tier

// ✅ S3 prefix isolation with session tags / inline session policy
{
  "Effect": "Allow",
  "Action": ["s3:GetObject", "s3:PutObject"],
  "Resource": "arn:aws:s3:::saas-data/${aws:PrincipalTag/tenant_id}/*"
}
# ✅ Encrypt with per-tenant CMK; never default to account key for bridge+
import boto3
s3 = boto3.client("s3")

def put_object(ctx, key: str, body: bytes) -> None:
    s3.put_object(
        Bucket="saas-data",
        Key=f"{ctx.tenant_id}/{key}",
        Body=body,
        ServerSideEncryption="aws:kms",
        SSEKMSKeyId=ctx.kms_key_arn,  # ✅ tenant CMK
    )

❌ Relying only on application filters without IAM/KMS boundaries for bridge/silo — one SQLi-shaped bug becomes a multi-tenant breach.

Noisy neighbors and blast radius

Pool tiers still need bulkheads: per-tenant rate limits (Multi-Tenant Rate Limits), reserved Lambda concurrency, and cell placement so a silo outage does not take pool customers.

Closing checklist

✅ Dos
– ✅ Document tier → controls matrix in an ADR
– ✅ Provision KMS keys and IAM paths automatically on upgrade
– ✅ Fail closed on tenant mismatch
– ✅ Load-test noisy-neighbor controls per tier
– ✅ Align contracts/DPA with actual technical tier

❌ Don’ts
– ❌ Don’t sell “dedicated” while sharing CMKs
– ❌ Don’t skip integration tests that attempt cross-tenant reads
– ❌ Don’t put all enterprise silos in one cell
– ❌ Don’t let support tooling bypass tenant guards without audit

Related reading

Last updated on September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply