---
title: 'Network Segmentation and the Demilitarized Zone'
source: 'https://academia.sh/en/courses/network-security/network-segmentation-and-dmz'
course: 'Network Security'
language: en
updated: '2026-09-26T03:02:54+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Counts the open path and the item reached from the outside zone separately for four layouts in the zone graph'
    - 'Measures how the demilitarized zone changes the item reached at the first hop and the number of boundaries to the data zone'
    - 'Compares the zone reached when one zone falls in the flat and segmented layouts: names the path that closes and the zone that does not'
---

# Network Segmentation and the Demilitarized Zone

Four layouts are run over an eight-zone graph: flat network, perimeter control alone, segmentation without a demilitarized zone, and full segmentation; each one counts the open path count, the item reached at the first hop and transitively from the outside zone, the zone reached when one zone falls, and the legitimate flow missed when rules were written.

The previous lesson counted what the box standing at the boundary could see, and assumed that
every unproven claim entered the network behind it. That assumption itself is this lesson's
subject: was "the network behind it" a single thing? All eight flow classes stood on the same
flat plane; a message reaching the meter collector could reach the billing interface with the
same move. Segmentation does not change the number of claims the box proves; it changes **the
number of paths along which a claim can be presented**.

Three things are measured. How many directed pairs are open in the zone graph, how many are
reached when one zone falls, and how many rule lines legitimate cross-zone traffic needs to keep
running. Address planning and subnet arithmetic at cloud-network scale were measured in another
course; the axis here is not the address, but the zone graph itself.

- **PD7.** The eight zones, item counts, and rule list belong to a fictional regional measurement
  network. No real traffic flows; what is measured is the graph model built with `node`.
- **PD8.** No constraint within a zone was assumed: whoever reaches an item in a zone reaches every
  item of that zone. In reality intra-zone controls can also be written; that lowers the item
  count, it does not change the direction of the between-zone measurement.
- **PD9.** The outside zone was counted as a destination, not a pass-through point. Leaving an
  inner zone to the outside does not mean reaching everywhere the outside zone can reach.
- **PD10.** Rule lines were counted at the level of the directed zone pair. In reality each line
  also states a port and a protocol; that grows the line count, it does not change the direction
  of the comparison.
- **PD11.** The phrase "a zone falls" names only the situation in which an item in that zone passes
  outside of control. How it does so was neither modeled nor written.
- **PD12.** The two legitimate flows missed when rules were written were placed into the model by
  hand. In reality this number depends on the observation period and on how rarely the flow runs.

## Zone Graph and Four Layouts

The model holds zones as nodes and the allowed directed pairs as edges. Four layouts do the same
work and differ along only two axes: whether there is a boundary between the inner zones, and
whether the public-facing items sit in a separate zone. When the public-facing items stand apart,
that zone is a demilitarized zone; when they do not, the same items sit inside the application
zone.

