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.
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 labelOpt-in & fail-safe
The mesh is an enhancement, never a hard dependency:
- No
ServiceMeshCR → 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
PreconditionNotMetwarning recommendingdataPlane: sidecarrather 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.enrollNamespaceson theServiceMesh.- The namespace label directly.
spec.mesh.modeon aStackor a workload (Gateway/Observability/ChatUI) — defaults toauto(join when a ReadyServiceMeshexists, no-op otherwise);enabledrequires one;disablednever 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):
isolation | Effect |
|---|---|
off | mTLS only — no authorization. |
namespace (default) | Deny cross-namespace; allow intra-namespace + the explicitly-wired cross-namespace edges. Isolates Stacks/tenants. |
strict | Per-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-pausedannotation 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 areRefused. - 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.