Network Load BalancerがTLS ALPNに対応しました

記事タイトルとURLをコピーする

2020年に5月27日のアップデートでNetwork Load Balancer(以下、NLB)でTLS ALPNが利用できるようになりました。
Network Load Balancer が TLS ALPN ポリシーのサポートを開始

TLS ALPN についての説明は後述しますが、要するにNLBがHTTP/2に対応したということです。 本記事では、「HTTP/2ってなんだっけ?」 というあたりから軽く学びつつ、今回のアップデートの内容を理解していきたいと思います。

HTTP/2とは

HTTPのバージョン2であり、2015年にRFC7540と策定されています。 HTTP/2は前バージョンであるHTTP1.1よりも、いくつかの点で通信効率が上がっています。 例えば、HTTP1.1ではTCPセッション1つあたりの同時リクエスト数は1だったので、Webページにアクセスするたびに複数のTCPセッションを接続する必要がありました。 HTTP/2では1つのTCPセッションの中で並列にリクエストが可能です。

いかにいい仕様であってもソフトウェアが対応していないと使えないわけですが、2015年末の時点で既に主要なブラウザはHTTP/2に対応していたようです。

The standardization effort was supported by Chrome, Opera, Firefox,[11] Internet Explorer 11, Safari, Amazon Silk, and Edge browsers.[12] Most major browsers had added HTTP/2 support by the end of 2015.[13] According to W3Techs, as of May 2020, 46.0% of the top 10 million websites supported HTTP/2. Wikipedia

したがって、HTTP/2は積極的に使っていくのがいいと思います。 あまり意識している方は少ないと思いますが、気がついたらHTTP/2を使っていたという方が多いと思います。

なお、HTTP/3というのもあるのですが、こちらはまだChromeブラウザ+Googleサイトの組み合わせでしか使われていないようです。

実際に自分でアクセスする時にどのプロトコルが使われているかの確認は各ブラウザの開発ツールで確認できます。 下記はChromeで弊社サイトにアクセスした時のものですが、現実はやや複雑ですね。 多くの外部リンクを含んでいるため、プロトコルが入り混じっていますが、h2がHTTP/2の通信となります。

ALBのよくある構成

AWSでHTTPS通信を使う場合、一般的にはNLBよりもALBがよく使われると思います。 NLBとの比較のためにALBを復習してみます。

ALBの構成図

ALBはHTTP/2に対応しているため、ブラウザも対応していれば自動的にHTTP/2で通信します。 ALBとEC2インスタンスの間の通信はHTTP1.1です。

Application Load Balancer は、HTTPS リスナーに HTTP/2 のネイティブサポートを提供します。1 つの HTTP/2 コネクションで最大 128 のリクエストを並行して送信できます。ロードバランサーは、これらのリクエストを個々の HTTP/1.1 のリクエストに変換し、ターゲットグループの正常なターゲットにこれを分配します。HTTP/2 ではフロントエンド接続を効率的に使用するため、クライアントとロードバランサー間の接続数が減少します。HTTP/2 のサーバープッシュ機能は使用できません。 Application Load Balancer のリスナー

ALBへの通信ログ

クライアントとALBの間でどんなやりとりがされているでしょうか? curlでALBにアクセスした時のログをみてみると、「GET / HTTP/2」と最終的にHTTP/2でリクエストを出しているのがわかります。 また、今回のお題であるALPNという文字も見えます。

