Microsegment the tiers: web to app to data, and no skipping

Microsegment the tiers: web → app → data, and no skipping

A three-tier app has exactly two legitimate internal hops: the web tier calls the app tier, and the app tier calls the data tier. Everything else - the web tier reaching straight into the database, the database calling back into the app - is either a bug or an attacker moving laterally. This lesson encodes that shape with one Calico NetworkPolicy in prod.

What you'll learn

The policy

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: prod-tier-microsegmentation
  namespace: prod
spec:
  selector: all()
  types:
    - Ingress
  ingress:
    - action: Allow
      protocol: TCP
      source:
        selector: tier == 'web'
      destination:
        selector: tier == 'app'
        ports: [8080]
    - action: Allow
      protocol: TCP
      source:
        selector: tier == 'app'
      destination:
        selector: tier == 'data'
        ports: [8080]

What to observe

Allowed

Denied

Untouched

{
  "question": "Why is `prod/frontend → prod/database` denied even though both rules use Allow?",
  "options": [
    "Because frontend is in the web tier and neither rule allows web→data, so it hits the default-deny",
    "Because Allow rules only work on backend pods",
    "Because the database is in a different namespace"
  ],
  "answer": 0,
  "explain": "Rule 1 is web→app and rule 2 is app→data. A web→data flow matches neither, so the implicit default-deny from selector all()+types:[Ingress] drops it. Tier-skipping is blocked by omission, not by an explicit Deny."
}

Recap

One namespaced policy, two pinned edges, and tier-skipping disappears - that's microsegmentation. But it only covered prod. To enforce a baseline across every namespace at once we need the cluster-wide Calico kind. Next: the GlobalNetworkPolicy and a cluster-wide default-deny.