---
id: SEC02
pillar: security
title: SEC 2. How do you segment the environment to contain a compromise?
description: Isolation comes in strengths, from a separate organization down to a filter in your own code. Which one a data set needs is a decision, not a default.
status: draft
services: [kubernetes-engine, kms]
sidebar:
  order: 11
  label: Segmentation
source_url: "https://framework.stackit.cloud/architecture/pillars/security/sec-02-segmentation/"
source_file: "docs/architecture/pillars/security/sec-02-segmentation.mdx"
---

Assume breach means assuming that at some point something inside your boundary is under someone
else's control. Segmentation decides whether that is an incident or a catastrophe.

The mistake this question guards against is not choosing a weak boundary. It is choosing one by
default, without asking what it would take to defeat and whether that matches what the data
requires. Both directions are expensive: a workload split across projects it did not need carries
permanent friction, and one relying on an application-level check where it needed a platform
boundary has a control an attacker never has to respect.

## Best practices

- [`SEC 2.1`](/architecture/pillars/security/sec-02-segmentation/#sec-21-choose-the-isolation-strength-from-the-protection-need-not-from-a-default) Choose the isolation strength from the protection need, not from a default
- [`SEC 2.2`](/architecture/pillars/security/sec-02-segmentation/#sec-22-structure-the-resource-hierarchy-so-it-can-carry-the-boundaries-you-need) Structure the resource hierarchy so it can carry the boundaries you need
- [`SEC 2.3`](/architecture/pillars/security/sec-02-segmentation/#sec-23-separate-environments-and-be-explicit-about-what-the-separation-rests-on) Separate environments, and be explicit about what the separation rests on
- [`SEC 2.4`](/architecture/pillars/security/sec-02-segmentation/#sec-24-grant-at-the-narrowest-scope-because-breadth-cannot-be-narrowed-further-down) Grant at the narrowest scope, because breadth cannot be narrowed further down
- [`SEC 2.5`](/architecture/pillars/security/sec-02-segmentation/#sec-25-segment-the-network-as-well-as-the-identity-boundary) Segment the network as well as the identity boundary

---

## SEC 2.1 Choose the isolation strength from the protection need, not from a default

**Risk if not established:** High

Isolation is a spectrum, not a switch. Each level is cheaper and more convenient than the one
above it, and each moves the enforcement closer to your own code.

| Boundary | Enforced by | What it takes to cross |
|---|---|---|
| Separate organization | Platform identity and billing, entirely separate | A second, unrelated set of credentials |
| Separate project | Platform access control, quotas, billing | A credential valid in the other project |
| Separate network | The network layer, independent of the hierarchy | A route, or a foothold on something with one |
| Separate service instance in one project | The service, plus whatever access control it has | Frequently just the project-scoped role |
| Separation inside one instance | That service's own access control: namespaces, database users, key rings, bucket policies | Its access model, not the platform's |
| Application-level, a tenant column or a filter | Your code | A bug in your code |

The useful question for each data set is not "which of these is best" but **what would an attacker
have to defeat**, and whether that is proportionate to what the data is worth. A separate project
means obtaining a different credential. A tenant column means finding one query that forgot its
filter.

Drive the answer from the classification in [`SEC 3`](/architecture/pillars/security/sec-03-data-classification/) and, where regulation is involved, the
sovereignty tier in [`SOV 1`](/architecture/pillars/sovereignty/sov-01-sovereignty-tier/). A Tier 1 data set and an internal reporting cache legitimately land
at different levels of this table, and applying the top of it to both is how segmentation gets a
reputation for being expensive.

Combine rather than choose. Most real designs use several levels at once: separate projects for
environments, one project holding several service instances, and namespace separation inside a
cluster. The levels are cumulative, and naming which one carries the weight for a given data set
is what makes the design reviewable.

**On STACKIT.** All the levels in the table exist here, and several services provide their own
access boundary inside a project rather than requiring one project per data set.

<LinkChip href="https://docs.stackit.cloud/products/security/kms/how-tos/create-and-manage-key-rings/">KMS key
rings</LinkChip> are
a logical group of keys **and** an access control boundary, so a project can hold key material for
several data sets with different access. <LinkChip href="https://docs.stackit.cloud/products/security/secrets-manager/getting-started/create-a-service-in-secrets-manager/">Secrets
Manager</LinkChip>
is instantiated as a service with its own access, so the same applies to secrets. In <LinkChip href="https://docs.stackit.cloud/products/runtime/kubernetes-engine/">Kubernetes
Engine</LinkChip>, namespaces with
Kubernetes RBAC, network policy and resource quotas are a well-established boundary inside one
cluster. Managed databases have their own user and privilege model, documented per service such as
<LinkChip href="https://docs.stackit.cloud/products/databases/postgresql-flex/how-tos/create-and-manage-users-for-postgresql-flex/">users in PostgreSQL
Flex</LinkChip>.

Kubernetes Engine goes further than logical grouping, and the difference matters before you
conclude that in-project separation is always the weaker option. Two mechanisms give a namespace
boundary that rests on platform identity rather than only on cluster configuration.

<LinkChip href="https://docs.stackit.cloud/products/runtime/kubernetes-engine/getting-started/access-cluster/">Cluster
access</LinkChip>
supports a STACKIT IdP kubeconfig, where a user authenticates through federated identity and
receives only the permissions granted to their email address through Kubernetes RBAC. The identity
is issued by the platform; the authorization is expressed per namespace in the cluster.

<LinkChip href="https://docs.stackit.cloud/products/runtime/kubernetes-engine/how-tos/workload-identity/">Workload
identity</LinkChip>
does the same for workloads, through OIDC federation between the cluster and the STACKIT IdP. The
assertion that STACKIT validates includes the Kubernetes namespace, which means **the namespace is
part of the identity rather than a label on it**. A pod in one namespace cannot obtain the STACKIT
service account of another.

There is a condition on the first of those, and it decides how strong the boundary actually is.
Project roles do not map to in-cluster permissions, but they do decide who can bypass them:
`owner`, `editor`, `ske.admin` and `ske.editor` on the project all yield an administrative
kubeconfig with full cluster rights, while `reader` and `ske.reader` can only download the IdP
kubeconfig. Namespace separation inside a cluster is therefore a real boundary for everyone
holding a reader role and no boundary at all for everyone above it. Which people are in which
group is [`SEC 2.4`](/architecture/pillars/security/sec-02-segmentation/#sec-24-grant-at-the-narrowest-scope-because-breadth-cannot-be-narrowed-further-down) and [`SEC 5.1`](/architecture/pillars/security/sec-05-least-privilege/#sec-51-grant-the-narrowest-role-that-permits-the-work), and it is the question to answer before relying on this level.

The general principle still holds. A project boundary is enforced by platform access control
regardless of the service; a database user or a bucket policy is enforced by that service, which
protects against a compromised application and not against an identity already holding broad
project rights. What SKE shows is that the gap between those two is narrower than a simple reading
of the table suggests, and that it is worth checking per service rather than assuming.

How far below the project a binding reaches is worth establishing per service before a design
leans on it. Custom roles are created and assigned at project level while the API refers to
`{resourceId}` generically, so the project is the granularity to design against unless
you have confirmed something narrower for the service in question. For a service with no
equivalent of SKE's IdP integration, that is the assumption to plan on.

**Tradeoffs.** **Cost Optimization.** Every step up the table costs consolidation: more projects,
less resource sharing, more to manage. **Operational Excellence.** Stronger boundaries mean
cross-boundary access has to be arranged deliberately, which is friction by design and is felt
daily.

**Verify.** For your most sensitive data set, which boundary separates it from the least sensitive
one, and who enforces that boundary? What would an attacker with a foothold in the less sensitive
workload have to defeat?

---

## SEC 2.2 Structure the resource hierarchy so it can carry the boundaries you need

**Risk if not established:** High

Where you do use the hierarchy as a boundary, its shape decides what is possible. A hierarchy that
grew organically gives you neither clean access separation nor clean attribution, and correcting
it later means moving resources.

Two axes shape it. **Ownership**, so that access can be granted to a team without granting it to
everything, which is what [`OPS 1.3`](/architecture/pillars/operational-excellence/ops-01-shared-ownership/#ops-13-define-ownership-so-that-every-component-has-a-name-against-it) also needs. And **sensitivity**, so that workloads carrying
regulated data can be governed differently, which is what [`SEC 3`](/architecture/pillars/security/sec-03-data-classification/) and [`SOV 1`](/architecture/pillars/sovereignty/sov-01-sovereignty-tier/) need.

Where the two conflict, sensitivity usually wins, though not always. A team owning both a
regulated and an unregulated workload may be better served by two projects. It may equally be well
served by one project with the regulated data in its own service instance, its own key ring and
its own network segment, if [`SEC 2.1`](/architecture/pillars/security/sec-02-segmentation/#sec-21-choose-the-isolation-strength-from-the-protection-need-not-from-a-default) concluded that boundary is sufficient. The point is that the
decision is made rather than inherited.

Decide it early. This is the part that genuinely is expensive to retrofit, because it is the one
that moves resources rather than changing a setting.

**On STACKIT.** The <LinkChip href="https://docs.stackit.cloud/platform/resource-manager/">Resource Manager</LinkChip>
provides
organization, folders and projects. The project is what resources belong to and what access,
quotas and billing attach to, which is why it is the boundary with the most properties at once.

Folders are the grouping layer, and their main use is applying a grant to a set of projects rather
than repeatedly. That convenience is also the risk in [`SEC 2.4`](/architecture/pillars/security/sec-02-segmentation/#sec-24-grant-at-the-narrowest-scope-because-breadth-cannot-be-narrowed-further-down), since a grant at folder level
reaches every project beneath it.

This is the same structure [`COST 2`](/architecture/pillars/cost-optimization/cost-02-cost-attribution/) needs for attribution and [`OPS 1.3`](/architecture/pillars/operational-excellence/ops-01-shared-ownership/#ops-13-define-ownership-so-that-every-component-has-a-name-against-it) needs for ownership, which
is the strongest argument for spending time on it: one decision serves three pillars, and none of
them can be satisfied well by a hierarchy that was never designed.

**Tradeoffs.** **Cost Optimization.** More projects means less resource sharing and more
management overhead, which is the direct cost of using this level of the table in [`SEC 2.1`](/architecture/pillars/security/sec-02-segmentation/#sec-21-choose-the-isolation-strength-from-the-protection-need-not-from-a-default).

**Verify.** Draw your project structure. Can you grant a team access to what it owns without
granting access to anything else? Where the answer is no, is that a deliberate acceptance?

---

## SEC 2.3 Separate environments, and be explicit about what the separation rests on

**Risk if not established:** High

Non-production environments have weaker controls, broader access and less attention, reasonably
so. That makes them the easier target and the natural route into production if a path exists.

The paths that exist are rarely the obvious ones. Shared credentials so that a test job can read
production data. A network route left open after a migration. A pipeline with permissions in both.
A shared database instance with separate schemas. Each was created for a good reason and each
removes the separation.

The test is direct: from a compromised non-production workload, what in production is reachable?
If the answer is anything, the environments are not separated regardless of how they are drawn.

Copying production data into non-production reintroduces the exposure even with perfect
separation, because the data is what the attacker wanted. [`OPS 6.4`](/architecture/pillars/operational-excellence/ops-06-environment-consistency/#ops-64-test-with-production-like-data-without-using-production-data) reaches the same conclusion
from the consistency side.

**On STACKIT.** Separate projects per environment is the usual arrangement, because access, quotas
and billing then follow the environment and the boundary is enforced by platform access control.
Combined with separate networks under [`SEC 2.5`](/architecture/pillars/security/sec-02-segmentation/#sec-25-segment-the-network-as-well-as-the-identity-boundary), that gives an identity boundary and a network
boundary at once.

It is not the only workable arrangement. Where the protection need is lower, environments can
share a project and be separated by network, by distinct service instances and by each service's
own access model, per [`SEC 2.1`](/architecture/pillars/security/sec-02-segmentation/#sec-21-choose-the-isolation-strength-from-the-protection-need-not-from-a-default). What that arrangement gives up is precisely the platform-enforced
part: a project-scoped role reaches both environments, so the separation then rests on the network
and on each service rather than on access control. Naming that explicitly is the difference
between a considered decision and an assumed boundary.

Whichever you choose, watch the grants that cross it. A role assigned at organization or folder
level reaches every project beneath, including both environments, which is the pattern [`SEC 2.4`](/architecture/pillars/security/sec-02-segmentation/#sec-24-grant-at-the-narrowest-scope-because-breadth-cannot-be-narrowed-further-down)
warns about. Pipeline identities are the common case: one service account with access to both is a
single credential whose compromise crosses whatever boundary you built.

**Tradeoffs.** **Cost Optimization.** Duplicating infrastructure per environment costs more than
sharing it, and the shared instance with separate schemas exists because someone did that
arithmetic and was not necessarily wrong. **Operational Excellence.** Separate environments need
separate everything, including separate secrets and separate monitoring.

**Verify.** From a workload running in your test environment, what can you reach in production?
List the credentials, network paths and shared components, and say which mechanism stops each.

---

## SEC 2.4 Grant at the narrowest scope, because breadth cannot be narrowed further down

**Risk if not established:** High

Access models built on a hierarchy reach downward, and the property that matters is the asymmetry:
there is no documented mechanism to restrict at a child level what was granted above it. An overly
permissive binding has to be removed where it was made.

That asymmetry is what should shape how you grant. Breadth is cheap to create and expensive to
correct, so the scope of a grant is a decision made once and unwound only at its origin.

The rules that follow:

- **Default to the project.** Grant at organization or folder level only when the role genuinely
  needs to apply everywhere beneath.
- **Audit upward, not downward.** What an identity can reach in a project is the union of every
  binding on every ancestor, not just the bindings on the project itself.
- **Treat organization-level grants as exceptional.** Each is a standing decision affecting
  everything you will ever create, including projects that do not exist yet.

The last point is the one that bites later. A convenient organization-level grant made when there
were three projects still applies when there are ninety, including the regulated ones.

**On STACKIT.** Bindings are made at organization, folder or project scope, and each level is a
subset of its parent.

How far a given role reaches downward is worth checking per role rather than assuming. The <LinkChip href="https://docs.stackit.cloud/platform/access-and-identity/roles-permissions/roles-permissions/">roles
and
permissions</LinkChip>
page describes permissions granted on organizations and folders as inherited along the hierarchy,
while <LinkChip href="https://docs.stackit.cloud/platform/resource-manager/basics/access-control/">access control in the Resource
Manager</LinkChip> notes that
not all roles behave that way, giving `organization.viewer` as one that grants no access to child
objects.

Where the two read differently, design against the broader one: assume a binding on an ancestor
reaches the project unless you have confirmed that the role in question does not. An entitlement
review under [`SEC 5.4`](/architecture/pillars/security/sec-05-least-privilege/#sec-54-review-actual-entitlements-against-intended-ones-on-a-cadence) is where the exact behaviour of the roles you actually use has to be
established, and it is a shorter list than every role that exists.

So grant narrowly, and when auditing what an identity can reach, look at every binding on every
ancestor rather than only at the project.

<LinkChip href="https://docs.stackit.cloud/platform/access-and-identity/roles-permissions/assign-roles-to-account/">Assigning
roles</LinkChip>
covers the mechanics; role selection itself is [`SEC 5.1`](/architecture/pillars/security/sec-05-least-privilege/#sec-51-grant-the-narrowest-role-that-permits-the-work).

**Tradeoffs.** **Operational Excellence.** Narrow grants mean more grants to manage and more
requests when someone needs access to something new. That friction is the containment working, and
[`SEC 5.2`](/architecture/pillars/security/sec-05-least-privilege/#sec-52-prefer-elevation-that-expires-over-standing-privilege) is how it avoids becoming a bottleneck.

**Verify.** List every role binding at organization and folder level. For each, does it genuinely
need to apply to every project beneath, including the ones created next year?

---

## SEC 2.5 Segment the network as well as the identity boundary

**Risk if not established:** High

The resource hierarchy segments identity. It does not segment packets. A compromised workload that
can reach another over the network has a path regardless of whether it holds any credentials for
it.

This is why the network row sits separately in the table in [`SEC 2.1`](/architecture/pillars/security/sec-02-segmentation/#sec-21-choose-the-isolation-strength-from-the-protection-need-not-from-a-default): it is orthogonal to the
hierarchy, so it can strengthen a weak identity boundary or be absent behind a strong one. Two
workloads in the same project on separate networks are network-isolated and not identity-isolated.
Two in different projects on the same network are the reverse.

Segment along the boundaries the classification requires: between environments, between workloads
of different sensitivity, and between tiers of a single workload where it warrants it. A web tier
that can reach the database directly has removed one of the layers.

Inside a segment, do not assume trust. Internal service calls authenticate rather than trusting
the network they arrived on, which is [`SEC 6.4`](/architecture/pillars/security/sec-06-network-controls/#sec-64-verify-inside-the-segment-rather-than-trusting-it).

**On STACKIT.** <LinkChip href="https://docs.stackit.cloud/products/network/core-networking/">Core networking</LinkChip>
describes the model, and <LinkChip href="https://docs.stackit.cloud/products/network/core-networking/security-groups/">security
groups</LinkChip> are the
instance-level control for what may reach what. <LinkChip href="https://docs.stackit.cloud/products/network/network-security/">Network
security</LinkChip> covers the centralized
side, including the <LinkChip href="https://docs.stackit.cloud/products/network/network-security/unified-firewall/">Unified
Firewall</LinkChip> for
rules across an environment rather than per resource.

For Kubernetes, <LinkChip href="https://docs.stackit.cloud/products/runtime/kubernetes-engine/how-tos/enable-private-clusters/">private
clusters</LinkChip>
remove the public control plane endpoint, and in-cluster segmentation between workloads is network
policy, which you configure. Cluster-level and platform-level controls are complementary rather
than alternatives, and network policy is one of the in-project mechanisms [`SEC 2.1`](/architecture/pillars/security/sec-02-segmentation/#sec-21-choose-the-isolation-strength-from-the-protection-need-not-from-a-default) lists.

**Tradeoffs.** **Reliability.** Every network control can fail or be misconfigured, and a wrong
rule is an outage. **Operational Excellence.** Segmented networks make debugging harder, since a
missing rule presents as an application timeout.

**Verify.** From a compromised instance in your application tier, which other systems are
reachable over the network? Is that set what you intended, or what the default produced?

---

## Related

- [`SEC 3`](/architecture/pillars/security/sec-03-data-classification/) Data classification, which supplies the protection need that [`SEC 2.1`](/architecture/pillars/security/sec-02-segmentation/#sec-21-choose-the-isolation-strength-from-the-protection-need-not-from-a-default) decides from
- [`SOV 1`](/architecture/pillars/sovereignty/sov-01-sovereignty-tier/) Sovereignty tier, the regulatory input to the same decision
- [`SEC 5`](/architecture/pillars/security/sec-05-least-privilege/) Least privilege, which the scope rule in [`SEC 2.4`](/architecture/pillars/security/sec-02-segmentation/#sec-24-grant-at-the-narrowest-scope-because-breadth-cannot-be-narrowed-further-down) shapes
- [`SEC 6`](/architecture/pillars/security/sec-06-network-controls/) Network controls, the detailed version of [`SEC 2.5`](/architecture/pillars/security/sec-02-segmentation/#sec-25-segment-the-network-as-well-as-the-identity-boundary)
- [`OPS 1.3`](/architecture/pillars/operational-excellence/ops-01-shared-ownership/#ops-13-define-ownership-so-that-every-component-has-a-name-against-it) Ownership and [`COST 2`](/architecture/pillars/cost-optimization/cost-02-cost-attribution/) Cost attribution, which share the hierarchy
