全テナントに触れる管理基盤を、どう多層で守るか — DRESS CODEのControl Plane設計
こんにちは。Dress Code株式会社でプロダクトエンジニアをしているぽこひで(@pokohide )です。試写会が当たったので一足先にプラダを着た悪魔2を観に行ってきました 👠 試写会って当たるんですね。
Dress Code株式会社は、2024年9月に設立された、人・採用・総務・IT などの領域を統合管理するワークフォースマネジメントプラットフォーム「DRESS CODE」を開発・提供する企業です。
マルチテナントSaaSアーキテクチャでは、システムを、テナント向けの機能とビジネスロジックを担う Application Plane と、テナント管理や横断的な運用に使う Control Plane の 2 つに分けて設計することがよく用いられます。Application Plane が SaaS の価値そのものを前に出す層なのに対し、Control Plane は全テナントのライフサイクル管理や運用の土台に向く層です。
本記事では、私たちがDRESS CODEのControl Planeを、インフラ・フロント/BFF・バックエンドでどのようにセキュアに設計・実装したかをご紹介します。
Control PlaneとApplication Planeとは
SaaSアーキテクチャを設計する上で、システムは役割の異なる 2 つの Plane(層)に分けて考えられます。
Application Plane
顧客(テナント)が実際に触れ、SaaS の機能的価値そのものを提供する層です。私たちが提供するDRESS CODEというサービス自身がこれに当たります。
テナントごとのデータパーティショニングや、他テナントのデータにアクセスできないようにするテナント分離などの仕組みはこのPlane内で実現されます。
Control Plane
特定のテナント向けの製品機能というより、全てのテナントを横断的に運用・管理するための共有基盤です。SaaSプロバイダーがシステム全体を把握・操作する窓口です。具体的には、テナントのオンボーディングや認証、テナント管理、課金、メトリクス収集といったSaaSを運用するためのコアサービスがここに集約されます。簡単に言えば、社内運用者向けの管理基盤です。
テナント数が増えた際に運用を破綻させずにスケールさせるためには、テナントのライフサイクルを一元管理するControl Planeの存在が不可欠です。
DRESS CODEのControl Planeの全体アーキテクチャ
DRESS CODEでは、この社内運用者向けの Web UI / BFF である「DRESS CODE Admin」とそれを支えるインフラ・ネットワーク全体を「Control Plane」と定義しています。BFF をハブとして、認証基盤(Ory Kratos)やバックエンド(Backend API)を安全な内部ネットワーク経由でプロキシする構成を取っています。
各環境ごとに1系統のみデプロイし、インフラ・ネットワーク・アプリケーションの各層で多層防御を意識して設計しています。
1. インフラ設計: 到達経路を物理的に縛る
Control Planeの入口となるインターネット境界では WAF v2 を用いた典型的なウェブ防御を行いつつ、主要なALBにもリージョナルなWeb ACLを関連付け、ホストヘッダや送信元制御などによる防御と、ブロックログやALBアクセスログの保管といった運用を組み合わせています。
Admin ALBにはGoogle OIDC認証[1]を必須とし、社内アカウントからのアクセスに制限しています。その先の認証基盤(Ory Kratos)[2]やBackend APIの管理経路はInternal ALB + Private Hosted Zone + セキュリティグループ間許可で閉域化し、VPC外からは名前解決すらできない状態にしています。さらにアプリケーション層では共有シークレットによる M2M 認証を要求することで多層化しています。
余談(最初はNATヘアピン構成でした)
この記事に着手する前は、インフラの二重管理を避ける目的で、DRESS CODE Admin ECS から Public ALB に向け、NAT Gateway の固定 EIP 経由で届ける構成でした。ALB 側のリスナーでは固定 EIP からの通信にだけ通す、という合わせ方です。
その後、運用コストとセキュリティの両面を見直し、同じ VPC 内なのにインターネットへ出戻るヘアピンをやめ、内部 ALB 直結に移行しました。NAT の従量と往復遅延を削れることに加え、承認先を IP リスト管理から「Admin ECS のセキュリティグループに限定する」といった SG 間制御へ寄せられます。顧客向け Public ALB と管理専用 Internal ALB も分け、Kratos やバックエンド管理系の経路を Private Hosted Zone 前提の閉域にまとめ直しました。記事にしようと思ったことが、あらためて構成を点検するきっかけになったのはよかったです笑
2. BFF設計: 薄く作り、多層で守る
Control Planeは全テナントに触れうる強い権限を扱うため、クライアントに認可の判断基準や秘匿情報を出さないところから設計を組み立てています。DRESS CODE AdminはNext.jsを用いてSSR / BFFで実装し、独自DBを持たずBackend APIと認証基盤(Ory Kratos)への薄いプロキシに徹しています。状態を持たせると、Backend API や Ory Kratos の Source of Truth(信頼できる正のデータ源)が分かれ、M2M トークンをクライアントへ渡す隙もできやすくなるためです。
1 つのリクエストを、ALB・ミドルウェア・ページ・APIという責務の異なる層で独立に検証します。ALBの認証を単一障害点にしないよう、ミドルウェアでは渡ってきたJWTを必ず再検証しています。UIのボタン制御等はクライアント改竄で突破されうるので信頼境界はサーバー側に置きつつ、「何が許可されるか」のポリシー定義は単一モジュールに集約しながら、その評価は UI・ページ・APIの各層で独立に呼び出す構成としています。
「誰がどの操作を行えるか」は、既に組織情報が揃うGoogle WorkspaceをSource of Truthとしています。AWSのタスクロールを外部IdPとして登録したWIF経由でAdmin SDKをリクエストする構成で静的クレデンシャルを持たないことと、組織の二重管理を避けて権限を自動追従させることが狙いです。加えて、Control Planeは全テナントを横断できる強い権限を持つ以上、「誰が・いつ・どのテナントの何を操作したか」はBFFとBackend APIの双方でリクエストIDを共通キーとした監査ログとして残し、後から突き合わせて追跡できるようにしています。
ローカルの開発環境ではALBでのOIDC終端が効かないため、ローカル専用のモックUser / Claimを注入する仕組みをBFFに用意し、本番と同じ型のコンテキストが届いている状態を作ることでDXとのトレードオフを解消しています。
一方で、Google Workspaceに寄せた設計のトレードオフとして、「所属部門」ではControl Planeとして切り出したいリソース境界を表現しきれない場面も出てきています。機能拡充に伴い、この整合性をどう取るかは今後の課題です。
3. バックエンド設計: 特別扱いを作らない
バックエンドはNestJS + Prismaで実装したモジュラモノリスで、機能領域ごとに「Force」と呼ぶモジュール(IT管理のIT Force、人事・労務のHR Force、総務のGA Force等)に分割しています。各Forceは自ドメインのEntityやユースケース、QueryServiceを抱え、共通基盤であるshared-kernelが後述するContextやページング等のユーティリティを提供します。Control Plane向けの管理機能をまとめたadminモジュールが本章の主役で、依存方向は Admin → *-Force → shared-kernel の一方向に揃えています(Force間連携が必要な場合はAdapter I/Fを介します)。
重視しているのは、Admin経路もエンドユーザー向けの通常のAPIも、最後に到達するのは同じ Force のドメイン(QueryService / CommandHandler)だという点です。Admin 専用の「広いスコープ専用ルート」は生やさず、shared-kernel の Context にスコープ情報を注入し、同じ手順で呼び出すだけ、という形にしています。要するに Admin API も、認証と入口の違いの向こうに置いた「通常の API」に寄せた、という言い方が近いです。
ただし、テナント自体やユーザーのように、そもそもテナントを横断する性質を持つリソースは例外です。これらは特定のテナントにスコープする対象ではないため、テナント横断で動く専用のQueryServiceを用意し、Adminからのみ参照する構成にしています。
ネットワーク防御を突破された万一の事態に備え、アプリケーション層でも防御を重ねています。Backend APIの管理用エンドポイントは、インフラ設計でも述べたように内部ALBとセキュリティグループによってDRESS CODE Adminからの通信のみを許可するようにネットワークレベルで閉域化しています。その上で、アプリケーション層で共有シークレットによる M2M 認証を要求することで正当な呼び出しであることを保証しています。シークレットはSecrets Managerで管理しており、ローテーションは再デプロイで安全に入れ替え可能です。
加えて、Admin ControllerはForce内部(Prismaや具象QueryService)に直接触れず、Force側で定義された抽象インターフェース(*-Adapter)にのみ依存します。Controllerは受け取ったDTOをAdapterに渡し、結果をレスポンスDTOに詰め替えるだけに留まるため、管理機能が青天井に増えても、Admin側のコード量がドメインの複雑さに引きずられません。
まとめ
DRESS CODE の Control Plane では、ネットワーク上は OIDC 終端に加え、Internal ALB・セキュリティグループの組み合わせで到達先を制限し、BFF では薄いプロキシにしながら ALB から API まで段階的に認可を確認し、バックエンドでは Context でスコープを強制する、という層の積み重ねにしています。管理用 API も、入口と認証方式が違うだけで、同じドメイン実装(QueryService / CommandHandler 等)に届く、という一貫性を意識した構成です。
各層が同じ仮定だけに依存しすぎないよう分けたほうが、後から挙動を追いやすい、という感覚に近いです。
おわりに
SaaSにおけるControl Planeの構築は、マルチテナントの境界をいかに守るかという地道で複雑な設計の連続です。しかし、この「人間向けのオペレーションを徹底的にセキュアにする」という泥臭いアプローチは、これからのシステムアーキテクチャの強力な基盤になり得ると考えています。
エンタープライズシステムの世界的な潮流は「人間がUIを操作するアプリケーション」から「AIがAPIを叩いて自律的に動くエージェント」へとパラダイムシフトが起きています。AIエージェントがシステムを横断して自律的に動く時代において、Control Planeは単なる管理画面からAIエージェントの行動を安全に統治するレイヤーへ昇華していく必要があります。
安全な API だけを公開するTool Governanceの境界線、エージェント自身を識別するM2M トークン、誰の代理で動いているかという実行時コンテキストによる認可制御。これらはまさにAIエージェントを安全に迎え入れるための土台そのものだと考えています。
一方で、今回採用した共有シークレット型のM2M認証は、呼び出し元がDRESS CODE Adminひとつである現状では十分ですが、Control Planeを複数のエージェントが叩く時代には合わなくなります。呼び出し元を「アプリケーション単位」ではなく「エージェント個体単位」で識別し、短命トークンで動かす方向への進化や、Contextを「誰の代理で動いているか」まで持つon-behalf-of型[3]に広げていくこと、そのために現状の認証基盤(Ory Kratos)と組み合わせてOry HydraのようなOAuth 2.0 Authorization Serverを足していく構成など、次の打ち手はいくつか見え始めています。
今日"人間向け"に敷いた多層防御は、そのままの哲学で"エージェント向け"に拡張できる前提で構築されています。今後もこの強固な基盤の上で、人間とAIが共に顧客の課題を解決していけるような次世代のプラットフォーム開発を進めていきます。
Dress Code のこれからに興味がある方はぜひ一度、お話ししましょう!
-
OAuth 2.0 Token Exchangeで標準化されている特定ユーザーの代理として動いていることをトークンに明示的に埋め込む考え方です ↩︎

Discussion