```js
// zone.mjs — zone graph, item counts and four layouts; a model, not a real network
const COMMON = { outside: 0, branch: 120, field: 60, data: 6, management: 5, backup: 3 };
export const ITEM = {                        // whether public-interface items sit in a separate zone or the same zone as the application
  separate: { ...COMMON, dmz: 4, application: 12 },
  inside: { ...COMMON, application: 16 },
};
// Legitimate flows with rules written: allowed directed zone pair under default-deny
export const LEGITIMATE = [
  ['outside', 'dmz'], ['dmz', 'application'], ['field', 'dmz'], ['branch', 'dmz'], ['branch', 'application'],
  ['application', 'data'], ['application', 'outside'], ['data', 'backup'], ['backup', 'outside'],
  ...['dmz', 'branch', 'field', 'application', 'data', 'backup'].map((b) => ['management', b]),
];
// Two legitimate flows missed when rules were written: quarter-end reconciliation and branch archive
export const MISSED = [['field', 'data'], ['branch', 'backup']];
const unique = (l) => [...new Set(l.map((e) => e.join('>')))].map((s) => s.split('>'));
const fold = (l) => unique(l.map(([a, b]) => [a === 'dmz' ? 'application' : a, b === 'dmz' ? 'application' : b]))
  .filter(([a, b]) => a !== b);
const flatEdge = (zl) => {                    // no constraint between inner zones, outbound direction free
  const inner = zl.filter((z) => z !== 'outside');
  return [...inner.flatMap((x) => inner.filter((y) => y !== x).map((y) => [x, y])), ...inner.map((z) => [z, 'outside'])];
};
const Z8 = ['outside', 'dmz', 'branch', 'field', 'application', 'data', 'backup', 'management'];
const Z7 = Z8.filter((z) => z !== 'dmz');
export const LAYOUT = {                       // [zones, edge, rules written, item map]
  flat: [Z7, [...flatEdge(Z7), ['outside', 'application']], 0, 'inside'],
  perimeter: [Z8, [...flatEdge(Z8), ['outside', 'dmz']], 1, 'separate'],
  nodmz: [Z7, fold(LEGITIMATE), fold(LEGITIMATE).length, 'inside'],
  segmented: [Z8, LEGITIMATE, LEGITIMATE.length, 'separate'],
};
export function reached(edge, start) {        // transitive access; outside is a destination, not a pass-through point
  const seen = new Set(), queue = [start];
  while (queue.length) {
    const z = queue.shift();
    if (z === 'outside' && start !== 'outside') continue;
    for (const [x, y] of edge) if (x === z && !seen.has(y)) { seen.add(y); queue.push(y); }
  }
  seen.delete(start);
  return seen;
}
export function distance(edge, start, target) { // how many zone boundaries are crossed to reach the target
  let layer = new Set([start]), n = 0; const seen = new Set([start]);
  while (layer.size) {
    n++;
    const next = new Set();
    for (const [x, y] of edge) if (layer.has(x) && !seen.has(y)) { seen.add(y); next.add(y); }
    if (next.has(target)) return n;
    layer = next;
  }
  return '-';
}
export const path = (zl, edge) => zl.reduce((a, z) => a + reached(edge, z).size, 0);
export const itemsOf = (set, h) => [...set].reduce((a, z) => a + (ITEM[h][z] ?? 0), 0);
export const firstHop = (edge, start) => new Set(edge.filter(([x]) => x === start).map(([, y]) => y));
export const direct = (edge, [a, b]) => edge.some(([x, y]) => x === a && y === b);
```

Two measurement functions walk over the graph. Transitive access starts from one zone and,
following edges, collects every zone reached; the distance function makes the same walk layer by
layer and gives how many boundaries are crossed to reach the target. The item counts are read
from two maps, because the four public-interface items sit either in their own zone or inside the
application zone, and the total of two hundred ten items stays the same either way. What changes
place is not the item count, but which boundary the item sits behind.

## The Path Each Layout Opens

```js
// run1.mjs — open path, item reached from the outside zone, and cut legitimate flow in four layouts
import { LAYOUT, LEGITIMATE, MISSED, reached, distance, path, itemsOf, firstHop, direct } from './zone.mjs';

console.log(`legitimate flow with rules written ${LEGITIMATE.length}, missed when rules were written ${MISSED.length}\n`);
console.log(`${'layout'.padEnd(11)} ${'zones'.padStart(5)} ${'rules'.padStart(5)} ${'open/all paths'.padStart(15)} ` +
  `${'first-hop item'.padStart(16)} ${'transitive item'.padStart(16)} ${'boundary to data'.padStart(17)} ${'legitimate cut'.padStart(15)}`);
for (const [name, [zl, edge, rules, h]] of Object.entries(LAYOUT)) {
  const total = zl.length * (zl.length - 1);
  console.log(`${name.padEnd(11)} ${String(zl.length).padStart(5)} ${String(rules).padStart(5)} ` +
    `${`${path(zl, edge)}/${total}`.padStart(15)} ${String(itemsOf(firstHop(edge, 'outside'), h)).padStart(16)} ` +
    `${String(itemsOf(reached(edge, 'outside'), h)).padStart(16)} ${String(distance(edge, 'outside', 'data')).padStart(17)} ` +
    `${String(MISSED.filter((m) => !direct(edge, m)).length).padStart(15)}`);
}
console.log('\nzones reached from the outside zone:');
for (const [name, [, edge]] of Object.entries(LAYOUT))
  console.log(`  ${name.padEnd(11)} ${[...reached(edge, 'outside')].join(', ')}`);
const cut = MISSED.filter((m) => !direct(LAYOUT.segmented[1], m));
console.log(`\nlegitimate flow cut in the segmented layout: ${cut.map(([a, b]) => `${a} -> ${b}`).join(', ')}`);
```

