AmazonLinux2のhttpdをALB経由で公開するWeb環境で発生していた、SafariのHTTPS接続エラーを改善してみた

はじめに

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 を使用するための最適な設定を教えてください。

ELBのタイムアウトを回避するためApacheのKeepAliveTimeoutを設定する