チェーンは順序ではなく具体性で勝つ
一言でいうと
リスナーは「アドレスとポートの組」であり、その中のフィルターチェーンが実際に接続を受けます。どのチェーンが受けるかは接続の性質(SNI・トランスポートプロトコル・ALPN・送信元)で決まり、その判定は8段階の固定された順序で絞り込まれます。設定に書いた順序は関係ありません。
なぜ必要なのか
イングレスゲートウェイ1台が数十のドメインを受けます。ドメインごとに証明書が違い、HTTP/2で受けなければならないドメインもあり、内部のヘルスチェックは平文で入ってきます。これをポートごとにプロセスを別々に立てて解決する方法もありますが、そうするとアドレスをその分だけ割り当てる必要があり、設定もその分だけ散らばります。
Envoyは別の道を選びました。ポートは1つのままにして、そのポートに入ってきた接続の性質を見て別のチェーンへ送ります。 そのため、4431つがドメインごとに別の証明書を提示でき、同じポートが平文のリクエストも受けられます。
この構造を知らないと、2つの症状の前で行き詰まります。1つは「証明書は確かに入れたのに、このドメインだけ接続がただ切れる」、もう1つは「チェーンを上に移したのに、まだ下のものが受ける」です。どちらも原因は同じで、チェーンの選択規則を順序と誤解したことです。
どう動くのか
接続が入ってくると、まずリスナーフィルターが動きます。これらのフィルターはまだバイトを読むだけで、何も変更しません。その中でもtls_inspectorの働きが核心です。TLSハンドシェイクの最初のメッセージ(ClientHello)は平文なので、復号しなくても読むことができ、そこにSNI(接続しようとしている名前)とALPN(使いたいプロトコルの一覧)が入っています。インスペクターはその2つを取り出して接続に付けておき、ついでに「この接続はTLSかどうか」も判定して、transport_protocolをtlsまたはraw_bufferに設定します。
このフィルターを有効にしないと、server_namesとapplication_protocolsの条件はどの接続にも一致しません。設定は通るのに動作しないだけなので、最も見つけにくい種類のミスです。
その次にマッチングが動きます。公式ドキュメントに書かれている順序は次のとおりです。
1. 목적지 포트 2. 목적지 IP
3. 서버 이름(SNI) 4. 전송 프로토콜
5. 애플리케이션 프로토콜(ALPN)
6. 직접 연결된 출발지 IP 7. 출발지 유형
8. 출발지 IP 9. 출발지 포트
各段階で最も具体的に一致するチェーンだけが次の段階へ進みます。条件を書いていないチェーンは「何でも受ける」という意味なので生き残りますが、条件を書いたのに合わないチェーンは、その場で脱落します。すべての段階を通過すると、残るチェーンは最大1つであることが保証されます。
ここから出る結論の1つが、実務でよく足を引っ張ります。SNIの条件はALPNの条件より先です。そのため、server_names: ["a.example.com"]だけを書いたチェーンと、application_protocols: ["h2"]だけを書いたチェーンがあるとき、a.example.comにHTTP/2のリクエストが来ると、SNI側が受けます。「h2はあちらのチェーンへ送るつもりだったのに」という言葉が出てくる場面が、まさにここです。
ワイルドカードも具体性で比較します。SNIがwww.example.comのとき、優先順位はwww.example.com → *.example.com → *.com → 条件なしの順です。
現場での姿
「このドメインだけ接続が切れます」という報告では、応答コードがないことが手がかりです。404や503はHTTPレイヤーまで届いたという意味ですが、一致するチェーンがないとその前で接続が閉じられるため、クライアントにはTLSハンドシェイクの失敗としてしか見えません。新しいドメインの証明書を入れるときに、server_namesに名前を追加し忘れると、まさにこの形になります。
「チェーンの順序を変えても結果が同じです」というのは当然です。順序はマッチングに使われません。変えるべきなのは条件の具体性です。
平文とTLSを1つのポートで受ける構成。Kubernetesの中では、ヘルスチェックは平文で、外から来るトラフィックはTLSで受けたいときに、transport_protocol: raw_bufferのチェーンを1つ追加して解決します。ポートをもう1つ開き、ファイアウォールルールをもう1つ作るよりも、はるかに簡単です。
公式ドキュメント: Listeners・FilterChainMatch・TLS Inspector
次のラボですること
証明書を2枚自分で作ってSNIでチェーンを振り分け、同じポートで平文とTLSを一緒に受け、SNIとALPNが同時に一致したときにどちらが勝つかを自分で確認します。最後にどのチェーンにも一致しないリクエストを送り、「応答ではなく沈黙」を目で確かめます。