SNI 以外のクライアントの TLS handshake 中に接続がリセットされる

症状

Apigee エンドポイントを呼び出すときに、クライアント アプリケーションで TLS handshake 中に接続リセット、接続拒否、または同様のエラーが発生することがあります。

エラー メッセージ

  • Postman または Node.js クライアントに ECONRESET エラー メッセージが表示されることがあります。

  • Apigee Ingress IP アドレスに HTTPS 呼び出しを直接行うと、Curl で [Connection reset by peer] が表示されることがあります。次に例を示します。

    curl https://1.2.3.4/basepath -H "Host: your.apigee.domain" -kv
    * Connected to 1.2.3.4 (1.2.3.4) port 443
    * (304) (OUT), TLS handshake, Client hello (1):
    * Recv failure: Connection reset by peer
    * Closing connection 
  • 他のクライアントでは、異なるエラーが表示されることがあります。ただしパターンは同じであり、クライアントは TLS handshake の途中で完全に接続を確立できません。

考えられる原因

Apigee Hybrid の Ingress ゲートウェイでは、デフォルトで Server Name Indication(SNI)が有効になっています。この問題は、クライアントで SNI が有効になっておらず、SNI 非対応のクライアントを有効にするように構成されたワイルドカード Apigee ルートがない場合に発生する可能性があります。これにより、デフォルトの TLS サーバー証明書がクライアントに送信されず、Apigee 上り(内向き)TCP リセットが発生します。

