An HTTPS endpoint exposed through a Kubernetes LoadBalancer Service can behave differently for external clients and pods inside the same cluster. External traffic might traverse the cloud load balancer, while in-cluster traffic to the same load balancer IP can follow Kubernetes Service routing.

For an OKE environment using Envoy Gateway, that difference can produce a confusing symptom: external HTTPS succeeds, but a pod calling the same hostname over HTTPS fails with SSL wrong version number.

Validated ipMode Proxy flow

The short answer

The validated fix is to configure the OKE LoadBalancer Service with LoadBalancerIPMode set to Proxy:

metadata:
  annotations:
    oci.oraclecloud.com/ingress-ip-mode: "proxy"

Use this fix when the intended architecture is:

Client or pod -> OCI Load Balancer HTTPS listener -> TLS termination -> HTTP backend -> Envoy Gateway

With ipMode: Proxy, in-cluster traffic to the LoadBalancer IP goes to the actual OCI Load Balancer first. That preserves the existing OCI Load Balancer TLS termination and HTTP backend design. This solution was validated for the observed OKE and Envoy Gateway issue.

Why the failure happens

In the failing configuration, the generated Service reported ipMode: VIP:

status:
  loadBalancer:
    ingress:
      - ip: 192.0.2.10
        ipMode: VIP

The gateway design intentionally terminated TLS at the OCI Load Balancer and forwarded HTTP to Envoy:

External client
  -> HTTPS to OCI Load Balancer
  -> TLS terminates at the OCI Load Balancer
  -> HTTP to Envoy Gateway

That design is valid for external clients. The problem appeared when a pod inside OKE called the same endpoint over HTTPS.

# External client
curl -vk https://<api-hostname>/
# succeeds

# Pod inside OKE
curl -vk https://<api-hostname>/
# fails with SSL wrong version number

# Same pod, same endpoint, plaintext HTTP on port 443
curl -vk http://<api-hostname:443>/
# succeeds

These results point to a protocol boundary problem, not a TLS version mismatch. The pod sent TLS bytes, but the endpoint it reached expected HTTP.

With ipMode: VIP, Kubernetes can treat the load balancer address as a local Service VIP. In this case, in-cluster traffic to the LoadBalancer IP was routed directly to the Envoy Service endpoint, bypassing the OCI Load Balancer listener where TLS termination happens.

Pod
  -> LoadBalancer IP:443
  -> Kubernetes Service routing
  -> Envoy Gateway HTTP listener

The port was 443, but Envoy was configured for HTTP behind the load balancer. Port numbers do not define protocol; the listener configuration does.

Failing ipMode VIP flow

The validated fix: ipMode: Proxy

OKE supports LoadBalancerIPMode for clusters running Kubernetes 1.30 or later. Oracle documents the annotation oci.oraclecloud.com/ingress-ip-mode: "proxy" for specifying Proxy mode on a load balancer Service.

After applying the annotation through the Service generation path, the Service should report:

status:
  loadBalancer:
    ingress:
      - ip: 192.0.2.10
        ipMode: Proxy

The traffic path then becomes:

Pod
  -> LoadBalancer IP:443
  -> actual OCI Load Balancer HTTPS listener
  -> TLS termination at OCI Load Balancer
  -> HTTP backend connection
  -> Envoy Gateway HTTP listener

This is the smallest fix when the goal is to keep OCI Load Balancer TLS termination, WAF or layer-7 load balancer behavior, and HTTP forwarding to Envoy.

Configure it through Envoy Gateway

Do not manually patch the generated Envoy Gateway Service except for a temporary test. Envoy Gateway can reconcile the Service and remove direct edits.

Instead, set the annotation in the Envoy service-generation configuration. The exact field path depends on the installed Envoy Gateway version, but conceptually it belongs under the EnvoyProxy referenced by the GatewayClass:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: example-envoy-proxy
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyService:
        annotations:
          oci.oraclecloud.com/ingress-ip-mode: "proxy"

Validate the field path against the Envoy Gateway CRD installed in the cluster before applying.

Validate the change

Confirm that the cluster runs Kubernetes 1.30 or later:

kubectl version

Confirm that the generated Envoy LoadBalancer Service reports Proxy:

kubectl get svc -n envoy-gateway-system <envoy-service> \
  -o jsonpath='{.status.loadBalancer.ingress[0].ipMode}{"\n"}'

Expected result:

Proxy

Then retest from an application pod:

curl -vk https://<api-hostname>/

The TLS handshake should reach the OCI Load Balancer certificate, and the request should continue through the existing HTTP backend path to Envoy.

When to choose another pattern

Use ipMode: Proxy for this failure when OCI Load Balancer TLS termination and HTTP backend forwarding are intentional. Choose another pattern only when the TLS boundary must change.

Solution options
RequirementRecommended pattern
Keep OCI Load Balancer TLS termination and HTTP backend to EnvoyipMode: Proxy
Keep load balancer layer-7 features and encrypt load balancer-to-Envoy trafficOCI Load Balancer termination plus backend TLS re-encryption
Envoy must see the original TLS session or client certificateTCP/TLS passthrough to Envoy
Internal callers do not need HTTPSUse a clearly named internal HTTP Service, not plaintext HTTP hidden behind port 443

Summary and call to action

If external HTTPS works but pod HTTPS fails with SSL wrong version number, verify whether the request is bypassing the cloud load balancer and landing directly on a plaintext backend. On OKE clusters running Kubernetes 1.30 or later, LoadBalancerIPMode Proxy is the targeted fix when in-cluster clients must traverse the actual OCI Load Balancer TLS listener.

Review the generated Envoy LoadBalancer Service, confirm the reported ipMode, and apply oci.oraclecloud.com/ingress-ip-mode: "proxy" through Envoy Gateway service generation when this traffic path is required.

Sources