Skip to content
Unverified — AI-generated content. Help verify this page

Network Segmentation

Why Network Segmentation Exists

Traditional flat networks allow any device to communicate with any other device. Once an attacker gains access to any machine, they can move laterally to reach high-value targets. Network segmentation breaks the network into isolated zones, limiting the blast radius of any compromise.

Micro-segmentation takes this to the extreme — each workload is its own segment, communicating only with explicitly authorized peers. Combined with identity-based access (not IP-based), this forms the network layer of Zero Trust.

From VLANs to Micro-segmentation

GenerationTechnologyGranularityIdentity Model
Gen 1VLANsNetwork-levelIP-based
Gen 2Firewall zonesSubnet-levelIP + port
Gen 3Software-definedWorkload-levelLabels/tags
Gen 4Service mesh (mTLS)Process-levelCryptographic identity

First Principles

The Blast Radius Problem

In a flat network with N services:

Attack surface from any compromised service=N1

With micro-segmentation where each service connects to an average of k other services:

Attack surface=kN

For a 100-service architecture with k=5:

  • Flat network: 99 potential targets per breach
  • Micro-segmented: 5 potential targets per breach
Blast radius reduction=N1k=995=19.8×

ZTNA vs VPN

FeatureVPNZTNA
Access modelNetwork-widePer-application
Identity checkAt VPN login onlyEvery request
Lateral movementUnrestrictedBlocked
Device complianceNot checkedContinuously verified
User experienceSlow, always-onTransparent, per-app
PerformanceAll traffic through VPNDirect connections
VisibilityIP logs onlyFull request audit

Core Mechanics

Kubernetes Network Policies

yaml
# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

---
# Allow web-frontend to talk to api-server on port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: web-frontend
      ports:
        - protocol: TCP
          port: 8080

---
# Allow api-server to talk to database on port 5432
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api-server
      ports:
        - protocol: TCP
          port: 5432

---
# Allow api-server egress to database and external APIs only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-server-egress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432
    - to:
        - podSelector:
            matchLabels:
              app: redis
      ports:
        - protocol: TCP
          port: 6379
    # DNS egress (required for service discovery)
    - to: []
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Service Mesh (Istio mTLS)

yaml
# Enforce strict mTLS across the mesh
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT

---
# Authorization policy: only allow specific services
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: payment-service-policy
  namespace: production
spec:
  selector:
    matchLabels:
      app: payment-service
  rules:
    - from:
        - source:
            principals:
              - "cluster.local/ns/production/sa/order-service"
              - "cluster.local/ns/production/sa/refund-service"
      to:
        - operation:
            methods: ["POST"]
            paths: ["/api/v1/charge", "/api/v1/refund"]
    - from:
        - source:
            principals:
              - "cluster.local/ns/production/sa/admin-dashboard"
      to:
        - operation:
            methods: ["GET"]
            paths: ["/api/v1/transactions*"]

Micro-segmentation Policy Generator

typescript
interface ServiceCommunication {
  from: string;
  to: string;
  port: number;
  protocol: 'TCP' | 'UDP';
  description: string;
}

interface NetworkPolicySpec {
  apiVersion: string;
  kind: string;
  metadata: { name: string; namespace: string };
  spec: any;
}