```
legitimate flow with rules written 15, missed when rules were written 2

layout      zones rules  open/all paths   first-hop item  transitive item  boundary to data  legitimate cut
flat            7     0           42/42               16              210                 2               0
perimeter       8     1           56/56                4              210                 2               0
nodmz           7    12           23/42               16               25                 2               2
segmented       8    15           31/56                4               25                 3               2

zones reached from the outside zone:
  flat        application, branch, field, data, backup, management
  perimeter   dmz, branch, field, application, data, backup, management
  nodmz       application, data, backup
  segmented   dmz, application, data, backup

legitimate flow cut in the segmented layout: field -> data, branch -> backup
```

The second row counts this lesson's most expensive misconception. In the perimeter layout the
public-interface items were moved into a separate zone, a single rule allowing access from
outside only to that zone was written, and the items the outside zone reaches at the first hop
dropped from sixteen to four. Against that, the transitive item count stayed at two hundred ten
and open paths came out at a full fifty-six. Because there is no boundary between the inner
zones, the side that reaches one of the four public-interface items reaches the whole of the six
inner zones. A demilitarized zone by itself is not a zone; it is a first hop — without a boundary
behind it, it only delays the first step.

The third and fourth rows show the same segmentation in two separate layouts. Both bring the
transitive item count from two hundred ten down to twenty-five; the difference is at the first
hop. When the public-interface items are left inside the application zone, a connection from the
outside zone lands directly next to sixteen items and, because there is no boundary within the
zone, reaches all of them. Moved to a separate zone, that number drops to four and the number of
boundaries that must be crossed to reach the data zone rises from two to three. The entire gain
of the separate zone is these two numbers: twelve items and one boundary.

The rule column gives the cost. The flat layout has no rules at all; the segmented layout needs
fifteen lines, and these fifteen lines are nothing but zone pairs — once ports and protocols are
added, each line splits into several. The column on the right gives a sneakier cost: segmentation
cuts two legitimate flows. The quarter-end reconciliation ran from the field zone to the data
zone, the branch archive from the branch zone to the backup zone; neither occurred to anyone
while rules were being written, because both run rarely. On the flat network these flows had
never appeared at all, because they needed no rule.

There is a reverse-reading trap in the open-path column. The segmented layout shows thirty-one
open paths, the layout without a demilitarized zone twenty-three; going by the number alone, the
second looks narrower. The difference comes from the zone count: an eight-zone graph has fifty-six
directed pairs, a seven-zone graph forty-two. Adding a zone grows the pair count. The path count
therefore cannot be compared on its own; what can be compared is the item the path lands on, and
that column reads twenty-five in both layouts.

## When a Zone Falls

```js
// run2.mjs — zones and items reached when a zone falls; flat and segmented layouts side by side
import { LAYOUT, reached, itemsOf } from './zone.mjs';

const [Z, FLAT] = [LAYOUT.flat[0], LAYOUT.flat[1]];
const SEG = LAYOUT.segmented[1];
const inner = (s) => [...s].filter((z) => z !== 'outside');
console.log(`${'fallen zone'.padEnd(11)} ${'flat zone'.padStart(9)} ${'flat item'.padStart(9)} ` +
  `${'seg. zone'.padStart(13)} ${'seg. item'.padStart(13)}  reached in segmented layout`);
let [td, ts] = [0, 0];
for (const z of Z.filter((x) => x !== 'outside')) {
  const [f, s] = [inner(reached(FLAT, z)), inner(reached(SEG, z))];
  td += f.length; ts += s.length;
  console.log(`${z.padEnd(11)} ${String(f.length).padStart(9)} ${String(itemsOf(f, 'inside')).padStart(9)} ` +
    `${String(s.length).padStart(13)} ${String(itemsOf(s, 'separate')).padStart(13)}  ${s.join(', ') || '-'}`);
}
console.log(`\nzone total reached   flat ${td}   segmented ${ts}   closed ${td - ts} (${Math.round((td - ts) * 100 / td)}%)`);
const d = inner(reached(SEG, 'dmz')), m = inner(reached(SEG, 'management'));
console.log(`when dmz falls        ${d.length} zones, ${itemsOf(d, 'separate')} items  (${d.join(', ')})`);
console.log(`when management falls   ${m.length} zones, ${itemsOf(m, 'separate')} items  — ${SEG.filter(([a]) => a === 'management').length} direct rules`);
console.log(`paths segmentation closes ${td - ts}; zone it cannot close 1`);
```

