MastertheMesh
Solo Enterprise for Istio · Ambient multicluster · Reference
Reference

Ambient segments: one mesh, hard internal boundaries

TO
Tom O'Rourke
EMEA Field CTO · Solo.io

When one ambient mesh spans clusters that must not see each other, segments give you the boundary. They stop same-named services in different environments from bleeding into one logical service, give each boundary its own DNS domain you choose, and let you decide exactly what is allowed to cross. Here is the why, the how, and a worked compliance example.

Ambient multicluster Segment CRD service-scope AuthorizationPolicy Enterprise

In a multicluster ambient mesh, services with the same name in the same namespace on different clusters are treated as one logical service and their endpoints are pooled. That behaviour, namespace sameness, is exactly what you want for high availability: a catalog in two clusters becomes one catalog with endpoints in both.

It is exactly what you do not want the moment two of those clusters are meant to be separate. Put a regulated workload and a general corporate workload in the same mesh and namespace sameness will happily merge a service that handles card data with one that does not. Segments are how you keep one mesh but draw a hard line through it.

Edition and status. Segments need the Solo distribution of Istio with an Enterprise licence and a multicluster ambient mesh. There is no equivalent in community Istio. The feature is alpha (introduced in 1.28, hostname aliases in 1.29) and not yet supported for production, so treat it as a design you are validating, not one you are shipping under an SLA.

The problem in one line

You want one mesh to operate (one control plane, one trust domain, one telemetry pipeline, one upgrade path) but several isolated worlds inside it, where the same namespace and service names can be reused safely and nothing crosses unless you say so. Namespace sameness alone gives you the single mesh but not the isolation. Segments add the isolation.

A real boundary: the cardholder data environment

PCI DSS requires you to segment the cardholder data environment (the CDE, everything that stores, processes or transmits card data) from the rest of the estate, so that out-of-scope systems cannot reach in. That is a boundary an auditor will ask you to prove. Here is that boundary as two segments in one mesh.

CORPORATE SEGMENT out of PCI scope domain: corp.bank CARDHOLDER DATA ENVIRONMENT PCI segment · in scope domain: cde.bank segment boundary frontend checkout corp cluster payment-api scope=global · the gate card-vault scope=segment · isolated allowed ✓ global + AuthorizationPolicy allowed inside CDE card-vault is not even resolvable scope=segment: never published into corp
corporate segment (out of scope) CDE segment (in scope) allowed blocked at the boundary

One mesh, two segments. card-vault is scope=segment, so it is only published inside the CDE, the corporate side never receives its hostname and cannot route to it, which is what an auditor wants to see. payment-api is scope=global, the one sanctioned crossing point, and an AuthorizationPolicy on it allows only the checkout identity from the corp cluster. Reuse of names, namespaces and the same control plane on both sides, with a boundary you can prove.

What a segment actually is

A segment groups clusters, not workloads inside a cluster. It is two pieces that people often conflate:

So the number of Segment resources is the number of segments in the mesh (two, here), applied everywhere; the label is each cluster's membership card that picks one entry from the map. A corporate cluster still carries the CDE segment's definition, that is how it knows the CDE exists and what its domain is, it just is not a member of it.

# Both Segment resources exist on EVERY peered cluster (istio-system)
apiVersion: admin.solo.io/v1alpha1
kind: Segment
metadata:
  name: corp
  namespace: istio-system
spec:
  domain: corp.bank
---
apiVersion: admin.solo.io/v1alpha1
kind: Segment
metadata:
  name: cde
  namespace: istio-system
spec:
  domain: cde.bank

# Then each cluster joins ONE segment by labelling its istio-system namespace:
#   kubectl --context corp-cluster label ns istio-system admin.solo.io/segment=corp
#   kubectl --context cde-cluster  label ns istio-system admin.solo.io/segment=cde

Name your own domains