~$ curl -v https://alb.supergood.site
*   Trying 18.180.196.49...
* TCP_NODELAY set
* Connected to alb.supergood.site (18.180.196.49) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=*.supergood.site
*  start date: May 31 00:00:00 2020 GMT
*  expire date: Jun 30 12:00:00 2021 GMT
*  subjectAltName: host "alb.supergood.site" matched cert's "*.supergood.site"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fbc6f808200)
> GET / HTTP/2
> Host: alb.supergood.site
> User-Agent: curl/7.64.1
> Accept: */*

ターゲットサーバのアクセスログ

今回の検証ではEC2インスタンスでNGINXを動かしていますが、そちらでは「GET / HTTP/1.1」となっているのが確認できます。 確かにALBとターゲットの間はHTTP1.1となっていますね。

10.200.10.52 - - [01/Jun/2020:16:33:48 +0900] "GET / HTTP/1.1" 200 3520 "-" "curl/7.64.1" "106.171.71.150"

TLS ALPNとは

ALPNとは、Application-Layer Protocol Negotiationの略でありTLSの拡張機能です。 アプリケーションレイヤーのプロトコルでHTTPの1.1を使うのか、それともHTTP/2を使うのか、それともさらに別の何かを使うのかをネゴシエーションします。

先ほどのALBの例ではブラウザとALBの接続は結果的にHTTP/2が使われましたが、実はそれはTLSでALPNが使われたからです。 ALBへの通信ログをみると、先頭部分にALPNのネゴシエーション要求が確認できます。

* ALPN, offering h2
* ALPN, offering http/1.1

また、中盤部分にもALPNのネゴシエーション応答が確認できます。

* ALPN, server accepted to use h2

HTTPSで通信をする場合は、TCP接続し、TLS接続し、それでやっとHTTP通信ができるようになります。 このプロトコルスタックの全体的な流れは以下のようになります。

HTTP/2には、ALPNは、ほぼ必須

HTTP/2にALPNは必須かというと、そうではないようです。 ただし、HTTP/2 over TLSの場合ALPNはMUSTであるとRFC7540に記載があります。

implementations that support HTTP/2 over TLS MUST use protocol negotiation in TLS [TLS-ALPN].

では、TLSを使わないHTTP/2なら不要かと思うのですが、各ブラウザは、HTTP/2単独ではサポートせずに、HTTP/2 over TLSのみサポートしているようです。

Although the standard itself does not require usage of encryption,[36] all major client implementations (Firefox,[37] Chrome, Safari, Opera, IE, Edge) have stated that they will only support HTTP/2 over TLS, which makes encryption de facto mandatory.

したがって、一般的にはHTTP/2はにTLSが必須、そうするとALPNも必須と考えていいかと思います。

NLBでHTTP/2を使う

長々と書いてきましたが、これでようやく 「Network Load Balancer が TLS ALPN ポリシーのサポートを開始」 というアップデートが、 「Network Load Balancer が HTTP/2のサポートを開始」 という意味と理解できました。

NLBの構成図

NLBでALPNを使うためには、リスナーをTLS、ターゲットグループもTLSにする必要があります。

Requirements ・ TLS listener ・ TLS target group TLS Listeners for Your Network Load Balancer

したがって、以下のような構成になります。

ターゲット側にもサーバ証明書を入れたりする必要があります。 なお、ここでは詳細の解説はしませんが、今回の検証で使用したサーバ証明書は、NLB用はACM、EC2インスタンス用はLet's Encryptで発行しました。

NLBでALPNの設定をする

NLBの設定としては、チェックボックスでALPNポリシーを選択するだけです。 curlの応答でみた 「ALPN, server accepted to use xx」の部分でNLBに何を応答させたいかを考えて選択しましょう。

下記表の日本語はマネコンの説明で、英語はTLS Listeners for Your Network Load Balancerの説明です。

ALPNポリシー 説明
None ALPN を受け入れません。
Do not negotiate ALPN. This is the default.
HTTP1Only HTTP/1 接続だけを許可します。
Negotiate only HTTP/1.*. The ALPN preference list is http/1.1, http/1.0.
HTTP2Only HTTP/2 接続だけを許可します。
Negotiate only HTTP/2. The ALPN preference list is h2.
HTTP2Optional HTTP/1 接続を優先します。HTTP/2 接続を受け入れます。
Prefer HTTP/1.* over HTTP/2 (which can be useful for HTTP/2 testing). The ALPN preference list is http/1.1, http/1.0, h2.
HTTP2Preferred HTTP/2 接続を優先します。HTTP/1 へのフェイルバックを許可します。
Prefer HTTP/2 over HTTP/1.*. The ALPN preference list is h2, http/1.1, http/1.0.

NLBでHTTPS通信をしてみる

各ALPNポリシーでどのような違いがでるか試してみました。 なお、ポリシー変更後、即座には変更が反映されず、数秒〜10数秒程度かかりました。 下記にログを記載しますが、結果としてはドキュメントに記載されている通りの期待通りの動作と確認できました。

None

~$ curl -v https://nlb.supergood.site
*   Trying 18.181.66.175...
* TCP_NODELAY set
* Connected to nlb.supergood.site (18.181.66.175) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=*.supergood.site
*  start date: May 31 00:00:00 2020 GMT
*  expire date: Jun 30 12:00:00 2021 GMT
*  subjectAltName: host "nlb.supergood.site" matched cert's "*.supergood.site"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: nlb.supergood.site
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.12.2
< Date: Mon, 01 Jun 2020 08:46:17 GMT
< Content-Type: text/html
< Content-Length: 3520
< Last-Modified: Wed, 28 Aug 2019 19:52:13 GMT
< Connection: keep-alive
< ETag: "5d66db6d-dc0"
< Accept-Ranges: bytes

クライアントは、「ALPN, offering」していますが、NLBは、「ALPN, server accepted to use h2」といった応答がありません。 その結果、HTTP/1.1が使われています。 NLBとターゲットの間もHTTP/1.1でした。

HTTP1Only

~$ curl -v https://nlb.supergood.site
*   Trying 18.181.66.175...
* TCP_NODELAY set
* Connected to nlb.supergood.site (18.181.66.175) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=*.supergood.site
*  start date: May 31 00:00:00 2020 GMT
*  expire date: Jun 30 12:00:00 2021 GMT
*  subjectAltName: host "nlb.supergood.site" matched cert's "*.supergood.site"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: nlb.supergood.site
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.12.2
< Date: Mon, 01 Jun 2020 08:53:52 GMT
< Content-Type: text/html
< Content-Length: 3520
< Last-Modified: Wed, 28 Aug 2019 19:52:13 GMT
< Connection: keep-alive
< ETag: "5d66db6d-dc0"
< Accept-Ranges: bytes
<

クライアントは、「ALPN, offering h2」していますが、NLBは、「ALPN, server accepted to use http/1.1」と応答しています。 その結果、HTTP/1.1が使われています。 NLBとターゲットの間もHTTP/1.1でした。

HTTP2Only

~$ curl -v https://nlb.supergood.site
*   Trying 18.181.66.175...
* TCP_NODELAY set
* Connected to nlb.supergood.site (18.181.66.175) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=*.supergood.site
*  start date: May 31 00:00:00 2020 GMT
*  expire date: Jun 30 12:00:00 2021 GMT
*  subjectAltName: host "nlb.supergood.site" matched cert's "*.supergood.site"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f8484009c00)
> GET / HTTP/2
> Host: nlb.supergood.site
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< server: nginx/1.12.2
< date: Mon, 01 Jun 2020 08:58:18 GMT
< content-type: text/html
< content-length: 3520
< last-modified: Wed, 28 Aug 2019 19:52:13 GMT
< etag: "5d66db6d-dc0"
< accept-ranges: bytes

クライアントは、「ALPN, offering h2」し、NLBも、「ALPN, server accepted to use h2」と応答しています。 その結果、HTTP/2が使われています。 NLBとターゲットの間もHTTP/2でした。

HTTP2Optional

~$ curl -v https://nlb.supergood.site
*   Trying 18.181.66.175...
* TCP_NODELAY set
* Connected to nlb.supergood.site (18.181.66.175) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=*.supergood.site
*  start date: May 31 00:00:00 2020 GMT
*  expire date: Jun 30 12:00:00 2021 GMT
*  subjectAltName: host "nlb.supergood.site" matched cert's "*.supergood.site"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: nlb.supergood.site
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.12.2
< Date: Mon, 01 Jun 2020 09:03:51 GMT
< Content-Type: text/html
< Content-Length: 3520
< Last-Modified: Wed, 28 Aug 2019 19:52:13 GMT
< Connection: keep-alive
< ETag: "5d66db6d-dc0"
< Accept-Ranges: bytes
<

クライアントは、「ALPN, offering h2」していますが、NLBはHTTP/1接続を優先ということで、「ALPN, server accepted to use http/1.1」と応答しています。 その結果、HTTP1.1が使われています。 NLBとターゲットの間もHTTP1.1でした。

HTTP2Preferred

~$ curl -v https://nlb.supergood.site
*   Trying 18.181.66.175...
* TCP_NODELAY set
* Connected to nlb.supergood.site (18.181.66.175) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=*.supergood.site
*  start date: May 31 00:00:00 2020 GMT
*  expire date: Jun 30 12:00:00 2021 GMT
*  subjectAltName: host "nlb.supergood.site" matched cert's "*.supergood.site"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7ff69500dc00)
> GET / HTTP/2
> Host: nlb.supergood.site
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< server: nginx/1.12.2
< date: Mon, 01 Jun 2020 09:10:26 GMT
< content-type: text/html
< content-length: 3520
< last-modified: Wed, 28 Aug 2019 19:52:13 GMT
< etag: "5d66db6d-dc0"
< accept-ranges: bytes
<

クライアントは、「ALPN, offering h2」し、NLBも、「ALPN, server accepted to use h2」と応答しています。 その結果、HTTP/2が使われています。 NLBとターゲットの間もHTTP/2でした。

まとめ

今回のアップデートでNLBでHTTP/2を使えるようになりました。 ただし、「クライアントーNLBーターゲット」でエンドツーエンドでTLSであるという条件があります。 おそらく、ALBと異なり、HTTPの処理をNLB側で行わないので、このような仕様になっているのだと思います。

エンドツーエンドで暗号化ができていいと思う反面、ターゲット側での証明書管理の負担がでてきます。 また、NLBでもTLS処理しますが、ターゲット側でもTLS処理するため、TLS負荷をNLB側に完全にオフロードできなくなっています。 ただし、HTTP/2は1.1よりも通信効率が改善されるため、全体としてはターゲットの負荷が増えるとは言えないと思います。

AWSのロードバランサーには、CLB、ALB、NLBとありますが、当初は選択基準が簡単でした。 HTTPやHTTPSはALB、その他のプロトコルはNLBを選べば良かったと思います。 しかし、今回のアップデートのようにNLBでできることも増えてくると、だんだんわからなくなってきました。 確かにALBはWAFが使えたり、リダイレクトしたり、パスベースのルーティングをしたり、選択するメリットがあります。 ただ、単純に高速なHTTPSサーバが欲しい場合、NLBを検討するのもいいのかもしれません。

YouTubeやってます

また、本ブログの内容は2020/6/3(水) 12:00よりYouTube Liveで配信される「30分でわかる AWS UPDATE!」でも取り上げる予定ですので、ぜひご覧ください!
もしリアルタイムで見逃しても、アーカイブ動画から内容を確認できます。

渡辺 信秀(記事一覧)

2017年入社 / 地味な内容を丁寧に書きたい