```
fallen zone flat zone flat item     seg. zone     seg. item  reached in segmented layout
branch              5        90             4            25  dmz, application, data, backup
field               5       150             4            25  dmz, application, data, backup
application         5       194             2             9  data, backup
data                5       204             1             3  backup
backup              5       207             0             0  -
management          5       205             6           205  dmz, branch, field, application, data, backup

zone total reached   flat 30   segmented 17   closed 13 (43%)
when dmz falls        3 zones, 21 items  (application, data, backup)
when management falls   6 zones, 205 items  — 6 direct rules
paths segmentation closes 13; zone it cannot close 1
```

In the flat column every row gives the same number: five zones. Which zone falls does not change,
the result does not change; the item difference in the second column comes only from excluding
the falling zone's own items. In the segmented column the rows separate from one another, and the
direction of that separation is the direction of the rule list. When the backup zone falls, no
zone is reached, because the only rule leaving that zone points outward. When the data zone
falls, only the backup zone is reached. Because the flow direction runs downward, the zones at
the bottom of the chain cannot climb back up.

The branch and field zones carry the largest item sets — one hundred twenty and sixty items — and
when they are the zone that falls, the items reached stay at twenty-five. In the flat layout the
same two rows gave ninety and one hundred fifty. The total of closed paths is thirteen out of
thirty; segmentation closes roughly half of the zone pairs, not all of them.

The two cut legitimate flows have a counterpart in this table. If a rule is written for the
reconciliation flow from the field zone to the data zone, the line count rises to sixteen and the
field zone reaches the data zone through a single boundary instead of three. The items reached
stay at twenty-five — the data zone was already at the end of the chain — but the path shortens;
and since the flow is legitimate, the rule gets written. Segmentation's measure is therefore not
the number taken on the day it was set up. Every new legitimate flow adds a line, every line opens
a path, and lines accumulate in one direction only — no one is warned to delete the rule once the
flow stops.

The last lines name the zone segmentation cannot close. The management zone is written directly
into six zones, and when it falls, two hundred five items are reached — the same number as on the
flat network, even in the segmented layout. The reason is not a gap but the work itself: the
management zone must reach every zone to distribute configuration and troubleshoot. Segmentation
closes no path in this zone. The twenty-one items reached when the demilitarized zone falls belong
to the same class: a public-facing zone carries an inbound rule by definition, without which the
zone serves no purpose at all. The path segmentation closes can be counted; the zone it cannot
close can also be counted, and there is one of it.

## Summary

- Perimeter control alone drops the items the outside zone reaches at the first hop from 16 to 4,
  but the transitive item count stays at 210: with no boundary between the inner zones, the side
  that reaches a single public-facing item reaches all 6 inner zones.
- Segmentation drops the transitive item count from 210 to 25. Moving the public-facing items to a
  separate zone gains 2 numbers on top of that: the item reached at the first hop goes from 16 to
  4, and the number of boundaries to the data zone goes from 2 to 3.
- The cost is 15 rule lines and 2 cut legitimate flows. Both of the cut flows are jobs that run
  rarely (the quarter-end reconciliation, the branch archive); on the flat network they had never
  appeared, because they needed no rule.
- The zone total reached when one zone falls is 30 in the flat layout, 17 in the segmented layout:
  13 closed paths. When the branch zone falls, the items reached drop from 90 to 25.
- The zone segmentation cannot close is the management zone: it is written directly into 6 zones,
  and when it falls, 205 items are reached — the same number as in the flat layout.

## Next Step

The management-zone row leaves one item unresolved: a zone written directly into six zones is the
one place segmentation closes no path. The next lesson's question is not removing this zone, but
what is gained by routing the connections leaving it through a single point. When a checkpoint is
placed, how many connections pass through that point, what does the number of claims that point
records and proves become, how many paths are cut at once when the point falls, and most
importantly — how many paths reach the target without ever using that point.
