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で行っているので、そこでできると新たにツールを増やさなくて良さそう(もちろん他に適切なツールがあればそちらを利用する)
決定事項
workspaceIdとIPアドレスは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.tsxやUnauthenticated.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 Functionsに比べ実行時間やメモリに余裕があり、ネットワークアクセスも可
- Cloudfront Functionsとの比較が参考になる
- 参考: ClassMethodの記事
- Cloudfrontのビヘイビアまわりでなんとかする
- IPアドレス制限をかけたいリソースへのパスが動的に変化するため厳しい。即候補から外した
- S3の構成は、
/thumbnail/<userId>/<itemId>/...みたいな形になっており、workspaceIdでは管理していない
- S3の構成は、
- IPアドレス制限をかけたいリソースへのパスが動的に変化するため厳しい。即候補から外した
workspaceIdとIPアドレス情報を一元的に管理することを考えると、ネットワークアクセスができるLambda@Edgeを採用することに
(IPアドレス情報などをコードベタ書きにすれば、Cloudfront Functionsでもできるが、Webアプリケーション側と別途管理することになり二重管理になってしまう)
Lambda@Edgeでどう実現するか
またトリガーについては、CloudfrontのCacheの手前でチェックを行いたいので、Viewer Request を採用
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で行っているため、更新作業が楽