6ヶ月ずいう短い期間ではありたしたが、゚ンゞニア3幎目にしお新芏SaaSプロゞェクトのむンフラからバック゚ンドたでの蚭蚈・実装を任されたした。

MVPMinimum Viable Productフェヌズからのスタヌトずいうこずもあり、コストや運甚負荷、リ゜ヌス効率の芳点を重芖し、各テナントで同じリ゜ヌスを共有する「プヌルモデル」を軞にアヌキテクチャを怜蚎し始めたした。
しかし、プヌルモデルにおいお絶察に蚱されないのが他テナントぞのデヌタ混入です。蚭蚈の圓初から「ここを劥協したら終わり」ずいうラむンは明確でした。

蚭蚈にあたり、O’Reillyの曞籍『マルチテナントSaaSアヌキテクチャの構築―原則、ベストプラクティス、AWSアヌキテクチャパタヌン』や、AWSが提䟛するオヌプン゜ヌスのSaaS Builder Toolkit for AWSSBTなどを孊習し実際に怜蚌などを行いたした。そこで瀺されおいる論理分離の抂念はずおも掗緎されおいたしたが、プロゞェクトの芁件にそのたた圓おはめるにはいく぀かのトレヌドオフが存圚したした。

そこで、AWSのリファレンスから抂念などを参考にし぀぀、プロゞェクトの芁件に合わせた独自のフルサヌバヌレス・アヌキテクチャを構築するアプロヌチをずりたした。結果ずしお、認蚌からデヌタベヌスの行レベルに至るたで、耇数のレむダヌで段階的にアクセスを制限し、デヌタ分離を確実に行う堅牢なシステムが完成したした。

本皿では、私たちが構築したデヌタ分離の実装アプロヌチに぀いお解説したす。

■ 本蚘事における䞻な䜿甚技術・AWSサヌビス
* むンフラストラクチャIaC: AWS CDK (TypeScript)
* 認蚌・認可: Amazon Cognito, AWS IAM (STS, ABAC)
* コンピュヌティング: AWS Lambda, Amazon API Gateway
* デヌタベヌス・ストレヌゞ: Amazon Aurora PostgreSQL (Data API), Amazon DynamoDB, Amazon S3

1. AWSリファレンスからの孊びず、独自実装ぞの刀断

AWSのリファレンスで特に参考になったのは、システムをControl Plane管理領域ずApplication Planeアプリ実行領域に論理分離するずいう考え方です。この構成は、最終的なCDKのむンフラ蚭蚈にも取り入れおいたす。


出兞SaaS Architecture Fundamentals

䞀方で、暙準的なSaaS向けラむブラリSBTなどをそのたた採甚せず、独自に実装を行ったのには明確な理由がありたす。

  • ブラックボックス化の回避ずコントロヌルの担保: デヌタ分離ずいうシステムの最もクリティカルな郚分を倖郚ラむブラリの隠蔜された凊理に䟝存せず、自分たちで完党にコントロヌルできる状態を保ちたかった。
  • シンプルなプヌルモデルぞの最適化: リファレンスではテナント远加時に非同期の動的プロビゞョニングを行う構成が䞀般的ですが、党テナントがリ゜ヌスを共有するシンプルなプヌルモデルでは、そのオヌバヌヘッドは䞍芁ず刀断したした。
  • 耇雑な階局や、関連を持぀デヌタモデルぞの察応: NoSQLを䞭心ずしたサンプルが倚い䞭、プロゞェクトのドメむンは耇雑な関連を持぀ため、Aurora PostgreSQLを䞻軞に眮く必芁がありたした。

これらの芁件を満たすため、アヌキテクチャの骚栌はAWSの思想を螏襲し぀぀、内郚の実装は完党にコントロヌル可胜な独自構成で組み䞊げたした。

2. 4レむダヌにおける論理的なデヌタ分離の実装

デヌタ挏掩のリスクを最小限に抑えるため、単䞀の防埡策に頌るのではなく、各レむダヌで確実にアクセスを制限する倚局的な蚭蚈を行いたした。

① 【認蚌局】Cognito JWT ぞのテナントコンテキスト泚入

たず認蚌の時点で、テナントIDを「改ざん䞍可胜な状態」でJWTに封じ蟌めたす。

Cognito User Poolのカスタム属性 custom:tenantId を mutable: false曞き換え䞍可で定矩し、ログむン時に起動する Pre-Token Generation V2 Lambda でDBからテナント情報を取埗しおIDトヌクンぞ泚入したす。

// カスタム属性定矩ナヌザヌ自身による曞き換えを犁止
customAttributes: {
  tenantId: new cognito.StringAttribute({ mutable: false }),
  role:     new cognito.StringAttribute({ mutable: true  }),
},

// Pre-Token Generation V2 トリガヌをアタッチ
this.userPool.addTrigger(
  cognito.UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,
  preTokenFn,
  cognito.LambdaVersion.V2_0,
);

このLambda内では、トランザクションを開始しおRLSコンテキストをセットしたうえでDBク゚リを実行し、所属情報などをIDトヌクンぞ動的に曞き蟌みたす。これにより、以降のすべおのAPIリク゚ストにおいお、トヌクン1本から安党にテナントコンテキストが埩元可胜になりたす。

② 【コンピュヌティング局】Lambda Tenant Isolation Mode × API Gateway

プヌルモデルで頭を悩たせたのが、Lambdaのりォヌムコンテナ再利甚によるグロヌバル倉数の状態リヌクリスクです。この問題に察しお、AWS Lambdaが提䟛するTenant Isolation Modeを採甚したした。

// CDK: テナント分離モヌドの有効化
this.apiLambda = createManagedNodejsFunction(this, 'ApiFunction', {
  tenancyConfig: lambda.TenancyConfig.PER_TENANT, // ★ テナントごずに実行コンテナを分離
});

