ちょっと話題の記事

ALBで複数のSSL/TLS証明書を設定できるSNIに対応しました

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ウィスキー、シガー、パイプをこよなく愛する大栗です。

Application Load Balancerで同じポートに複数のSSL/TLS証明書を設定できるSNI(Server Name Indication)に対応しました。

SNI(Server Name Indication)とは

SNI(Server Name Indication)とはRFC 6066に記述されている仕様です。「Server Name Indication」つまりサーバ名表示のことです。SNI以前の仕様ではIPアドレス/ポートに対して一つのSSL/TLS証明書しか設定が行なえませんでした。SNIによりサーバ名ごとにSSL/TLS証明書を設定できるようになり、リクエストから証明書を適用するサーバ名が判断して適用するようになります。

HTTPSリクエストのホストヘッダーを見れば判断できるだろうと思われるかもしれませんが、HTTPSで暗号化されるため証明書を適用する前にはホスト名を判断できません。そこで、SSL/TLSハンドシェイクのプロセス中でホスト名を送信することで解決しています。クライアントからサーバへ初回アクセスする時のClient Helloの中にserver_nameと言う型のExtensionsを含めてアクセス先のホスト名(host_name)を通知します。サーバ側ではClient Hello(とクライアントへ返すServer Hello)の内容を元にSSL/TLSで通信するための暗号形式などを決定するので、SSL/TLSで暗号化する前にアクセス先のホスト名が判断でき適切な証明書を適用します。

2003年6月にRFC 3546でTLSに加わった仕様であるため、古いUserAgentではSNIに対応していない場合がありました。例えばWindows XP上のIEやAndroid 2系ではSNIを使用できません。幸いなことにWindows XPは延長サポートフェーズが終了していますし、Android 2系最期のバージョンである2.3.7のリリースから6年を経過しているので、一般的な環境でSNIを使用できないということは無いでしょう(と思いたい)。

試してみる

下図のような環境を考えてみます。

ALB-SNI2

ALBにtarget-1.example.comtarget-2.example.comの2個のドメインを設定します。同様にACMで各々の証明書をALBに設定します。EC2側でもバーチャルホストで2個のサーバ名を受け付けます。

証明書の取得

ここでは証明書はACMで取得します。以下のエントリを参考に2つのドメインの証明書を発行してください。

[ACM] SSL証明書発行時のドメイン認証メールをSESで受け取ってみた

EC2の設定

ここでは以下を前提とします。

  • リージョン:東京リージョン
  • OS:Amazon Linux AMI 2017.09.0(ami-2a69be4c)
  • インスタンスタイプ:t2.small
  • Webサーバ:Apache HTTP Server 2.4.27

EC2を起動してhttpdをインストールしておきます。

httpd.confはこんな感じに適当に2つのホスト名でリスエストを受けるようにしておきます。

/etc/httpd/conf/httpd.conf

ServerRoot "/etc/httpd"

Listen 80

