【Fastly】SNI 設定漏れで503 エラー
概要
Fastly で SNI の設定漏れにより、503エラーが発生しました。
本記事では、エラーの原因と対策について記載していきます。
事象
TLD の異なる3つのドメインのうち、jp
は正常にアクセス可能でしたが、tw
, net
は、下画像のように オリジンへのヘルスチェックが失敗しており、リクエストを転送できない状況でした。
Error 503 backend is unhealthy(公式ドキュメントより)
構成
構成図
- Fastly
- 3つのドメインを Fastly のエンドポイントで受け付ける(各ドメインの証明書を登録)
-
Host
ヘッダの中身に応じて転送先オリジンを振り分ける
-
backend
にはhealthcheck
が紐付けてあり、200 の場合はリクエストを送る ← 今回これが失敗してる
- オンプレ
- 3つのドメインを1組の Load Balancer、 App サーバー で受け付ける
- Fastly -> オンプレ間もHTTPSで通信したいため各ドメインの証明書を登録
- 3つのドメインを Fastly のエンドポイントで受け付ける(各ドメインの証明書を登録)
-
Host
ヘッダの中身に応じて転送先オリジンを振り分ける -
backend
にはhealthcheck
が紐付けてあり、200 の場合はリクエストを送る ← 今回これが失敗してる
- 3つのドメインを1組の Load Balancer、 App サーバー で受け付ける
- Fastly -> オンプレ間もHTTPSで通信したいため各ドメインの証明書を登録
Terraform
管理は Terraform を利用しており、backend
とhealthcheck
は以下のようにしています。
var.domain_names
には、map 形式で TLD とドメイン名のペアを設定しています。
resource "fastly_service_v1" "web" {
(中略)
dynamic "backend" {
for_each = var.domain_names
content {
name = "lb_${backend.key}_https"
address = "XX.XX.XX.XX"
auto_loadbalance = false
between_bytes_timeout = 10000
connect_timeout = 1000
first_byte_timeout = 150000
max_conn = 1000
port = 443
use_ssl = true
ssl_cert_hostname = backend.value
healthcheck = "${local.healthcheck_name_lb}-${backend.key}"
}
}
(中略)
dynamic "healthcheck" {
for_each = var.domain_names
content {
name = "${local.healthcheck_name_lb}-${healthcheck.key}"
host = healthcheck.value
path = "/sample.txt"
expected_response = 200
http_version = 1.1
method = "HEAD"
check_interval = 5000
timeout = 32767
initial = 3
threshold = 3
window = 5
}
}
(中略)
}
domain_names = {
"jp" = "example.jp"
"tw" = "example.tw"
"net" = "example.net"
}
原因と対応
Fastly で SNI の設定が漏れていた
原因は、Fastly の backend
の設定にssl_sni_hostname
プロパティがないことでした。
このプロパティは、TLSハンドシェイクの中で、どの証明書を利用するか指定する際に用いられます。
SNIが何かについては、こちらの記事が簡潔に説明してくれていて分かりやすかったです。
利用されている証明書を確認
openssl コマンドから確認できます。
$ openssl s_client -showcerts -connect XX.XX.XX.XX:443
証明書の情報が2枚分表示されますが、先頭に0
がある方を参照します。(1
の方は中間CA証明書)
コモンネームを見ると、デフォルトではjp
が利用されていることが分かります。
Certificate chain
0 s:/C=JP/ST=XXXX/L=XXXX-SHI/OU=XXXX/O=XXXX, Inc./CN=example.jp
i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign RSA OV SSL CA 2018
したがって、tw
, net
のドメインに対しても、jp
の証明書が当たってしまい、TLS通信が開始できないことでヘルスチェックが失敗しているのだと分かります。
opensslコマンドで証明書情報を知る方法はこちらのサイトを参考になりました。
他のドメインの証明書もチェック
先程のコマンドに-servername
オプションを追加することで、SNIでホスト名を指定することができます。
$ openssl s_client -showcerts -connect XX.XX.XX.XX:443 -servername example.net
証明書の設定自体はできているので、SNIでホスト名を指定できれば問題なさそうです。
Certificate chain
0 s:/CN=example.net
i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign GCC R3 DV TLS CA 2020
※ こちらに載せている出力結果は一部マスクしており、本来の出力結果とは異なります。
プロパティを追加
backend
ブロックの中に、ssl_sni_hostname
を追加して、ドメイン名を記載します。
これにより、ヘルスチェックが通るようになり、エラーを解消することができました!
(中略)
dynamic "backend" {
for_each = var.domain_names
content {
name = "lb_${backend.key}_https"
address = "XX.XX.XX.XX"
auto_loadbalance = false
between_bytes_timeout = 10000
connect_timeout = 1000
first_byte_timeout = 150000
max_conn = 1000
port = 443
use_ssl = true
ssl_cert_hostname = backend.value
healthcheck = "${local.healthcheck_name_lb}-${backend.key}"
ssl_sni_hostname = backend.value ## newly add
}
}
(中略)
ひとこと
今回のエラーに遭遇したことで初めてSNIを知り、勉強することができました。
Hostヘッダを付加すれば、サーバー側で認識してくれるんじゃ? と思いましたが、
TLS通信前にヘッダの内容を知ることができないのは盲点でした(言われてみればそうだけど)。
最近のブラウザはSNIに標準対応しているそうで意識することはなさそうです。
こういう機会があって知れてよかった。
Author And Source
この問題について(【Fastly】SNI 設定漏れで503 エラー), 我々は、より多くの情報をここで見つけました https://qiita.com/sasamuku/items/46004f3ed3705c4769ba著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .