注:筆者は韓国在住のため、本文には韓国特有の文脈が含まれることがあります。

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することも可能です。
  • 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/hello APIを呼び出した時、VPC外部では404レスポンスを受け取りますが、VPC内部では正常なレスポンスを受け取ります。

4. それでは、ネットワーク設定だけで十分でしょうか?

  • どれくらい情報漏洩に敏感なアプリケーションを作るかによりますが、1と3の方法を合わせるのがより良いでしょう。そうすると..
    • もしサーバー同士の秘密鍵が漏洩したとしても、ネットワーク設定が防いでくれるので外部アクセスは遮断されます。
    • もしネットワーク設定を間違って触ってしまったとしても、外部からは秘密鍵を知らないので外部アクセスは遮断されます。
    • ミスはいつでも起こりうるので、ミスが起きてもシステムで防げる方法のほうがはるかに良いです!!

以上、Private (またはInternal) APIを作る方法について見てきました。

Private APIを作る方法は様々です。これ以外にもAWSのAPI Gatewayを使うことも、(よくは知りませんが) K8SやSpring Cloud Gatewayなどを使うこともできる..?でしょう。

色々な方法の中に、こういう方法もあるんだな〜と見ていただければ幸いです。もし間違っているところがあればコメントを残してください!

ありがとうございました。