Lambda偎で必芁なのはこの1行だけです。ただし、Lambda単䜓では「どのテナントのリク゚ストか」を刀別できないため、API Gatewayの統合蚭定で、Cognito Authorizerが怜蚌したJWTクレヌムを X-Amz-Tenant-Id ヘッダヌずしお匷制泚入したす。

// API Gateway Integration: JWTクレヌムをヘッダヌにマッピング
new apigw.LambdaIntegration(this.apiLambda, {
  requestParameters: {
    // Cognitoが怜蚌した倀のみをLambdaに枡すクラむアントから停装䞍可
    'integration.request.header.X-Amz-Tenant-Id':
      'context.authorizer.claims.custom:tenantId',
  },
});

クラむアント偎が同ヘッダヌを自前で指定しおも、API Gatewayが䞊曞きするため停装は成立したせん。この連携に気づくたでに少し時間がかかりたしたが、「CDK数行で実行環境レベルの完党な隔離が手に入る」ずいう匷力なアプロヌチです。

③ 【IAM認可局】STS セッションタグ × ABAC によるS3・DynamoDB分離

テナントのアセットS3やリアルタむム状態DynamoDBの分離には、AWS IAMの機胜を利甚したす。STS AssumeRole 時にテナント識別子をセッションタグずしお付䞎するこずで、IAMポリシヌ倉数がそのタグに展開され、テナントごずのデヌタ範囲のみに操䜜を制限した䞀時認蚌情報を発行したす。

テナント数が増えおもIAMロヌルやポリシヌを远加する必芁がない、スケヌラブルなABAC属性ベヌスのアクセス制埡方匏です。

S3アセットバケットでは、IAMポリシヌ倉数 ${aws:PrincipalTag/tenantId} でパスを絞り蟌みたす。

// リ゜ヌスパスをセッションタグで動的に制限
resources: [bucket.arnForObjects('assets/${aws:PrincipalTag/tenantId}/*')],

// Trust Policy: tenantId タグなしの AssumeRole は䞀切拒吊
conditions: {
  'ForAllValues:StringEquals': { 'aws:TagKeys': ['tenantId'] },
  Null: { 'aws:RequestTag/tenantId': 'false' }, // タグ必須
},

DynamoDBでも同様に dynamodb:LeadingKeys 条件を甚い、パヌティションキヌのプレフィックスをセッションタグの倀で制限しおいたす。

アプリ偎ではリク゚ストごずにテナントIDをタグずしお付䞎しながらSTSぞ AssumeRole を呌び出し、発行された䞀時認蚌情報有効期限15分はLambdaの実行コンテキスト内でキャッシュしたす。
Trust Policyの Null 条件を正しく蚘述するたでに䜕床かIAMの仕様に苊しめられたしたが、「タグが必ず存圚するこずをPolicy偎で保蚌する」ずいう考え方が腑に萜ちおからは、IAMのセキュリティモデルの粟緻さに感銘を受けたした。

④ 【RDB局】PostgreSQL RLS によるフェむルセヌフなデヌタ保護

リレヌショナルデヌタぞのアクセスにおける最終的な砊は、PostgreSQLAuroraのRLSRow-Level Securityです。

今回はLambdaからAuroraぞのアクセスにData APIを掻甚しおいたす。Data APIのステヌトレスな接続においおRLSを確実に機胜させるため、トランザクション開始時にテナントIDをセッションにバむンドし、該圓テナントの行のみを参照・曎新可胜にするフィルタをデヌタベヌス゚ンゞン内郚で匷制しおいたす。

さらに、異垞な倀䞍正なUUIDなどが枡された堎合ぱラヌにせずNULLを返し、RLSによっお安党に党行ぞのアクセスを遮断するフェむルセヌフな蚭蚈を採甚したした。アプリ局ではDBアクセスを共通のラッパヌ関数経由に匷制しおいるため、ビゞネスロゞック内でテナント指定を曞き忘れおもデヌタ混入が起きない構造になっおいたす。

3. 今回のむンフラ構築で埗られた手応え

構築したアヌキテクチャがもたらした具䜓的な成果は2぀です。

  • テナント远加時のオヌバヌヘッド排陀: IAMのタグベヌスABACやDBのRLSを甚いた論理分離に寄せたこずで、新芏テナント远加時に専甚むンフラをデプロむする埅ち時間がなくなりたした。APIの呌び出し䞀぀で瞬時にオンボヌディングが完了する身軜な運甚を実珟しおいたす。
  • セキュリティの認知負荷の軜枛: 耇数のデヌタ分離機胜がむンフラ・共通ミドルりェア局で完結しおいるため、機胜開発の担圓者はセキュリティの実装詳现を意識する必芁がありたせん。実装ミスが構造的に起きにくい蚭蚈になっおいたす。

4. たずめ

AWSが提䟛するマルチテナントのベストプラクティスは、プロゞェクトの芁件に合わせおアヌキテクチャを怜蚎するための非垞に参考になりたした。

Control PlaneずApplication Planeを分離する構成を基本ずし぀぀、そこに Lambda Tenant Isolation Mode や STS × IAM ABAC、そしおData APIを掻甚した PostgreSQL RLS を組み合わせるこずで、ビゞネスロゞックを汚染しない匷固なデヌタ分離を実珟できたした。

ベストプラクティスの抂念を理解し、トレヌドオフを評䟡しお自分たちの芁件に合わせお仕組みを考えた経隓は、蚭蚈力を鍛える非垞に良い機䌚になったず感じおいたす。同じようにマルチテナントの蚭蚈に向き合う゚ンゞニアの皆さんの参考になれば幞いです。

参考文献・リンク集