2021/04/07 追記
ALB - EC2 間を HTTP/2 で接続する方法について、ALBに追加された機能を使って解決する方法書きました。
はじめに
https 通信で CloudFront を経由し、ALB -> EC2 という非常に標準的な構成を採っていたのだが、一部のツール・ブラウザで表示が行われなかったため調査を実施した。
概要は以下の通り。
- iPhone の Safari ではページが全く表示されない
- curl を打つと
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
- Windows の Safari (古いけど...) では
キュリティ保護された接続を確立できません
という表示
結果として、2つの問題があった。
問題1. Upgrade: h2,h2c
これは全く同じ問題が公開されていたので引用。
ALB は HTTP2 を受け付けるが、ALB-EC2間は HTTP 1.1 で通信される。
Apache の mod_http2 を有効にしていると、HTTP 2 通信が可能なので Upgrade をレスポンスヘッダに含めてしまう。
すると、ユーザーとALBの間は HTTP2 でやり取りされているので、HTTP2 通信にも関わらず、レスポンスヘッダにUpgradeタグが注入されてしまう。
しかし、HTTP 2のRFCでは
8.1.2.2. Connection-Specific Header Fields
HTTP/2 does not use the Connection header field to indicate
connection-specific header fields; in this protocol, connection-
specific metadata is conveyed by other means. An endpoint MUST NOT
generate an HTTP/2 message containing connection-specific header
fields; any message containing connection-specific header fields MUST
be treated as malformed (Section 8.1.2.6).
引用元: https://tools.ietf.org/html/rfc7540#section-8.1.2.2
となっており、HTTP2 通信の中に HTTP2 をupgradeさせる情報を含めてはいけない(MUST NOT)。
この仕様を厳格に守った場合はそのような通信を受け入れてはならないので、Safari や curl では通信を行うことができない。
対処方法は EC2 側の httpd で HTTP2 を無効化して HTTP 1.1 のみを有効とすればよい。 一般的には mod_http2 を Load しなければよい。 やり方は上記URL参照。
問題2. TLS のデフォルトバージョン
問題1. を対処した結果、iPhone と curl は正常に通信できるようになったが、Windows の Safari のみ通信ができない状態だった。また、CloudFrontへ向けた場合は失敗、ALBへ向けた場合は成功、と地味にふるまいが一貫していない。
あれやこれやと試してみた結果、WindowsのSafariリリースが2012年で止まっているということで思い当たったのがサーバーサイドで受け入れ可能なTLSバージョンだったので確認。
リソースの生成時には CloudFront も ALB もセキュリティポリシーには、Management Console から選択可能なデフォルト/recommended を選択した。
しかし、実はこの2つで受け入れ可能なTLSのバージョンが異なっている(※記事執筆の19/12/02時点)
コンテンツ | デフォルト(recommend)ポリシー | サポートするTLSの最低バージョン |
---|---|---|
ALB | ELBSecurityPolicy-2016-08 | TLSv1.0 |
CloudFront | TLSv1.1_2016 (recommended) | TLSv1.1 |
今回は、下位側に合わせる意味で CloudFront 側のセキュリティポリシーを TLSv1_2016
に修正してDeployを実施。 ステータスが Deployed になった後に再度 Windows Safari で接続すると接続できた。