Switch to Istio Service Mesh and Gateway
Feature Flag
Istio-native routing requires the enableModernQuiverRouting feature flag. Set it in your gdcn-values.yaml together with the istio values shown below.
When Istio is enabled in the Helm chart, GoodData.CN no longer creates Kubernetes Ingress resources for organization routing. Instead, a shared Istio Gateway and organization-specific VirtualService resources expose per-organization hostnames and route requests into the platform.
Routing rules for all GoodData.CN backend services live in a shared VirtualService delivered by the Helm chart (istio.gateway.sharedVirtualServiceName, gooddata-cn by default). Each organization’s VirtualService delegates to this shared routing definition.
This architecture enables centralized TLS handling (including wildcard certificates) while keeping per-organization hostnames isolated.
Prerequisites
- GoodData.CN chart version 3.53.0 or later. Earlier versions do not support
istio.gateway.existingGateway, including for Dex. - A functional Istio installation:
istio-base,istiod, and an ingress gateway. - The Istio ingress gateway is exposed through a
LoadBalancerservice or an equivalent upstream, and DNS routes every organization hostname (and the DexauthHost) to it. - A TLS secret for the Gateway. Istio reads the credential from the namespace where the ingress gateway pods run, not from the namespace of the
Gatewayresource. Create the secret there. - If your cluster enforces sidecar injection, make sure the GoodData.CN namespaces comply with your injection policy.
Enable Istio in Helm
You can either let the chart create and manage the Gateway, or point the chart at a Gateway you manage yourself.
Option 1: Chart-managed Gateway
The chart and the organization controller create the shared Gateway and keep it in sync with your organizations:
enableModernQuiverRouting: true
istio:
enabled: true
gateway:
enabled: true
selector:
istio: ingressgatewayistio.gateway.selector must match the labels of your Istio ingress gateway pods.
Option 2: Existing Gateway
If you manage the Gateway yourself, for example from Terraform or from the same GitOps repository that holds your Istio configuration, reference it instead:
enableModernQuiverRouting: true
istio:
enabled: true
gateway:
enabled: true
existingGateway: "istio-ingress/istio-public-gateway"Use gateway-name for a Gateway in the release namespace, or namespace/gateway-name for a Gateway elsewhere.
Organization TLS is ignored
When istio.gateway.existingGateway is set, the chart and the organization controller do not create or manage any Gateway, and Organization.spec.tls is ignored. TLS for every organization hostname comes from the credential referenced by your Gateway, so that certificate must cover all of them.
Effects of enabling Istio, in both options:
- A shared
VirtualServicedefining all GoodData API routes is installed by the Helm chart. - The organization controller creates one
VirtualServiceper organization, each delegating routing to the sharedVirtualService. - No Kubernetes Ingress resources are created, and
ingress.ingressClassNamehas no effect.
Shared Gateway Model
A single Istio Gateway handles TLS termination and accepts traffic for all organization hostnames.
Each organization receives a VirtualService that:
- Matches its hostname.
- References the shared Gateway.
- Delegates routing to the shared platform VirtualService (
gooddata-cn) containing the API paths and backend services.
This model is required for wildcard certificate support and consistent routing behavior.
Reference Implementation
The Terraform evaluation module deploys this setup end to end and is a useful reference for a production configuration. It:
- Installs the
base,istiod, andgatewaycharts at Istio 1.30.2 fromhttps://istio-release.storage.googleapis.com/charts, withistiodinistio-systemand the gateway inistio-ingress. - Enables native sidecars (
global.proxy.nativeSidecarplusENABLE_NATIVE_SIDECARS) so the Envoy proxy starts before init containers. - Creates the Gateway
istio-public-gatewayinistio-ingress, serving HTTPS on 443 withtls.mode: SIMPLEandcredentialName: gdcn-istio-gateway-tls, plus an HTTP server on port 80 that redirects to HTTPS. Both servers list the DexauthHostand every organization hostname. - Points the chart at that Gateway with
istio.gateway.existingGateway: "istio-ingress/istio-public-gateway". - Issues the Gateway certificate with cert-manager. Because HTTP-01 solving goes through Istio, it creates an
IngressClassnamedistiowith controlleristio.io/ingress-controllerand setsPILOT_ENABLE_K8S_INGRESS=trueonistiod. - Labels the
gooddata-cn,pulsar, andobservabilitynamespaces withistio-injection=enabledand applies aPeerAuthenticationnameddefaultwithmtls.mode: STRICTin each. - Raises the sidecar resource requests for the Pulsar ZooKeeper, BookKeeper, and broker pods (
sidecar.istio.io/proxyCPU: 100m,proxyCPULimit: 1,proxyMemory: 256Mi,proxyMemoryLimit: 1536Mi). Pulsar sidecars are memory-hungry; the Istio defaults are not enough.
If your Istio control plane runs on a private network, note that the module also opens TCP 15012 (istiod XDS) and TCP 15017 (istiod webhook) from the worker nodes to the control plane.
Switching an Existing Deployment To or From Istio
Sidecars are injected at pod creation time, so changing the ingress mode on a running deployment requires a restart of every workload in the injected namespaces:
kubectl -n <namespace> rollout restart deployment
kubectl -n <namespace> rollout restart statefulsetRepeat for each injected namespace (gooddata-cn, pulsar, observability).
Also verify before and after the switch:
- External DNS records point to the Istio ingress gateway.
- The TLS secret exists in the ingress gateway’s namespace and is referenced by the shared Gateway.
- Dex is reachable on its separate hostname and certificate.
TLS and Wildcard Certificate Limitations
These limitations are inherited from Envoy (Istio’s underlying proxy):
- No overlapping wildcard and specific certificates for the same domain hierarchy:
- For example, you cannot use both
*.something.comand a separate certificate forxyz.something.comon the same Gateway at the same time.
- For example, you cannot use both
- Dex endpoint and wildcard certificates:
- If you use Istio Gateway mode, Dex IdP, and a wildcard certificate, do not host Dex on a domain covered by that wildcard certificate.
- Dex must use its own certificate on a different hostname that does not conflict with the wildcard certificate used on the shared Gateway.
- Wildcard certificate reuse across multiple Gateways:
- The same wildcard certificate cannot be reused across multiple different
Gatewayservers. - In practice, do not share the same wildcard certificate between “managed” and “unmanaged” endpoints or across multiple separate GoodData.CN deployments. Use a single shared Gateway per deployment or distinct certificates.
- The same wildcard certificate cannot be reused across multiple different
Dex on a separate hostname
When running with Istio Gateway and wildcard TLS, deploy Dex on a separate hostname with its own certificate, outside the wildcard’s scope, to avoid certificate conflicts and failed handshakes.
Examples of Created Resources
The shared Gateway, in the shape the Terraform module creates it:
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: istio-public-gateway
namespace: istio-ingress
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: gdcn-istio-gateway-tls # secret in the gateway's namespace
hosts:
- "auth.example.company"
- "*.example.company"
- port:
number: 80
name: http
protocol: HTTP
tls:
httpsRedirect: true
hosts:
- "auth.example.company"
- "*.example.company"One VirtualService per organization, delegating to the shared routing definition:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: org-foo-routing
namespace: gooddata-cn
spec:
hosts:
- foo.example.company
gateways:
- istio-ingress/istio-public-gateway
http:
- delegate:
name: gooddata-cn
namespace: gooddata-cnNotes:
- Names and exact shapes are illustrative; the organization controller manages the per-organization resources and they may differ across versions.
credentialNamemust reference a Kubernetes secret in the namespace where the Istio ingress gateway pods run, containing a certificate valid for the served hostnames (wildcard or SANs as appropriate).
Known Limitations
- Certificate overlap conflicts cannot be worked around at the Envoy level. Plan your hostname strategy to avoid overlaps between wildcard and single-host certificates.
- Do not reuse the same wildcard certificate across multiple Gateways. Prefer a single shared Gateway per deployment or use distinct certificates per Gateway.
- With
istio.gateway.existingGatewayset, per-organization TLS throughOrganization.spec.tlsis unavailable.
Troubleshooting
- TLS handshake errors on specific hostnames:
- Check for certificate overlap (wildcard vs specific cert) and remove the conflict.
- Confirm the
credentialNamesecret exists in the gateway pods’ namespace and has the correct certificate chain and private key.
- Dex login fails or loops:
- Ensure Dex is not served through the shared wildcard certificate when Istio Gateway mode is used.
- Verify that the Dex hostname and certificate are independent of the shared Gateway’s wildcard domain.
- 404 or 503 on an organization hostname:
- Confirm a
VirtualServiceexists for the hostname and references the shared Gateway. - Confirm the hostname is listed in the Gateway’s
servers[].hosts.
- Confirm a
- Pods start but never become ready after enabling Istio:
- Confirm the namespace carries
istio-injection=enabledand that the workloads were restarted after the label was added. - Check the sidecar memory limits on Pulsar pods.
- Confirm the namespace carries