TL;DR
OSSM 2→3 アップグレード後、自己署名証明書を使うサービスへのアクセスが 503 になる。
Ingress Gateway がサーバー証明書の検証に失敗することが原因で、
Istio 1.21 で有効化された以下の2つのデフォルト変更が重なって顕在化する。
| フラグ | 変更内容 | 影響 |
|---|---|---|
VERIFY_CERT_AT_CLIENT=true |
Envoy がサーバー証明書を OS の CA バンドルで検証するようになった | 自己署名 CA は CA バンドルにないため検証失敗 → 503 |
ENABLE_AUTO_SNI=true |
Host ヘッダーから SNI を自動設定するようになった | Ingress Gateway 経由では外部ホスト名が SNI になり、内部 DNS 名の SAN と不一致 → TLS 失敗 |
暫定対応: DestinationRule に insecureSkipVerify: true
恒久対応: sni で SAN を明示、credentialName で CA を登録、EKU と CA トラストチェーンを整備
これらの破壊的変更は Red Hat の移行ガイドに記載されていない。
この問題が発生する条件
以下をすべて満たす場合に発生する。
| 条件 | 詳細 |
|---|---|
| OSSM 2.x → 3.x へアップグレード済み | Istio 1.20 → 1.24 相当 |
| サービスが自己署名証明書を使用 | OpenShift 自動生成証明書(Service Serving Certificates)も該当 |
DestinationRule に SIMPLE を設定 |
Ingress Gateway からの TLS 通信を意図している |
| Ingress Gateway 経由でアクセス | クラスター外部からルーティングしている |
環境構成
DR-1:外部アクセス経路(今回問題が発生した経路)
ブラウザ
│ HTTPS(OpenShift の Route 証明書)
▼
OpenShift Route
│ HTTP(Route が TLS を終端)
▼
Ingress Gateway Pod(Envoy)
│ TLS(tls.mode: SIMPLE)
▼
対象サービス Pod
├─ Envoy サイドカー(TLS 終端・サービスメッシュ制御)
└─ アプリケーションコンテナ(Envoy から plaintext で受信)
DR-2:内部サービス間通信経路(今回影響なし)
内部クライアントサービス Pod
├─ アプリケーションコンテナ(Envoy へ plaintext で送信)
└─ Envoy サイドカー(mTLS・Istio 管理証明書)
│ mTLS(tls.mode: ISTIO_MUTUAL)
▼
対象サービス Pod
├─ Envoy サイドカー(mTLS 終端・サービスメッシュ制御)
└─ アプリケーションコンテナ(Envoy から plaintext で受信)
| 項目 | 内容 |
|---|---|
| Service Mesh | OSSM 2.x → 3.x へアップグレード |
| サイドカー注入 | 対象 Namespace に istio-injection=enabled 済み |
| DestinationRule | 2つ存在。Ingress Gateway→サービス用は tls.mode: SIMPLE
|
内部クライアントサービス→サービス用は tls.mode: ISTIO_MUTUAL(後者は今回影響なし) |
|
| PeerAuthentication | 存在しない(デフォルト動作) |
| サービスの証明書 | 自己署名証明書(OpenShift 自動生成を含む) |
症状
- ブラウザからサービスにアクセスすると 503 Service Unavailable
- Ingress Gateway のログに
certificate_verify_failedエラー
前提知識:キーワードの解説
サイドカー(Sidecar)
Podの中にアプリケーションコンテナとは別に自動注入されるプロキシコンテナ(Envoy)のこと。
アプリが意識しなくても、すべての通信がこのプロキシを経由することで
メッシュの機能(暗号化、トラフィック制御など)が実現される。
┌─── 対象サービス Pod ──────────────────────┐
│ [Envoyサイドカー] ← → [サービスコンテナ] │
└───────────────────────────────────────────┘
↑
メッシュ内の通信はここを通る
Ingress Gateway
メッシュへの「入り口」となる専用のプロキシPod。クラスター外部(ブラウザ)からの通信を受け取り、メッシュ内のサービスに転送する役割を持つ。
DestinationRule
メッシュ内で特定の送信先への通信をどう扱うかを定義するリソース。
今回は tls.mode: SIMPLE が設定されており、
「この送信先との通信にはIstio管理のTLSを使う」という意味になる。
PeerAuthentication
メッシュ内で受信側がどの通信を受け付けるかを定義するリソース。
今回は存在しないため、デフォルト動作が適用されている。
根本原因
OSSM 3 は Istio 1.24 ベース、OSSM 2.6 は Istio 1.20 ベースであり、
この間の Istio 1.21 で以下の2つのデフォルト値が変更された。
① VERIFY_CERT_AT_CLIENT=true
Envoy クライアント側がサーバー証明書を OS の CA バンドルで検証するようになった。
- OSSM 2(Istio 1.20):検証なし → 自己署名証明書でも通信可能
- OSSM 3(Istio 1.24):検証あり → 自己署名 CA は OS の CA バンドルにないため検証失敗
② ENABLE_AUTO_SNI=true(Ingress Gateway 経由では自動解決できないケースがある)
DestinationRule に SNI を明示しなくても Host ヘッダーから自動設定されるようになった。
ただし Ingress Gateway 経由の場合、Host ヘッダーは外部公開ホスト名になる。
一方、自動生成証明書の SAN は <service>.<namespace>.svc.cluster.local 形式の
内部 DNS 名であるため、SNI と SAN が不一致となり TLS ハンドシェイクが失敗する。
OSSM 2 では①②ともオフだったため動作していた問題が、OSSM 3 で一度に顕在化する。
原因の特定
通信経路を整理すると問題の発生箇所が絞り込める。
ブラウザ
│ HTTPS(OpenShift の Route 証明書)
▼
OpenShift Route
│ HTTP(Route が TLS を終端)
▼
Ingress Gateway Pod(Envoy)
│ TLS(tls.mode: SIMPLE)
│ ├─ 対象サービスの Envoy サイドカーがサーバー証明書(serving-cert)を提示
│ └─ Ingress Gateway の Envoy がその証明書を検証 ← ★ VERIFY_CERT_AT_CLIENT=true により失敗
▼
対象サービス Pod
├─ Envoy サイドカー(TLS 終端・サービスメッシュ制御)
└─ アプリケーションコンテナ(Envoy から plaintext で受信)
Ingress Gateway → 対象サービス間の DestinationRule に tls.mode: SIMPLE が設定されているため、
Ingress Gateway は TLS で対象サービスに接続しに行く。
OSSM 3(Istio 1.21 以降)では VERIFY_CERT_AT_CLIENT=true がデフォルトになったことで、
このとき Ingress Gateway 側がサーバー証明書を検証するようになった。
-
certificate_verify_failed:Ingress Gateway でサーバー証明書の検証に失敗 -
503:その結果、upstream への接続そのものが失敗
「Pod に届いていない」のではなく「TLS ハンドシェイクで弾かれている」。
補足:2つの DestinationRule で挙動が異なった理由
本環境では対象サービスに対して DestinationRule が2つ存在した。
| DR | 経路 | tls.mode | 今回の影響 |
|---|---|---|---|
| DR-1 | Ingress Gateway → 対象サービス | SIMPLE |
影響あり(503) |
| DR-2 | 内部クライアントサービス → 対象サービス | ISTIO_MUTUAL |
影響なし |
DR-2 が影響を受けなかった理由は、ISTIO_MUTUAL では Istio が自動管理する証明書(SPIFFE 形式)を使用するため、対象サービスの serving-cert(OpenShift 内部 CA 署名)が TLS ハンドシェイクに関与しないからである。VERIFY_CERT_AT_CLIENT の検証対象はサーバー証明書であり、Istio 管理証明書は Istio の内部 CA で署名されているため検証を通過する。
一方 DR-1 の SIMPLE では、対象サービスが提示するサーバー証明書(serving-cert)をそのまま Envoy が検証しに行くため、OpenShift 内部 CA が OS の CA バンドルに含まれていないことが問題として顕在化した。
解決策
暫定対処:insecureSkipVerify: true
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: example-service
namespace: <your-namespace>
spec:
host: <service-host>
trafficPolicy:
tls:
mode: SIMPLE
insecureSkipVerify: true # 証明書検証をスキップ
503 は即座に解消されるが、証明書の正当性を確認しない状態になる。
MitM 攻撃への耐性がなくなるため、本番環境では恒久対処に移行すること。
恒久対処:sni と credentialName を明示する
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: example-service
namespace: <your-namespace>
spec:
host: <service-host>
trafficPolicy:
tls:
mode: SIMPLE
sni: <service>.<namespace>.svc.cluster.local # 証明書の SAN と一致させる
credentialName: my-ca-cert # CA 証明書を Secret で参照
| フィールド | 目的 | 対応する原因 |
|---|---|---|
sni |
SNI を証明書の SAN(内部 DNS 名)に合わせる | ENABLE_AUTO_SNI の自動解決の限界 |
credentialName |
自己署名 CA を明示して検証を通す | VERIFY_CERT_AT_CLIENT=true |
credentialName で参照する Secret は以下のように用意する(Istio 1.14+)。
oc create secret generic my-ca-cert \
--from-file=ca.crt=./ca.crt \
-n <your-namespace>
証明書の EKU とトラストチェーンの確認
insecureSkipVerify を外した後も503が続く場合は、証明書自体の問題が残っている。
openssl x509 -in server.crt -noout -text | grep -A3 "Extended Key Usage"
serverAuth と clientAuth の両方が含まれていること、
および CA トラストチェーンが正しく構成されていることを確認する。
まとめ
| フェーズ | 内容 |
|---|---|
| 症状 | 503 + certificate_verify_failed
|
| 問題の発生箇所 | Ingress Gateway によるサーバー証明書の検証失敗 |
| 根本原因① |
VERIFY_CERT_AT_CLIENT=true:自己署名 CA が OS の CA バンドルにない |
| 根本原因② |
ENABLE_AUTO_SNI の限界:Ingress Gateway 経由で SNI と SAN が不一致 |
| 暫定対処 |
insecureSkipVerify: true で検証をスキップ |
| 恒久対処 |
sni で SAN 一致、credentialName で CA を明示、EKU・トラストチェーンを整備 |
OSSM 2→3 のアップグレードでは TLS 周りのデフォルト動作が複数変わっている。
自己署名証明書(自動生成含む)を使うサービスが Service Mesh に参加している場合は、
アップグレード前にこの点を必ず確認してほしい。
今後の教訓
トラブルシューティングの進め方
今回の対応を振り返ると、エラーログは早期に取得できていたが、
「ログが何を示しているか」の読み取りと「構成の把握」が不足していたため、
原因特定に時間がかかった。
以下のループを意識することで、次回以降の解決速度が上がる。
通信経路を図示する
│
▼
ログからどのレイヤーで失敗しているか読み取る
│
▼
仮説を1つ立てる
│
▼
検証する
│
▼
結果で仮説を修正 → 次の仮説へ
やみくもに設定を変えて試すのではなく、仮説を立ててから検証することが遠回りを防ぐ。
ログの読み取り方
エラーメッセージはレイヤーを教えてくれる。
| エラー | 意味 | 該当レイヤー |
|---|---|---|
no healthy upstream / connection refused
|
TCP 接続失敗 | Pod に届いていない |
certificate_verify_failed |
TLS ハンドシェイク失敗 | Pod には届いている |
Ingress Gateway の Envoy ログが最も情報量が多いが、権限上確認できない場合でも
対象サービス側の Envoy サイドカーログや Kiali から同等の切り分けが可能。
平時にやっておくこと
- 自分のシステムの通信経路を図示して説明できる状態にしておく
- DestinationRule 等のメッシュ設定が何のために存在するか把握しておく
構成知識は障害時に初めて理解しようとすると間に合わない。
平時のドキュメント整備が障害時の自己解決率を上げる最短ルートになる。