The domain suffix is yours. Whatever you put in spec.domain becomes the suffix for every service published in that segment, so a service resolves at <service>.<namespace>.<domain>, for example card-vault.payments.cde.bank. From 1.29 you can also add alias patterns (RFC 6570 URI templates) for shorter or label-driven names:

apiVersion: admin.solo.io/v1alpha1
kind: Segment
metadata:
  name: cde
  namespace: istio-system
spec:
  domain: cde.bank
  aliases:
    - pattern: "{service}.cde.bank"                      # card-vault.cde.bank
    - pattern: "{service}.{labels['tier']}.cde.bank"     # card-vault.data.cde.bank
These are mesh-internal names, not public DNS. The segment domain is resolved by the ambient dataplane (ztunnel, via auto-generated ServiceEntry resources), only for workloads inside the mesh. So you can pick cde.bank, tom.com, or anything you like: you do not need to own it, it will not clash with the real public domain, and it only resolves for callers in the mesh. Check what actually got published on a given cluster with kubectl get serviceentry -n istio-system.

Deciding what crosses: visibility is not access

There are two separate controls, and mixing them up is the usual mistake.

ControlWhat it decidesSet whereEnforced by
solo.io/service-scope Visibility. segment = only publish inside my own segment. global = publish to every segment. This decides whether a caller can even resolve and route to the service. label on the Service service discovery: a ServiceEntry is generated only where the service is visible; elsewhere the name simply does not exist
AuthorizationPolicy Access. Which specific identities may connect to what is visible. This is the security control. policy targeting the Service (or its waypoint) ztunnel at L4 (peer identity), or the waypoint at L7 (methods, paths, JWT claims)
Scope is a discovery boundary, not a firewall. The Solo docs are explicit: do not treat service-scope as a security feature. It controls what is reachable by keeping a service out of other segments' service discovery, which is a strong boundary, but if you need to enforce who may connect, that is AuthorizationPolicy.

So the CDE boundary is built from both. card-vault is scope=segment, so it is never published outside the CDE, the corporate side has no route to it at all. The one service that must cross, payment-api, is scope=global, and then an AuthorizationPolicy pins down exactly who may call it:

# card-vault stays inside the CDE: never leaves the segment
kubectl label svc card-vault -n payments solo.io/service-scope=segment

# payment-api is the one sanctioned crossing point: visible mesh-wide
kubectl label svc payment-api -n payments solo.io/service-scope=global

...but visible is not open. Only checkout, from the corp cluster, may call it:

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: payment-api-allow-checkout
  namespace: payments
spec:
  targetRefs:
    - kind: Service
      group: ""
      name: payment-api
  action: ALLOW
  rules:
    - from:
        - source:
            principals:
              - "corp.bank/ns/shop/sa/checkout"   # exactly this identity
      to:
        - operation:
            methods: ["POST"]
            paths: ["/charge", "/tokenize"]

Targeting a specific cluster

Because each cluster can carry its own trust domain in the Solo distribution, the SPIFFE principal on a request encodes which cluster the caller is in, not just its service account. That is what lets you target access per cluster: an allow rule for corp.bank/ns/shop/sa/checkout grants the checkout workload in the corporate cluster and nothing else, a workload with the same service account in another cluster presents a different principal and is denied. Put the policy on the waypoint if you need L7 conditions (method, path, JWT claim); leave it at ztunnel for pure L4 identity allow or deny.

When segments are the wrong tool

Segments earn their place when you want isolation and a few shared services and one team operating the whole thing. If two estates share nothing, are run by different teams with different SLAs, and never need a service to cross, then separate meshes are simpler and you are not giving anything up. Reach for segments when the alternative, standing up and federating several independent meshes just to share a handful of services, costs more than drawing a line through one.

Related. Segments sit on top of a working ambient multicluster mesh. If you are still getting there, the sidecar to ambient migration walks the move one namespace at a time, and the Ambient CRDs visual map shows where scope, waypoints and AuthorizationPolicy actually land.