この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
AWSチームのすずきです。
Webサーバとして、Amazon Linux2 の httpd (Apache/2.4.37)、 ELB に HTTP/2 をサポートする Application Load Balancer (ALB) を利用する環境で、 iOS と macOS の Safari からの HTTPS 接続がエラーとなる現象を、 httpdの設定で回避する機会がありました。
その内容について紹介させていただきます。
構成
事象
Safariから HTTPS (HTTP/2) 接続時に、プロトコルエラーが発生する事がありました。
以下のサイトで紹介されていた、 iOS と、High Sierra の macOS 環境の Safari で、CentOS7 の httpd24 (Apache) を Nginx をリバースプロキシとする環境で発生する報告を参考に、 調査と対応を実施しました。
参考サイト
iPhone does not open HTTPS site in Safari with error NSPOSIXErrorDomain:100
Safari can't open the page. The error is: "The operation couldn't be completed. Protocol error" (NSPOSIXErrorDomain:100)
調査結果
Amazon Linux2 の 標準リポジトリから httpd のインストール時、 依存関係で同時にインストールされる「http2_module」により、HTTP/2 切替を促すレスポンスヘッダが、ブラウザ(Safari)の誤動作を引き起こしていました。
Installing:
httpd x86_64 2.4.37-1.amzn2.0.1 amzn2-core 1.3 M
Installing for dependencies:
mod_http2 x86_64 1.11.1-1.amzn2 amzn2-core 150 k
HTTP/2 をサポートするALB、配下のEC2との通信は HTTP/1.1 が利用されます。
AWS ドキュメント
Application Load Balancer のリスナー
Application Load Balancer は、HTTPS リスナーに HTTP/2 のネイティブサポートを提供します。1 つの HTTP/2 コネクションで最大 128 のリクエストを並行して送信できます。ロードバランサーは、これらのリクエストを個々の HTTP/1.1 のリクエストに変換し、ターゲットグループの正常なターゲットにこれを分配します
対策
「mod_http2」のSRPMパッケージに含まれるSPECファイルより設定ファイルを特定し、 モジュールの無効化後、httpdサービスを再起動しました。
設定ファイルの特定
モジュールをロードしている 設定ファイルの10-h2.conf
の特定と、
yum update などによる設定が上書きされない事 「%config(noreplace)」を確認しました。
$ yumdownloader --source mod_http2
$ rpm -Uvh mod_http2-1.11.1-1.amzn2.src.rpm
$ cat ~/rpmbuild/SPECS/mod_http2.spec | grep 10-h2.conf
echo "LoadModule http2_module modules/mod_http2.so" > %{buildroot}%{_httpd_modconfdir}/10-h2.conf
%config(noreplace) %{_httpd_modconfdir}/10-h2.conf
モジュールの無効化
sed -i -e "s/^LoadModule/#LoadModule/g" /etc/httpd/conf.modules.d/10-h2.conf
設定変更確認
cat /etc/httpd/conf.modules.d/10-h2.conf
# LoadModule http2_module modules/mod_http2.so
httpd再起動
sudo systemctl restart httpd
レスポンス確認
変更前
$ curl -v http://localhost/ > /dev/null
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 03 Apr 2019 09:45:31 GMT
< Server: Apache/2.4.37 ()
< Upgrade: h2,h2c
< Connection: Upgrade
< Last-Modified: Thu, 10 Jan 2019 00:16:31 GMT
< ETag: "e2e-57f0f7d8a35c0"
< Accept-Ranges: bytes
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
変更後
$ curl -v http://localhost/ > /dev/null
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Wed, 03 Apr 2019 09:51:10 GMT
< Server: Apache/2.4.37 ()
< Last-Modified: Thu, 10 Jan 2019 00:16:31 GMT
< ETag: "0-58604a5875420"
< Accept-Ranges: bytes
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
レスポンスヘッダから、HTTP/2 切替を促す、Upgrade: h2,h2c
の出力がなくなりました。
まとめ
効率の良い通信が期待できる ALBの HTTP/2 サポートを無効化する事なく、一部のブラウザ(Safari)で発生していたHTTPS接続エラーを回避する事ができました。
ELB配下で利用する場合には不要な Amazon Linux2 のmod_http2 モジュール 無効化により僅かながらもトラフィックやEC2リソースの節約にもなったと推測されます。
EC2インスタンス起動時に Amazon Linux2 の httpd をインストールする場合、以下のようなユーザーデータをお試しください。
#!/bin/bash -xe
# httpd install
yum install -y httpd
# mod_http2 off
sed -i -e "s/^LoadModule/#LoadModule/g" /etc/httpd/conf.modules.d/10-h2.conf
# KeepAlive On
echo 'KeepAlive On' > /etc/httpd/conf.d/keepalive.conf
# KeepAliveTimeout
echo 'KeepAliveTimeout 120' >> /etc/httpd/conf.d/keepalive.conf
# systemctl
systemctl start httpd
systemctl enable httpd
参考
KeepAlive 設定は、以下を参考に設定しました。
ELB のバックエンドサーバーとして Apache または NGINX を使用するための最適な設定を教えてください。