[PR] あなたが Kindle で読みたいその本、Kindle に対応したら Twitter でお知らせします。

SNI 非対応クライアントのために API Gateway をハックする

Posted on

API Gateway + Lambda でサーバレスの API を実装したのですが、特定のクライアントからリクエストが来ていないことに気づきました。よくよく調べてみると、おそらく Java 1.6 で書かれたクライアントで SNI (Server Name Indication) に対応していないことがわかりました。

ドキュメントにも書いてある通り、API Gateway のバックエンドには CloudFront が使われており、提供されるエンドポイントはカスタムドメイン名かどうかに関係なくすべて SNI の SSL/TLS です。

カスタムドメイン名を持つ API リクエストは、カスタムドメイン名の CloudFront ディストリビューションを通過してから、API の CloudFront ディストリビューションに達します。 API Gateway は、CloudFront ディストリビューションで Server Name Indication (SNI) を利用することにより、API でカスタムドメイン名をサポートしています。

リクエストを送ってくるクライアントが Web ブラウザであれば問題ないのですが(最近のブラウザは SNI に対応している)、Web ブラウザ以外だとプログラミング言語やそのバージョン、また依存ライブラリによって SNI に対応していないことがあります。

API Gateway の前に CloudFront を挟むことで専用 IP の SSL/TLS にできますが、毎月 600 ドルのコストが必要になってきます。サーバレスにしてコストを抑えたいのに、600 ドルも払っていたら元も子もありません。

ELB + nginx でリバースプロキシする

AWS のサービスで SNI を使用しない SSL/TLS 処理ができるのは ELB だけです。 API Gateway のメリットを半減させてしまいますが、次のような構成にすれば SNI 非対応のクライアントでも接続できます。

リバースプロキシの構成図

ELB で SSL/TLS を処理し、EC2 上の nginx で API Gateway にリバースプロキシします。

アップストリーム(今回だと API Gateway)が SNI の場合は proxy_ssl_server_name on; を指定します。このオプションは nginx 1.7.0 以降で使えます。

location / {
    proxy_ssl_server_name on;
    proxy_pass https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/;
}

この構成だと nginx がボトルネックになってしまうので、Auto Scaling でスケールアウトできる構成にしておきましょう。

また API Gateway が本来持っている世界中のエッジロケーションが活用されないので、低いレイテンシーやトラフィックの激増に耐えるといったメリットを享受できません。

まとめ

接続してくるクライアントが SNI に対応していないのは盲点でした。古くからあるサービスだと気軽にアップデートできない事情もあると思うので、あらかじめ確認しておいたほうが良いでしょう。