Lesson 12 / 14
VPN Technologies
A tunnel is a routing arrangement: the tunnel selector decides which destinations it lets inside, and a flow that is not selected exits without ever seeing the boundary. Across forty flows, four selectors and two tunnel configurations measure false admit and false deny separately.
Contents
The course’s four lessons so far used the boundary in the same direction throughout: to separate the outside from the inside. Firewall types, the rule chain, the zone graph, and the detection mechanism all took the distinction between “what is inside” and “what is outside” as given, and built their decision on top of that distinction.
But what if an outside endpoint has to be let in? A remote employee’s machine, an entire segment at another site, or a cluster inside a cloud provider — none of these is physically inside, and yet they need to count as inside. VPN (virtual private network) is the name of the technology class that does this job. This lesson’s question is not what the tunnel protects: it is which flows the tunnel lets inside, and what happens to the ones it does not.
A Tunnel Is a Routing Decision
What can be done once the tunnel is established — the scope of access — was measured in the Perimeter Defense topic of the Cybersecurity curriculum and is not repeated here. What is measured here is the decision before the tunnel is established, and that decision is not a security decision but a routing decision.
The software running at the remote end answers a single question for every flow to be sent: does this flow go through the tunnel, or through the local exit? If the answer is yes, the flow is wrapped and carried to the concentrator at the other end of the tunnel; it is opened there and enters the organization’s boundary from that point. If the answer is no, the flow is never wrapped, exits directly through the remote end’s own connection, and never sees the organization’s boundary at all.
The distinction between these two paths carries the entire lesson. A flow going through the tunnel is subject to the rule chain. A flow not going through the tunnel is subject to no rule at all — because it never passes through the place where the rule was written.
The Tunnel Selector
The structure that makes this decision is called the tunnel selector. A selector is not a rule chain; it produces no action, it only defines a set: which destinations enter the tunnel.
# taught dump , not executed
tunnel definition A
configuration : remote access
selector : destination zone { int }
assigned zone : int
not selected : local exit
tunnel definition B
configuration : site-to-site
selector : destination zone { int, dmz, mgmt }
assigned zone : (none; source zone is preserved)
not selected : local exit
A setup where the selector covers every destination is called full tunnel; one that leaves part of them out is called a split tunnel. The split tunnel’s rationale is carrying capacity: routing all of the remote end’s traffic through the concentrator means routing flows with no relevance at all through it too. What this lesson asks is not whether the rationale holds, but in which direction its cost is paid.
A Selector Is a Set of Paths
Seeing where the selector sits makes the rest of the lesson readable. In the Switching and Routing course, a packet’s path was determined by the next hop, chosen by looking at its destination. The tunnel selector sits in exactly the same place: the tunnel interface is one of the next-hop candidates, and the selector says for which destinations that candidate wins. This is why the selector is evaluated before the rule chain — the rule chain waits for the packet to reach the boundary; the selector decides whether the packet sets out toward the boundary at all.
This has two consequences. First, the selector’s resolution is routing’s resolution: it looks at the destination. A distinction like “this port through the tunnel, that port through the local exit” requires telling apart two flows going to the same destination, and a set of paths cannot express that. Second, the selector sits at the remote end. The concentrator sends it when the tunnel is established, but what determines the path of a non-selected flow is the remote end’s own routing table. Loosening the selector at the remote end is a configuration flaw, and it gives no indication at all on the boundary side: the boundary never sees that flow in the first place.
Two Configurations: Site-to-Site and Remote Access
The difference between the two tunnel configurations is not in the selector; it is in the identity of the source.
In a site-to-site tunnel, both ends are real zones. One site’s internal segment connects to another’s; every flow arrives with its own zone, and the boundary evaluates it by that zone. The tunnel only changes the path; it does not change who the flow is.
In a remote-access tunnel, the remote end has no zone of its own. It is a single
machine, it connects from a variable location, and the boundary does not know which zone
to place it in. This is why the tunnel assigns it a zone: every flow coming out of the
tunnel enters the boundary with the zone the concentrator gives it. The assignment is not
a convenience; it is a necessity — the rule chain reads the source criterion, and it
needs something to read.
The measurement’s assumptions:
- NS51 — The forty flows are produced from the course’s shared fixture; each flow’s
source, destination, and port come from the fixture. The oracle is the
policyfunction, known because we wrote the fixture.policyis not changed in this lesson. - NS52 — A flow entering the tunnel is opened at the concentrator and enters the
boundary from there; the boundary evaluates it with
policy. In the remote-access configuration, evaluation is done on a record whose source has been replaced with the assigned zone. What is changed is the record, not the function. - NS53 — A flow not entering the tunnel goes out through the remote end’s local exit. If its destination is the external zone it arrives; if it is headed for one of the internal zones it never arrives at all. These flows never pass through the rule chain.
- NS54 — Four selectors are compared: full tunnel and three split sets. The selectors look only at the destination zone; they do not use the oracle.
- NS55 — In the remote-access configuration, the assigned zone is a second axis and is measured on its own; the selector is held fixed at full tunnel in that measurement.
- NS56 — The set is forty flows; the smallest measurable difference is
1/40 = 0.025. A smaller difference cannot be defended with this set.
Measurement
"""A tunnel is a routing decision: the selector decides which destinations come inside. Part 1 - four tunnel selectors, in two tunnel configurations. Part 2 - what the zone a remote-access tunnel assigns does on its own. """ SEED = 20260811 ZONES = ("ext", "dmz", "int", "mgmt") def generator(seed): d = seed % 2147483646 + 1 def r(n): nonlocal d d = (d * 48271) % 2147483647 return d % n return r def flows(count=40, seed=SEED): r, out = generator(seed), [] for i in range(count): out.append({"no": i + 1, "source": ZONES[r(4)], "dest": ZONES[r(4)], "port": (80, 443, 22, 3306, 8080)[r(5)]}) return out def policy(a): """Intent: only the DMZ web entry from outside; nobody from outside to mgmt.""" if a["source"] == "ext": return a["dest"] == "dmz" and a["port"] in (80, 443) if a["source"] == "dmz": return a["dest"] == "int" and a["port"] == 3306 if a["source"] == "mgmt": return True return a["dest"] != "mgmt" def diff(events, intent, device): d = {"correct_pass": 0, "correct_block": 0, "false_admit": 0, "false_deny": 0} for o in events: n, g = intent(o), device(o) if n and g: d["correct_pass"] += 1 elif not n and not g: d["correct_block"] += 1 elif g: d["false_admit"] += 1 else: d["false_deny"] += 1 return d SELECTORS = {"full tunnel": lambda a: True, "split: int": lambda a: a["dest"] == "int", "split: int, dmz": lambda a: a["dest"] in ("int", "dmz"), "split: except ext": lambda a: a["dest"] != "ext"} def site_to_site(selector): """Both ends are real zones; the tunnel only picks the path.""" def run(a): if selector(a): return policy(a) return a["dest"] == "ext" # a flow not selected exits directly return run def remote_access(selector, assigned="int"): """The remote end has no zone of its own; the tunnel assigns it one.""" def run(a): if selector(a): return policy({**a, "source": assigned}) return a["dest"] == "ext" return run ak = flows() print(f"flow {len(ak)} | intent passes " f"{sum(1 for a in ak if policy(a))} | destination external " f"{sum(1 for a in ak if a['dest'] == 'ext')}") print() print(f"{'tunnel selector':<20s} {'selected':>8s} {'site-to-site':>25s}" f"{'remote access':>26s}") print(f"{'':20s} {'':8s} {'correct':>8s} {'f.admit':>8s} {'f.deny':>8s}" f" {'correct':>8s} {'f.admit':>8s} {'f.deny':>8s}") for name, s in SELECTORS.items(): x, y = diff(ak, policy, site_to_site(s)), diff(ak, policy, remote_access(s)) print(f"{name:<20s} {sum(1 for a in ak if s(a)):8d} " f"{x['correct_pass'] + x['correct_block']:8d} {x['false_admit']:8d} " f"{x['false_deny']:8d} {y['correct_pass'] + y['correct_block']:8d} " f"{y['false_admit']:8d} {y['false_deny']:8d}") print() print(f"{'assigned zone':<14s} {'correct':>6s} {'false admit':>12s} " f"{'false deny':>12s} (full tunnel, remote access)") for assigned in ZONES: d = diff(ak, policy, remote_access(SELECTORS["full tunnel"], assigned)) print(f"{assigned:<14s} {d['correct_pass'] + d['correct_block']:6d} " f"{d['false_admit']:12d} {d['false_deny']:12d}") print() print(f"{'not selected':<20s} {'count':>5s} {'exits directly':>15s} " f"{'unreachable':>11s}") for name, s in SELECTORS.items(): outside = [a for a in ak if not s(a)] print(f"{name:<20s} {len(outside):5d} " f"{sum(1 for a in outside if a['dest'] == 'ext'):15d} " f"{sum(1 for a in outside if a['dest'] != 'ext'):11d}")
flow 40 | intent passes 22 | destination external 11
tunnel selector selected site-to-site remote access
correct f.admit f.deny correct f.admit f.deny
full tunnel 40 40 0 0 25 11 4
split: int 10 24 6 10 21 9 10
split: int, dmz 18 30 6 4 25 11 4
split: except ext 29 34 6 0 25 11 4
assigned zone correct false admit false deny (full tunnel, remote access)
ext 22 0 18
dmz 20 0 20
int 25 11 4
mgmt 22 18 0
not selected count exits directly unreachable
full tunnel 0 0 0
split: int 30 11 19
split: int, dmz 22 11 11
split: except ext 11 11 0
Where an Unselected Flow Goes
The upper table’s first row is a baseline. In a site-to-site full tunnel, all forty flows go through the tunnel, each enters the boundary with its own zone, and the result is 40 correct, 0 false admit, 0 false deny. Here the tunnel added nothing to policy and took nothing away; it only carried the remote segment in front of the boundary.
As the selector narrows, both directions start speaking at once, and the two do not come from the same source.
False deny comes from flows not taken into the tunnel whose destination is an internal
zone. They try to go by the local exit, fail to reach the organization, and intent would
have passed a portion of them. 10 for the int selector, 4 for int, dmz, 0
for except ext. Widening the selector directly fixes this direction.
False admit, however, stays fixed at 6 no matter how wide the selector gets. Its
source is in the lower table’s last row: in all three split configurations, 11 of the
flows not entering the tunnel have an external destination, and these eleven really do
arrive through the local exit. The oracle would have blocked 6 of them — because
policy does not pass every flow with an external destination. Widening the selector from
int to except ext never touches these six, because none of the three selectors takes
an external destination into the tunnel.
The pattern is this: in a split tunnel, false deny is born from the selector’s
narrowness, and false admit is born from what is left outside the selector never seeing
the rule chain at all. The first closes as the selector is widened; the second does
not — closing it requires the selector to take in the external destination too,
meaning abandoning the split tunnel and returning to full tunnel. The except ext row
shows this exactly: false deny is zeroed, false admit is still 6.
The size of the numbers is also worth noting. In a set of forty flows, the smallest
measurable difference is 1/40 = 0.025. The fixed six false admits amount to 0.150 in
this set; the int selector’s ten false denies amount to 0.250; both are well above the
band. Every nonzero row difference in the table is at least two flows, that is,
0.050; no reading rests on a single flow. What the measurement claims, however, is not
the numbers themselves but that the two directions come from separate sources: one
from the narrowness of the selector, the other from what is left outside the selector
never passing through the audit at all.
What the Assigned Zone Does on Its Own
The three columns on the right measure the same selectors in the remote-access
configuration, and the table changes: even at full tunnel, 25 correct, 11 false admit,
4 false deny. Although all forty flows go through the tunnel, the result degrades,
because in this configuration flows enter the boundary not with their own zone but with
the zone the tunnel assigns. The rule chain reads the source criterion, and it now
reads the same value for every one of them.
The middle table opens up this whole axis. With the assigned zone int, 11 false
admit, 4 false deny. If the assignment is pulled to ext, false admit drops to 0
and false deny climbs to 18: no flow coming from the tunnel is trusted at all. If the
assignment is opened to mgmt, false admit is 18, false deny is 0: every flow has
been placed in the widest zone. dmz, meanwhile, worsens both directions at once — 0
and 20 — because the path policy gives the DMZ is a narrow one and does not match the
real distribution of remote ends.
None of the four rows gives zero-zero. The reason is structural: the forty flows come from four separate source zones, and a single assignment collects them into a single zone. Information carrying a distinction, once mapped to a single value, cannot be recovered. The remote-access tunnel’s cost is not the bytes it carries but the distinction it erases.
The only way to recover the erased distinction is to stop the assignment from being a
single value: set up a separate tunnel for each privilege set and assign each tunnel its
own zone. The measurement says this directly — flows from four zones under four separate
assignments produce four separate rows, and none of the rows carries another’s error. The
cost shows up on the rule side: as many concentrator interfaces as tunnels, as many
source values as interfaces, and that many separate branches in the rule chain. The
course already said that counting rules does not measure policy; here the reverse is also
true — avoiding a rule does not measure policy either, it just closes off a distinction
cheaply.
Summary
- Before it is established, a tunnel is a routing decision: the tunnel selector determines which destinations are let inside, and an unselected flow never passes through the rule chain.
- In a site-to-site full tunnel, forty flows give 40 correct, 0 false admit, 0 false deny; the tunnel adds nothing to policy.
- In a split tunnel, false deny comes from the selector’s narrowness and drops 10 → 4 → 0 as it widens; false admit stays fixed at 6 across all three split configurations, because its source is the 11 externally destined flows left outside the selector.
- In the remote-access configuration, the tunnel must assign the flow a zone; even at full tunnel, the result falls to 25 correct, 11 false admit, 4 false deny.
- The assigned zone alone pulls the two directions in opposite ways: 0 / 18 with
ext, 18 / 0 withmgmt. Collecting flows from four zones into a single zone erases the distinction irrecoverably.
Next Step
The remote-access tunnel’s whole trick is making an outside endpoint look as if it came
from the inside to the rule chain. The reason this trick is necessary sits in a single
line: the rule chain still reads the source criterion and expects a zone to read. The
next lesson measures what happens when this expectation is removed — how rule count and
the two-way error change when the source criterion is stripped from the rule chain, and
what has to be put in the location criterion’s place.
To keep your progress and take notes, Log in
My notes
Log in to take notes.