Skip to content

ServiceMesh ​

Scope: cluster (singleton) · Licensed — currently in preview (usable with the preview or service-mesh feature)

Preview feature

The service mesh is shipped but not yet GA — it can cause more friction than value on some platforms (ambient/Cilium interplay, teardown edge cases). It is gated behind the preview opt-in so only a test cohort enables it. A license without service-mesh or preview leaves it refused (PreviewLocked) and the platform runs normally without mTLS.

ServiceMesh brings mTLS and tenant isolation to the platform using Istio, the operator way: it installs Istio when absent and adopts it when already present (notably on OpenShift Service Mesh 3), then configures the platform's component namespaces — no manual istioctl/helm steps.

It is driven through the Sail Operator (the GA successor to the removed in-cluster IstioOperator, and the basis of OpenShift Service Mesh 3), so the managed (vanilla Kubernetes) and adopt (OpenShift / existing Istio) paths converge on one API.

yaml
apiVersion: core.navique.com/v1alpha1
kind: ServiceMesh
metadata:
  name: cluster        # singleton
spec:
  mode: managed        # managed | adopt | external
  provider: auto       # auto | sail | openshift | istio
  dataPlane: ambient   # ambient | sidecar
  version: "1.30"
  mtls:
    mode: strict       # strict | permissive (escape hatch)
  isolation: namespace # off | namespace | strict
  enrollNamespaces: [] # also enrolled: any namespace with the mesh-enroll label

Opt-in & fail-safe ​

The mesh is an enhancement, never a hard dependency:

  • No ServiceMesh CR → nothing happens. Components run plaintext; the platform is unaffected. License-gated: unlicensed is the same no-op.
  • STRICT mTLS is applied only after the control plane is Ready — a failed or incompatible install can never break workload traffic. Worst case is "no mTLS," never "broken platform."
  • On Cilium + ambient (a known sharp edge) the operator raises a PreconditionNotMet warning recommending dataPlane: sidecar rather than silently failing.

How a namespace is enrolled ​

A namespace joins the mesh when it carries the core.navique.com/mesh=enabled label. Three ways to set it:

  • spec.enrollNamespaces on the ServiceMesh.
  • The namespace label directly.
  • spec.mesh.mode on a Stack or a workload (Gateway/Observability/ChatUI) — defaults to auto (join when a Ready ServiceMesh exists, no-op otherwise); enabled requires one; disabled never joins.

For each enrolled namespace the operator applies the data-plane label, a namespace-wide STRICT PeerAuthentication (via a brief permissive→strict cutover so already-running workloads are not dropped), and — unless isolation: off — an isolation AuthorizationPolicy.

mTLS vs isolation ​

mTLS authenticates + encrypts but does not authorize: any meshed workload can reach any other. spec.isolation adds an auto-generated AuthorizationPolicy derived from the wiring graph (the refs the operator already resolves):

isolationEffect
offmTLS only — no authorization.
namespace (default)Deny cross-namespace; allow intra-namespace + the explicitly-wired cross-namespace edges. Isolates Stacks/tenants.
strictPer-service least-privilege (L4; L7 method/path needs waypoints — not yet implemented).

In ambient this is enforced at L4 by ztunnel with no waypoints.

External datastores & egress ​

mode: external datastores, external LLM APIs, blob storage, and Key Vault live outside the mesh: their security is the connection's own TLS, not Istio. STRICT and isolation govern inbound only, so outbound egress to those endpoints is never blocked.

Debugging without fighting the operator (break-glass) ​

STRICT does not block kubectl exec, kubectl logs, ephemeral debug containers, or an in-mesh debug pod — use those. To relax mTLS deliberately, do not hand-edit the managed PeerAuthentication (the operator reverts it); instead:

  • spec.mtls.maintenanceUntil (RFC3339): the operator holds enrolled namespaces at PERMISSIVE until that time, then auto-restores STRICT. Auditable and self-healing.
  • the core.navique.com/reconcile-paused annotation on a managed object, for an ad-hoc pause.

Provenance & teardown ​

  • managed: install the Sail Operator (ref-counted) + own the Istio control plane. On delete (when nothing else needs it) uninstall it and clean its CRDs.
  • adopt: a mesh already exists (OpenShift / user Istio) → configure only, never manage its lifecycle. (managed mode also auto-adopts when a mesh is present.)
  • external: never touch the control plane; emit our config only.

Deleting the ServiceMesh strips the labels we added and GCs the policies we own (via the cluster-scoped owner reference); an adopted control plane is left intact.

Limits ​

  • Cluster singleton named cluster; other names are Refused.
  • OpenShift Service Mesh 2.x (Maistra) is detected and deferred with a clear status — adopt an existing mesh or upgrade to OSSM 3 / Sail.
  • L7 AuthorizationPolicy (method/path) + waypoints and a north-south Istio ingress gateway are not yet implemented.

See Editions & Licensing.

Open core under AGPL-3.0. Enterprise components are proprietary and license-gated.