診断

  1. クライアントで SNI が有効になっているかどうかを確認します。SNI が有効になっていないことがわかっている場合は、ステップ 4 に進んで Apigee Hybrid 構成を検証します。

    Apigee Ingress のアクセスログで、SNI サーバー名のないクライアント リクエストの兆候を確認し、SNI 非対応のクライアントのデフォルト証明書で仮想ホストが構成されていないかどうかを確認します。

    • 次のコマンドを使用して、apigee-ingressgateway Pod のリストを取得します。
      kubectl -n apigee get pods -l app=apigee-ingressgateway

      出力例

      NAME                                                              READY   STATUS    RESTARTS   AGE
      apigee-ingressgateway-ext-ingress-myorg-hyb-8f2c412-dvrcp         2/2     Running   0          46h
      apigee-ingressgateway-ext-ingress-myorg-hyb-8f2c412-wg26k         2/2     Running   0          46h
      
    • apigee-ingressgateway Pod のログを取得します。
      kubectl -n apigee logs APIGEE_INGRESSGATEWAY_POD
      ここで、APIGEE_INGRESSGATEWAY_POD は前のコマンド出力に示されている apigee-ingressgateway Pod です。
    • アクセスログは次のようになります。
      {
        "request_time": 1,
        "tls_protocol": null,
        "upstream_service_time": null,
        "request_method": null,
        "request_protocol": null,
        "upstream_response_time": null,
        "bytes_sent": 0,
        "start_time": "2025-05-19T04:46:20.117Z",
        "bytes_received": 0,
        "host": null,
        "upstream_cluster": null,
        "upstream_address": null,
        "remote_address": "10.138.0.28:19432",
        "request_path": null,
        "request_id": null,
        "user_agent": null,
        "status_details": "filter_chain_not_found",
        "request": "- - -",
        "status": 0,
        "x_forwarded_for": null,
        "apigee_dynamic_data": null,
        "upstream_response_flags": "NR",
        "sni_host": null
      }
      
    • 上記のログを分析すると、次のことがわかります。
      1. "sni_host": null: SNI ホスト名がないため、クライアントで SNI が有効になっていません。たとえば、api-test.mydomain.com がこのリクエストに関連付けられています。
      2. "status_details": "filter_chain_not_found" : サーバー証明書は、sni_host に基づく filter chain に基づいて選択されます。sni_host が存在せず、デフォルトが構成されていない場合、filter chain は見つかりません。このため、クライアント リクエストの例に示すように、サーバー証明書は返されません。
      3. "status": 0: 接続がリセットされたため、ステータス コードはありません。
  2. ログを確認する代わりに、クライアントで SNI が有効になっているかどうかをより正確に確認するには、Apigee Ingress の前または Apigee Ingress 自体でパケット キャプチャを行います。これにより、クライアントが TLS handshake の SNI ヘッダーを送信しているかどうかを判断できます。
    1. Google Kubernetes Engine の Apigee Ingress で実行するには、上り(内向き)ゲートウェイを実行しているノードに SSH 接続し、toolbox と tcpdump をインストールする必要があります。
      tcpdump -n -i any -s 0 'host IP_Address' -w FILE_NAME

      ここで、FILE_NAME はパケット キャプチャの出力を保存するファイルの名前(パスを含む)です。

    2. Wireshark または同様のツールを使用してパケット キャプチャを分析します。
    3. 以下に、Apigee Ingress で取得した Wireshark を使用したパケット キャプチャの分析例を示します。.
      • パケット キャプチャ出力のメッセージ 83 は、クライアント(送信元)が「Client Hello」メッセージを Apigee Ingress(宛先)に送信したことを示しています。client-hello.png
      • [Client Hello] メッセージを選択して [Handshake Protocol: Client Hello] を調べると、[Extension: server_name] が欠落していることがわかります。 client-hello-extension.png
      • たとえば、SNI が有効なクライアントでは、次の例に示すように [Extension: server_name] と出力されます。 client-hello-extension-sni.png
      • これで、クライアントが server_name を Apigee Ingress に送信しなかったことが確認できました。
      • SNI server_nameClient Hello メッセージに含まれていないため、サーバー証明書は返されず、Apigee Ingress は RST パケットで接続を閉じます。
  3. SNI 非対応のクライアントがサポートされているかどうかを確認するために、OpenSSL のようなツールを使って、SNI ヘッダーを付けた場合と付けない場合の両方で Apigee のエンドポイントにリクエストを送信してテストします。
    1. SNI 非対応のクライアントが有効になっているかどうかを確認します。
      openssl s_client -connect api-test.mydomain.com:443 -noservername

      出力例

      Connecting to 1.2.3.4
      CONNECTED(00000005)
      write:errno=54
      ---
      no peer certificate available
      ---
      No client certificate CA names sent
      ---
      SSL handshake has read 0 bytes and written 299 bytes
      Verification: OK
      ---
      New, (NONE), Cipher is (NONE)
      This TLS version forbids renegotiation.
      Compression: NONE
      Expansion: NONE
      No ALPN negotiated
      Early data was not sent
      Verify return code: 0 (ok)
      ---
      
      
    2. 上記のレスポンスは、ピア証明書が利用できないことを示しています。これは、コマンドで -noservername オプションを渡したため、Apigee ルート内で SNI クライアントが有効になっていないことを意味します。-noservername フラグの使用中にピア証明書が返された場合、ワイルドカード ルートが構成されていることを示します。
  4. 現在の Apigee ルート構成を確認して、ワイルドカード ルートが仮想ホストで構成され、有効になっているかどうかを確認します。
    1. 次のコマンドを使用して、定義された Apigee ルートのリストを取得します。
      kubectl -n apigee get apigeeroutes 

      出力例

      NAME                                  STATE     AGE
      myorg-hyb-dev-grp-000-33620d0         running   2d1h
      non-sni                               running   17s
      
    2. 各 Apigee ルートで、ワイルドカードを含むホスト名を確認します。

      Apigee ルートごとに、指定されたコマンドを実行して、定義されたホスト名の JSON 配列を取得します。ワイルドカード ルートは、出力内のアスタリスク(*)で示されます。

      kubectl -n apigee get apigeeroute APIGEE_ROUTE_NAME

      myorg-hyb-dev-grp-000-33620d0 ルートの例:

      kubectl -n apigee get apigeeroute myorg-hyb-dev-grp-000-33620d0 -o jsonpath='{.spec.hostnames}'

      出力例

      ["api-test.mydomain.com"]

      non-sni ルートの例:

      kubectl -n apigee get apigeeroute non-sni -o jsonpath='{.spec.hostnames}'

      出力例

      ["*"]

      non-sni apigeeroute は、ホスト名として(*)が含まれているため、ワイルドカード ルートです。

    3. ワイルドカード ルートが構成されている場合、SNI 非対応のクライアントが有効になります。
    4. ワイルドカード ルートの場合は、enableNonSniClient フラグが true に設定されていることを確認します。次のコマンドは、non-sni クライアントを使用してワイルドカード ルートで実行されます。
      kubectl -n apigee get apigeeroute non-sni -o jsonpath='{.spec.enableNonSniClient}'

      出力例

      true
    5. ワイルドカード ルートが存在し、SNI 非対応のクライアントが有効になっている場合は、overrides.yaml ファイルの仮想ホスト構成を確認して、ワイルドカード ルートが additionalGateways にリストされていることを確認します。
      virtualhosts:
      - name: dev-grp
        selector:
          app: apigee-ingressgateway
          ingress_name: ext-ingress
        sslCertPath: ./certs/keystore_dev-grp.pem
        sslKeyPath: ./certs/keystore_dev-grp.key
        additionalGateways: ["non-sni"]
        
    6. additionalGateways は、仮想ホスト構成で定義された Apigee ルートに表示されます。次のコマンドを使用して、構成した additionalGateway が Apigee ルート構成に表示されていることを確認します。
        kubectl -n apigee get apigeeroute APIGEE_ROUTE_NAME -o jsonpath='{.spec.additionalGateways}

      たとえば、myorg-hyb-dev-grp-000-33620d0 ルートは non-sni ルートを additionalGateway として表示します。

        kubectl -n apigee get apigeeroute myorg-hyb-dev-grp-000-33620d0 -o jsonpath='{.spec.additionalGateways}'

      出力例

      ["non-sni"]

    解決策

    診断手順で、クライアントが SNI 非対応であり、Apigee ハイブリッド インストールで SNI 非対応のクライアントが有効になっていないか、正しく構成されていないことが示された場合は、SNI 非対応のクライアントを有効にする方法が記載されたドキュメントに沿って、SNI 非対応のクライアントからのトラフィックを許可します。

    診断情報の収集が必要な場合

    上記の手順でも問題が解決しない場合は、次の診断情報を収集して Google Cloud カスタマーケアに連絡してください。
    • Overrides.yaml
    • 次のコマンドの出力:
      • kubectl -n apigee get apigeeroutes
      • 指定されたルートごとに、次のコマンドを実行します。 kubectl -n apigee describe apigeeroute
    • 問題が発生している環境グループ