function generateNetworkPolicies(
  namespace: string,
  communications: ServiceCommunication[]
): NetworkPolicySpec[] {
  const policies: NetworkPolicySpec[] = [];

  // Default deny all
  policies.push({
    apiVersion: 'networking.k8s.io/v1',
    kind: 'NetworkPolicy',
    metadata: { name: 'default-deny-all', namespace },
    spec: {
      podSelector: {},
      policyTypes: ['Ingress', 'Egress'],
    },
  });

  // Allow DNS for all pods
  policies.push({
    apiVersion: 'networking.k8s.io/v1',
    kind: 'NetworkPolicy',
    metadata: { name: 'allow-dns', namespace },
    spec: {
      podSelector: {},
      policyTypes: ['Egress'],
      egress: [{
        ports: [
          { protocol: 'UDP', port: 53 },
          { protocol: 'TCP', port: 53 },
        ],
      }],
    },
  });

  // Group communications by destination
  const byDestination = new Map<string, ServiceCommunication[]>();
  for (const comm of communications) {
    const existing = byDestination.get(comm.to) ?? [];
    existing.push(comm);
    byDestination.set(comm.to, existing);
  }

  // Generate ingress policy per destination
  for (const [destination, comms] of byDestination) {
    policies.push({
      apiVersion: 'networking.k8s.io/v1',
      kind: 'NetworkPolicy',
      metadata: {
        name: `allow-ingress-to-${destination}`,
        namespace,
      },
      spec: {
        podSelector: { matchLabels: { app: destination } },
        policyTypes: ['Ingress'],
        ingress: comms.map(comm => ({
          from: [{ podSelector: { matchLabels: { app: comm.from } } }],
          ports: [{ protocol: comm.protocol, port: comm.port }],
        })),
      },
    });
  }

  // Group by source for egress policies
  const bySource = new Map<string, ServiceCommunication[]>();
  for (const comm of communications) {
    const existing = bySource.get(comm.from) ?? [];
    existing.push(comm);
    bySource.set(comm.from, existing);
  }

  for (const [source, comms] of bySource) {
    policies.push({
      apiVersion: 'networking.k8s.io/v1',
      kind: 'NetworkPolicy',
      metadata: { name: `allow-egress-from-${source}`, namespace },
      spec: {
        podSelector: { matchLabels: { app: source } },
        policyTypes: ['Egress'],
        egress: comms.map(comm => ({
          to: [{ podSelector: { matchLabels: { app: comm.to } } }],
          ports: [{ protocol: comm.protocol, port: comm.port }],
        })),
      },
    });
  }

  return policies;
}

// Usage
const communications: ServiceCommunication[] = [
  { from: 'web-frontend', to: 'api-gateway', port: 8080, protocol: 'TCP', description: 'Frontend to API' },
  { from: 'api-gateway', to: 'user-service', port: 3000, protocol: 'TCP', description: 'API to Users' },
  { from: 'api-gateway', to: 'order-service', port: 3001, protocol: 'TCP', description: 'API to Orders' },
  { from: 'order-service', to: 'payment-service', port: 3002, protocol: 'TCP', description: 'Orders to Payments' },
  { from: 'user-service', to: 'postgres', port: 5432, protocol: 'TCP', description: 'Users to DB' },
  { from: 'order-service', to: 'postgres', port: 5432, protocol: 'TCP', description: 'Orders to DB' },
];

const policies = generateNetworkPolicies('production', communications);

Edge Cases & Failure Modes

Network Policy Ordering and Conflicts

Kubernetes NetworkPolicies are additive — if any policy allows traffic, it's allowed:

Policy A: Allow frontend → api (port 8080)
Policy B: Allow monitoring → api (port 9090)

Result: api accepts traffic from frontend:8080 AND monitoring:9090

There is no explicit deny in K8s NetworkPolicy. The "deny" comes from having a policy that selects a pod — once selected, all unlisted traffic is denied.

DANGER

Forgetting to add DNS egress rules after applying a default-deny policy will break all service discovery. This is the most common micro-segmentation deployment issue.

Service Mesh Performance Overhead

ComponentLatency AddedMemory per sidecarCPU per sidecar
Istio sidecar1–5ms (p50), 10–20ms (p99)50–100 MB0.1–0.5 cores
Linkerd sidecar0.5–2ms (p50), 5–10ms (p99)20–50 MB0.05–0.2 cores
Cilium (eBPF)< 0.5ms0 (kernel)Minimal

For 100 services: Istio sidecars add ~5–10 GB memory and ~10–50 CPU cores of overhead.

Performance Characteristics

eBPF vs iptables for Network Policies