Include conf.modules.d/*.conf

User apache
Group apache

<VirtualHost *:80>
    DocumentRoot "/var/www/target1/html"
    ServerName target-1.example.com
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "/var/www/target2/html"
    ServerName target-2.example.com
</VirtualHost>

<IfModule mime_module>
    TypesConfig /etc/mime.types
</IfModule>

アクセス確認用のファイルを作成します。

$ sudo mkdir -p /var/www/target1/html /var/www/target2/html
$ echo target-1 `curl -s http://169.254.169.254/latest/meta-data/instance-id` | sudo tee /var/www/target1/html/index.html
target-1 i-0a1b2c3d4e5f6g7h8
$ echo target-2 `curl -s http://169.254.169.254/latest/meta-data/instance-id` | sudo tee /var/www/target2/html/index.html
target-2 i-0a1b2c3d4e5f6g7h8

httpdを開始します。

$ sudo service httpd start
Starting httpd:                                            [  OK  ]

index.htmlにアクセスできることを確認しましょう。デフォルトのtarget-1の内容が表示されます。

$ curl http://127.0.0.1/index.html
target-1 i-0a1b2c3d4e5f6g7h8

上記EC2の設定をもう一台別AZで実施します。

ターゲットグループの作成

以下の内容でターゲットグループを作成します。

項目名 備考
ターゲットグループ名 sni-target
プロトコル HTTP
ポート 80
ターゲットの種類 instance
VPC <EC2を設置したVPC>
ヘルスチェックの設定 プロトコル HTTP
ヘルスチェックの設定 パス /index.html

作成したターゲットグループのターゲットとして2台のEC2を設定します。

ALBの作成

ALB(Application Load Balancer)を作成します。

1. ロードバランサーの設定

項目名 備考
名前 sni-alb
スキーマ インターネット向け
IP アドレスタイプ ipv4
ロードバランサーのプロトコル HTTPS(セキュア HTTP)
ロードバランサーのポート 443
アベイラビリティゾーン <対象VPCのパブリックなサブネット>

2. セキュリティ設定の構成

項目名 備考
証明書タイプ ACM から証明書を選択する
証明書の名前 target-1.example.com デフォルトの証明書です
セキュリティポリシー ELBSecurityPolicy-TLS-1-2-2017-01 安全なプロトコルのみを許可します

3. セキュリティグループの設定

クライアントのHTTPSリクエストを受けられて、EC2へHTTPリクエストができるセキュリティグループを設定します。

4. ルーティングの設定

項目名 備考
ターゲットグループ 既存のターゲットグループ
名前 sni-target
プロトコル HTTP
ポート 80
ターゲットの種類 instance
ヘルスチェック プロトコル HTTP
ヘルスチェック パス /index.html

5. ターゲットの登録

先程設定したEC2が設定されていることを確認して作成します。

SNIの設定

作成したALBのリスナーでView/edit certificatesをクリックします。

EC2_Management_Console

target-2.example.comの証明書を選択して追加をクリックします。

EC2_Management_Console

DNSの設定

ここではRoute 53で登録します。

このような形で、Alias Targetをtarget-1.example.comtarget-2.example.comで登録します。Alias Targetで登録する名前はdualstack.<ALBのDNS名>となります。

Route_53_Management_Console

アクセスしてみる

まずはhttps://target-1.example.com/index.htmlにアクセスしてみます。するとtarget-1の内容が表示されます。

https___target-1_oguri_classmethod_info_index_html

証明書も確認してみるとtarget-1.example.comの証明書でアクセスしています。target-1の内容が表示されます。

スクリーンショット_2017_10_11_20_22

次にhttps://target-2.example.com/index.htmlにアクセスしてみます。target-2の内容が表示されます。

https___target-2_oguri_classmethod_info_index_html

証明書も確認してみるとtarget-2.example.comの証明書でアクセスしています。

スクリーンショット_2017_10_11_20_26

このようにホスト名によって別の証明書でアクセスしていることが確認できました。

特殊なパターンとしてIPアドレスでアクセスする場合を試してみます。https://203.0.113.139/index.htmlのようにIPアドレスでアクセスしてみるとtarget-1の内容が表示されます。これはhttpd.confでtarget-1.example.comを最初に書いているためデフォルトの設定となっているためです。

https___54_64_10_139_index_html

証明書も確認してみるとtarget-1.example.comの証明書でアクセスしています。

スクリーンショット_2017_10_11_20_22

デフォルトの証明書をtarget-2で、追加の証明書をtarget-1に変更してみると、IPアドレスでアクセスした時の証明書もtarget-2となります。

スクリーンショット_2017_10_11_20_26

このようにホスト名によって別の証明書でアクセスしていることが確認できました。

さいごに

今回ALBがSNIに対応することによって複数の証明書を設定可能となりました。実は、以前から同じように複数ドメインのHTTPSアクセスを1つのALBでホストすることは可能でした。マルチドメイン証明書を使用することです。しかしマルチドメイン証明書を使用すると、ドメインを追加する場合に全ドメインの証明書の再発行が必要となり運用上の手間が増えます。SNIではドメインごとに別の証明書を運用できるため個別に追加削除が行えるという利点があります。また1つの証明書にできないドメインでも使用可能となります。1つの環境で複数のドメインをホストする場合にはALBのSNIサポートが有力な選択肢となると思います、

追伸

AWS田舎クラウドデザインパターン(AWS Inaka Cloud Design Pattern, 略してICDPと呼ぶ)というネタに100EZUD(100ドメイン EC2に 全部入り 運用次第で どんどん安く!)というパターンがありましたが、ALBとACMがあればhttpsでも大丈夫ですね!(ネタが古すぎて覚えている人がいなそう。。。)