Skip to content

ADR: IP アドレス制限

特定のIPアドレスからのアクセスだけを許可するためのオプション。特定のワークスペース(以下WS)に対して特定のIPアドレスからしかアクセスできないようにするもの

某大手企業からQudenを導入するにあたり、IPアドレス制限かSAML(SSO)のどちらかが必須要件と言われて実装することに
(汎用性で言えばSAMLの方が良かったが、紆余曲折ありIPアドレス制限に。ここで書くと長くなるので省略)

意思決定者: Azumi, Ota

背景

大きく3つのポイントにIPアドレスをチェックする機構を入れる必要がある

  • QudenのFrontendへのアクセス
  • QudenのBackendへのアクセス (APIに直接アクセスすることを想定)
  • 動画・サムネなどが保存されているS3へのアクセス

Qudenのアーキテクチャ概要

要件的には、特定のWSに関連するリソースへアクセスする際に、IPアドレスをチェックしてアクセス可否を判断できれば良い

また、制限を入れるworkspaceIdとIPアドレスについては、変更が容易な形かつ一元的に管理したい。
FeatureFlagの管理はApp Configで行っているので、そこでできると新たにツールを増やさなくて良さそう(もちろん他に適切なツールがあればそちらを利用する)

決定事項

  • workspaceIdIPアドレスはApp Configで管理
  • Webアプリケーション側へのアクセス
    • Nextでアクセス可否をチェックするLayerを追加
    • NestJS(各エンドポイント)へのアクセスは、IPアドレスをチェックするGuardをかける
  • S3へのアクセスはLambda@Edgeでチェック
    • itemIdとIPアドレスをNestJS投げてアクセス可否をチェック

意思決定の詳細

Frontendへのアクセス

要件

特定のWSに関連するリソースへのアクセスについて、IPアドレスをチェックできるようにする

検討した方法

  • 別ドメインを作成し、IP制限のWSはそこにアクセスしてもらう (ex. companyA.quden.io)
    • 別ドメインへのアクセスはVercel Edge Middlewareなどでルーティング
    • ドメインを分けても、app.quden.io側でIPアドレス制限がかかっているWSへのアクセスを遮断しないといけないので二度手間
  • NextのコンポーネントでLayerを挟み、アクセス可否をチェック
    • 自前で実装しないといけないが、要件は達成できる

以上の選択肢から、Nextのコンポーネント側でLayerを挟むことに
Authenticated.tsxUnauthenticated.tsxなどのほぼ全てのコンポーネントで使用されているLayerに挟みたかったが、WSに関係のないコンポーネント(ex.ユーザー情報)は制限する必要がないため、src/pages配下の該当コンポーネントに対してLayerを挟むように

決定にあたり受け入れたデメリット/やりきれていないこと

  • src/pagesに新たにコンポーネントを追加する場合、WSチェックのLayerを都度挟む必要がある
    • 2023/5時点では意思決定者のどちらかが実装/レビューを行うようになっているため、そこで対応忘れを防止していく
    • ゆくゆくは自動的に検知できる仕組みを入れていきたい

Backendへのアクセス

要件

特定のWSに関連するリソースへのアクセスについて、IPアドレスをチェックできるようにする

検討した方法

  • ALB
    • 特定のパスのみ制限するとかはできそう
    • 今回やりたいのはパスベースでの制限というより、アクセスしたいリソースベースなので微妙
  • NestJS
    • Guardなどを作成し、各エンドポイントに被せることで割と簡単に実装できそう

以上の選択肢から、NestJSにIpAddressFilterGuardを作成し、該当のエンドポイントに被せることに
NestJSのcacheを活用することで、AppConfigやDBへのアクセスが増えすぎないようにもしている

決定にあたり受け入れたデメリット/やりきれていないこと

  • 該当のエンドポイントに都度IpAddressFilterGuardを適用する必要がある
    • 理由
      • itemGuard/workspaceGuardに依存した構造になっており、デフォルトのガードとして設定できないため
      • デフォルトのガードとして適用できるようになっても、IpAddressFilterGuardが必要のないエンドポイントへの対応も必要
    • PR作成時などにIpAddressFilterGuardが適用されていないエンドポイントをチェックする機構を入れたい

S3へのアクセス

背景

S3へのアクセスはCloudfrontを経由して行われている

要件

特定のWSに関連するリソースへのアクセスについて、IPアドレスをチェックできるようにする

検討した方法

  • Cloudfront Functions
    • 渡されたリクエストの情報を少し書き換える処理に向いてる
    • 実行時間が1msかつネットワークアクセス不可なので、複雑な処理はできない
  • Lambda@Edge
  • Cloudfrontのビヘイビアまわりでなんとかする
    • IPアドレス制限をかけたいリソースへのパスが動的に変化するため厳しい。即候補から外した
      • S3の構成は、/thumbnail/<userId>/<itemId>/... みたいな形になっており、workspaceIdでは管理していない

workspaceIdとIPアドレス情報を一元的に管理することを考えると、ネットワークアクセスができるLambda@Edgeを採用することに
(IPアドレス情報などをコードベタ書きにすれば、Cloudfront Functionsでもできるが、Webアプリケーション側と別途管理することになり二重管理になってしまう)

Lambda@Edgeでどう実現するか

またトリガーについては、CloudfrontのCacheの手前でチェックを行いたいので、Viewer Request を採用
Lambda@Edge アーキテクチャ図

S3の構成がWS情報を含まないため、AppConfig内でworkspaceIdとIPアドレスの他にuserIdを管理することも考えたが、WS内の人の入れ替えがあるたびに更新するのが面倒なので却下

Lambda@Edgeで取得できる主な情報は、「どのリソースにアクセスしようとしているか(uri)」と「IPアドレス」なので、WSの情報は持っていない。そのため、「uriから抜き出したitemId」と「IPアドレス」を NestJS に投げてDBとAppConfigの情報からアクセス可否を返してもらうことで、チェックするように

決定にあたり受け入れたデメリット/やりきれていないこと

  • Lambda@Edgeのリリースフロー整備
    • 現状は、Typescriptのコードをトランスパイル&zip化してS3に保存->AWS コンソールからLambdaを更新するようになっている
    • 開発者はgithub上の操作だけにして、リリースは自動化できるのが理想だが、変更頻度が高くないため一旦見送り

採用した結果どうだったか

pros

  • workspaceIdとIPアドレスの管理をAppConfigで行っているため、更新作業が楽

cons

Reference