0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenShift Service Mesh 2→3 アップグレード後、自己署名証明書を使うサービスが503エラーになる

0
Last updated at Posted at 2026-03-27

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 攻撃への耐性がなくなるため、本番環境では恒久対処に移行すること。


恒久対処:snicredentialName を明示する

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"

serverAuthclientAuth両方が含まれていること、
および 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 等のメッシュ設定が何のために存在するか把握しておく

構成知識は障害時に初めて理解しようとすると間に合わない。
平時のドキュメント整備が障害時の自己解決率を上げる最短ルートになる。


参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?