FeatureiptableseBPF (Cilium)
Rule evaluationO(n) linear scanO(1) hash lookup
Policy updateFull chain rebuildIncremental
Latency at 1K rules5–10ms< 0.1ms
Latency at 10K rules50–100ms< 0.1ms
ObservabilityLimitedDeep visibility

Mathematical Foundations

Network Reachability as Graph Theory

Model the network as a directed graph G=(V,E) where vertices are services and edges are allowed connections.

The blast radius from a compromised service v is the set of reachable vertices:

BlastRadius(v)={uV: path from v to u}

The goal of micro-segmentation is to minimize:

maxvV|BlastRadius(v)|

For a star topology (all services connect through an API gateway):

|BlastRadius(gateway)|=N1

but

|BlastRadius(servicei)|=1 (only the gateway)

Real-World War Stories

War Story

The Target Breach (2013)

Attackers gained access to Target's network through a compromised HVAC vendor. Once inside, the flat network allowed lateral movement from the HVAC system to the point-of-sale systems, resulting in 40 million stolen credit card numbers.

With micro-segmentation, the HVAC vendor's access would have been limited to HVAC control systems, with no path to POS systems.

Lesson: Third-party access must be segmented from critical systems. Network proximity is not a trust indicator.

War Story

Kubernetes Default-Deny Incident

A company applied default-deny NetworkPolicies to their production Kubernetes cluster without first mapping all inter-service communications. This immediately broke 30% of their services, causing a 2-hour outage.

Resolution: They implemented a "learning mode" approach: deploy Cilium in monitoring-only mode for 2 weeks, analyze all traffic flows, auto-generate policies based on observed traffic, review and apply policies in staging, then gradually apply to production.

Decision Framework

ZTNA Product Selection

FactorCloudflare AccessZscaler ZPATailscaleWireGuard
ArchitectureCloud proxyCloud proxyMesh P2PP2P tunnel
Device complianceOptionalBuilt-inNoNo
Per-app accessYesYesACLsManual
Self-hosted optionNoNoYes (Headscale)Yes
CostFree–$7/user/monthEnterpriseFree–$18/userFree

Advanced Topics

eBPF-Based Micro-segmentation with Cilium

yaml
# Cilium Network Policy with L7 visibility
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-server-l7-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: web-frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/api/v1/public/.*"
              - method: "POST"
                path: "/api/v1/orders"
                headers:
                  - 'Content-Type: application/json'

Service Communication Graph Discovery

typescript
// Automatically discover service communication patterns
// from network flow logs or Istio telemetry
interface NetworkFlow {
  sourceService: string;
  destinationService: string;
  destinationPort: number;
  protocol: string;
  requestCount: number;
  lastSeen: Date;
}

async function discoverCommunicationPatterns(
  flowLogs: NetworkFlow[],
  observationPeriod: number = 14 // days
): Promise<ServiceCommunication[]> {
  const cutoff = new Date(Date.now() - observationPeriod * 86400000);

  // Filter to recent, significant flows
  const significantFlows = flowLogs.filter(
    f => f.lastSeen > cutoff && f.requestCount > 10
  );

  // Deduplicate and aggregate
  const uniqueFlows = new Map<string, NetworkFlow>();
  for (const flow of significantFlows) {
    const key = `${flow.sourceService}->${flow.destinationService}:${flow.destinationPort}`;
    const existing = uniqueFlows.get(key);
    if (!existing || flow.requestCount > existing.requestCount) {
      uniqueFlows.set(key, flow);
    }
  }

  return Array.from(uniqueFlows.values()).map(f => ({
    from: f.sourceService,
    to: f.destinationService,
    port: f.destinationPort,
    protocol: f.protocol as 'TCP' | 'UDP',
    description: `Auto-discovered: ${f.requestCount} requests in ${observationPeriod} days`,
  }));
}

Cross-References

"What I cannot create, I do not understand." — Richard Feynman