🥘

Cilium/IngressのHost Network ModeはひとつのポートでHTTP/HTTPS両方対応するので叩く時に注意が必要

2025/03/09に公開

Cilium v1.16.0 で追加されたHost network modeを試します。下記のようなValuesでHelmを適応すると、Ingressを作成した時にHostNetworkが有効になります。L7相当のロードバランサをクラスタ内に作成することができるので特にオンプレ環境で嬉しい機能になります。

tldr

  • Host Network ModeがバインドするポートはHTTPSもHTTPも両対応する
  • 自己証明書を用いて検証する時は証明書を無視したとしてもSNIが有効にならないIP直打ちのアクセスはできない。curlでは --resolve を用いる必要あり。
# test.example.com としてアクセスしたいならば...
curl --resolve test.example.com:8080:NODE_IP -k https://test.example.com:8080/

詳細

ingressController:
  default: true
  enabled: true
  loadbalancerMode: shared
  hostNetwork:
    enabled: true
    sharedListenerPort: 8080
nodePort:
  enabled: true
upgradeCompatibility: "1.14"

さて、この設定ファイルに含まれているホストにバインドするポートである ingressController.hostNetwork.sharedListenerPort ですが、これがHTTPを話すのかHTTPSを話すのか見た目だけでは分かりません。公式のドキュメントにもさらっとした説明しか書かれていません。

ingressController.hostNetwork.sharedListenerPortc: Host network port to expose the Cilium ingress controller Envoy listener. The default port is 8080. If you change it, you should choose a port number higher than 1023 (see Bind to privileged port).

そこでコードを見に行くと、どちらも話しそうなことがわかります。

Port on the host network that gets used for the shared listener (HTTP, HTTPS & TLS passthrough)

https://github.com/cilium/cilium/blob/86639d0a4d0242a4f9e42f51d2fba13152a41a6c/operator/pkg/ingress/cell.go#L84

curlで検証

HTTP

curl -H "Host: test.example.com" http://NODE_IP:8080/

HTTPS
-k を利用して証明書を無視すれば良いかと思いますが、実際にはうまくいきません。

curl -H "Host: test.example.com" -k https://NODE_IP:8080/
curl: (35) Recv failure: Connection reset by peer

HostヘッダとCNI

HTTPの場合はHostヘッダーで正しいバックエンドを特定できますが、HTTPSの場合は少し複雑です。HTTPSでは、TLSハンドシェイクの段階でSNIという仕組みを使ってホスト名を送信する必要があります。

単純に -H "Host: test.example.com" を付けても、これはTLSハンドシェイク後に暗号化されたHTTPリクエストの一部となるため、Cilium(Envoy)が適切なバックエンドを選択する前の段階では見えません。これが Connection reset by peer エラーが発生する理由です。

TLSハンドシェイクの際に適切なSNIが送信されないと、Ciliumはどの証明書を提示すべきかわからず、接続をリセットします。IP直打ちのHTTPSアクセスではSNI情報が欠落しているため、正しくルーティングできないのです。

--resolve を使って解決

curlの --resolve オプションを使うと、特定のホスト名とポートの組み合わせに対して使用するIPアドレスを指定できます。これによりDNS解決をバイパスしつつ、正しいホスト名をSNIとして送信できます。

curl --resolve test.example.com:8080:NODE_IP -k https://test.example.com:8080/

おわり

Cloudflare Tunnelsに慣れすぎていて直接HTTPSをうまくやろうとすると面倒くさいですね。

Discussion