注:筆者は韓国在住のため、本文には韓国特有の文脈が含まれることがあります。
MSAサービスを作ると、サーバー間で非常に頻繁な通信が発生します。
例えば、(かなり簡略化した) 送金サービスがあると仮定してみましょう。

ウォレットサーバーとユーザー情報サーバーがあるとすれば、送金APIを呼び出すと上図のようなことが起きるでしょう。
ところで、もし正常ユーザー確認APIが外部からアクセスできるとしたらどうでしょうか?

悪意のあるユーザーが本気でアクセスしてきたら、私たちのサービスの正常ユーザー比率を図表化することすらできてしまうかもしれません(..)
そこで、MSA構造においてサーバー間のリクエストではAPIを使えるが、外部ユーザーがアクセスしてはいけないという要件が出てきました。
どうすればこの要件を達成できるでしょうか?
1. 最もシンプルな方法は、サーバー同士で秘密鍵を共有する方式です。

- 環境変数などを使って、サーバー同士で同じ秘密鍵を共有します。
- これをHTTPカスタムヘッダーなどに載せて、サーバーがリクエストする際に鍵も一緒に送ります。
- サーバーは内部APIリクエストを受け取ると秘密鍵を確認し、もし一致しなければRejectします。
しかし、この方法には問題があります。
- もし誰かが秘密鍵を持ち出したら?あるいは、Gitなどの外部リポジトリに鍵が露出してしまったら?
- 当然、外部からAPIリクエストが可能になります。
2. それなら、Whitelistなどを管理するのはどうでしょう?

- サーバーのIPがすべてわかっていれば、
X-Forwarded-Forヘッダーなどを通じてIPを確認した後、私たちが知っているサーバーのIPでなければブロックできます。
ところが、この方法にも少し問題があります!
- もしリクエストが大量に来てサーバーを増設したらどうでしょう?
- 当然、新しいサーバーのIPはわからないので、ブロックされてしまいます。
3. ネットワーク設定を通じて外部からの侵入を防いでみましょう!
1、2の方法も良い方法ですが、そもそも外部からの侵入の場合、レスポンスがサーバーまで届かないようにすればもっと安全でしょう。
これをPrivate DNS / Public DNSを使って実装してみましょう!
Split Horizon DNS、またはMultiview DNSとも呼ばれる方法です。関連ドキュメント: ( Link )
それでは一つずつ順番に見ていきましょう。
1. まずPublic DNS / Private DNSについて見てみましょう。

- 非常にシンプルに見ると、VPC外部からアクセスする時 / VPC内部からアクセスする時で、異なるレスポンスアドレスを返すことができます。
- VPC内部からアクセスする時は内部DNSサーバーを、外部からアクセスする時は外部DNSサーバーを使うと考えれば良いです!

では、内部DNSにないレコードをリクエストしたらどうなるでしょうか?
上図のようにPrivate DNSに先に問い合わせ、なければPublic DNSに問い合わせます。
(Note:実際のネットワークフローと正確に一致しない場合があります。しかし重要なのは、Private DNSに優先権があるということです。)
2. それでは次にALBについて見てみましょう。

ALB (Application Load Balancer) は入ってくるリクエストを評価して、適切に分配することができます。
- HostNameを基準に適切に応答できます。例えば..
wallet.lemondouble.comに来たらwallet serverに接続できます。user.lemondouble.comに来たらuser serverに接続できます。
- Pathを使っても同じ作業ができます。
api.lemondouble.com/walletに来たらwallet serverに接続できます。api.lemondouble.com/userに来たらuser serverに接続できます。- もちろん、
特定のPathが含まれていればDenyすることも可能です。
- HostNameを基準に適切に応答できます。例えば..
3. では、この2つを組み合わせると?

まず、Public DNSとインターネットに接続されたPublic ALBを作成します。
- この時、Public ALBのルールは次の通りです。
- 優先順位 1.
Hostnameがwallet.lemondouble.comと一致し、Pathが/internal*なら固定の404レスポンス - 優先順位 2.
Hostnameがwallet.lemondouble.comと一致すればWallet Serverの80ポートに接続
この場合、ALBルールは優先順位順に適用されるので
wallet.lemondouble.com/api/helloの場合、優先順位1にはMatchせず、優先順位2にマッチするのでwallet Serverに接続されます。wallet.lemondouble.com/internal/helloの場合、優先順位1にMatchするので、404レスポンスを受け取ります。

次に、Public DNSと同様にPrivate DNSと、インターネットに接続されていないPrivate ALBを作成します。
- この時、Private ALBのルールは次の通りです。
- 優先順位 1.
Hostnameがwallet.lemondouble.comと一致すればWallet Serverの80ポートに接続
この場合、
/internal*に対して404を返すロジックがないので、すべてのリクエストはWallet Serverに転送されます。

- 2つを合わせると、次のような形になります。
wallet.lemondouble.com/internal/helloAPIを呼び出した時、VPC外部では404レスポンスを受け取りますが、VPC内部では正常なレスポンスを受け取ります。
4. それでは、ネットワーク設定だけで十分でしょうか?
- どれくらい情報漏洩に敏感なアプリケーションを作るかによりますが、1と3の方法を合わせるのがより良いでしょう。そうすると..
- もしサーバー同士の秘密鍵が漏洩したとしても、ネットワーク設定が防いでくれるので外部アクセスは遮断されます。
- もしネットワーク設定を間違って触ってしまったとしても、外部からは秘密鍵を知らないので外部アクセスは遮断されます。
- ミスはいつでも起こりうるので、ミスが起きてもシステムで防げる方法のほうがはるかに良いです!!
以上、Private (またはInternal) APIを作る方法について見てきました。
Private APIを作る方法は様々です。これ以外にもAWSのAPI Gatewayを使うことも、(よくは知りませんが) K8SやSpring Cloud Gatewayなどを使うこともできる..?でしょう。
色々な方法の中に、こういう方法もあるんだな〜と見ていただければ幸いです。もし間違っているところがあればコメントを残してください!
ありがとうございました。

Comments