Chapter 2 — Networks and internet services
Lesson map
Lesson 02 published. Map, example, EX004.
flowchart TD
N0["Learning outcomes and connection"]
N1["2.1 From addresses to zones"]
N2["2.2 Services, ports and what they imply"]
N3["2.3 Declared versus observed"]
N4["2.5 Practical activity: flows versus conduits"]
N5["Chapter synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5
N5 --> EX["Worked example"]
EX --> Q["Exercise EX004"]
Logical drawing
flowchart LR
N0["Learning outcomes and connection"]
N1["2.1 From addresses to zones"]
N2["2.2 Services, ports and what they imply"]
N3["2.3 Declared versus observed"]
N4["2.5 Practical activity: flows versus conduits"]
N5["Chapter synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX004"]
[Learning outcomes and connection]
|
v
[2.1 From addresses to zones]
|
v
[2.2 Services, ports and what they imply]
|
v
[2.3 Declared versus observed]
|
v
[2.5 Practical activity: flows versus conduits]
|
v
[Chapter synthesis]
|
v
[Worked example] --> [Exercise EX004]
Learning outcomes and connection
After this chapter you should be able to:
- describe a network in terms of zones (groups of systems with similar trust and function) and conduits (the permitted paths between them);
- read a flow record and map it to zones, protocols and ports;
- compare what the network actually carries with what was declared, and classify each difference;
- explain why a declared flow that carries no traffic is also a finding.
Chapter 1 looked at one host. A service is many hosts talking to each other. In critical infrastructure, the network is where most of the separation between business systems, control systems and the outside world is supposed to be enforced — and where it most often erodes, one "temporary" rule at a time.
2.1 From addresses to zones
An IP address tells you where a packet is going; it does not tell you what the destination is for. Operators therefore group systems into zones: in LANTERN's design, the station zone (PLCs and RTUs at pumping stations), the relay zone (RLY hosts), the corporate zone (FSTS business systems and the historian's report users) and external (anything outside, including vendors). The idea of zones and conduits comes from industrial-automation security practice: the ISA/IEC 62443 series uses exactly these terms (ISA overview), and NIST's OT guide recommends segmentation of control networks from business networks (NIST SP 800-82 Rev. 3).
A conduit is a declared, justified path between two zones: source zone, destination zone, protocol, port and purpose. LANTERN declares four: stations send Modbus/TCP (tcp/502) to the relay; the relay sends HTTPS (tcp/443) to the historian in the corporate zone; administrators in corporate reach the relay by SSH (tcp/22); and the relay serves time (udp/123, NTP) to stations.
2.2 Services, ports and what they imply
A port number is a convention, not a guarantee: tcp/443 usually carries TLS, but anything can listen there. Still, the convention tells you what to expect and what to question. Industrial protocols such as Modbus/TCP were designed for closed networks and have no built-in authentication; a device that accepts Modbus writes will accept them from anyone who can reach it. That is why the path matters so much: if a corporate laptop can reach a PLC on tcp/502, the only thing between a mistake (or an attacker) and a pump is the absence of someone trying.
Internet services — DNS, NTP, HTTPS, SSH — are the plumbing of every modern service. Each has a failure mode that matters operationally: DNS failure makes names unresolvable even though hosts are fine; NTP failure lets clocks drift; TLS failure (an expired certificate) turns a working service into an unreachable one; SSH exposed to the internet attracts constant password-guessing.
2.3 Declared versus observed
A firewall rule base or a design diagram says what should happen. Flow records — summaries of conversations collected by firewalls, switches or sensors — say what did happen. Comparing the two is one of the highest-value routine tasks a technologist can do, and it needs no attack tools: it is reading your own records. Each difference falls into one of four classes:
- Observed and declared — expected; still check volumes look normal.
- Observed but undeclared — a path exists that nobody justified. Either the declaration is incomplete (update it, with an owner and purpose) or the rule base is too permissive (fix it).
- Declared but not observed — either the service is not working (a station is silent), or the declared conduit is obsolete and should be removed.
- Observed to an unknown zone — an address you cannot place; your inventory is incomplete.
2.4 Worked example: one night's flows (synthetic)
B. Lind gives you a flow export from the hub firewall for one night. Most flows match conduits. Three do not: a corporate workstation 10.40.3.7 talked Modbus directly to the PLC at PS-04 (12 conversations); an external address 192.0.2.44 reached the relay on SSH (3 conversations); and the relay made one HTTPS connection to an external address 192.0.2.80. One declared source, PS-09's RTU at 10.20.0.13, sent nothing.
None of these is proof of an attack. The corporate-to-PLC flow might be an engineer using a configuration tool from the wrong network; the external SSH might be internet background noise reaching an exposed port; the relay's outbound HTTPS might be a package update. Each is still a finding, because each is a path the design does not allow. The silent station is an operational problem first: Water operations may be running PS-09 blind.
2.5 Practical activity: flows versus conduits
Run Lab 2. It assigns each address to a zone with the ipaddress module, compares each flow with the declared conduits, and reports declared flows with zero traffic.
Lab 2 (save as lab02.py, run with python3 lab02.py):
# Lab 2 - observed flows versus the declared conduit list (offline, standard library only)
# Synthetic: Fallowmere Water pumping-station network. Addresses are documentation/private ranges.
import ipaddress
zones = {
"station": ipaddress.ip_network("10.20.0.0/24"),
"relay": ipaddress.ip_network("10.30.0.0/28"),
"corp": ipaddress.ip_network("10.40.0.0/16"),
"vendor": ipaddress.ip_network("192.0.2.0/24"), # TEST-NET-1, stands in for internet
}
declared = {("station", "relay", "tcp", 502), ("relay", "corp", "tcp", 443),
("corp", "relay", "tcp", 22), ("relay", "station", "udp", 123)}
flows = """10.20.0.11,10.30.0.5,tcp,502,1840
10.20.0.12,10.30.0.5,tcp,502,1790
10.30.0.5,10.40.8.20,tcp,443,3600
10.40.3.7,10.30.0.5,tcp,22,4
10.30.0.5,10.20.0.11,udp,123,96
10.40.3.7,10.20.0.11,tcp,502,12
192.0.2.44,10.30.0.5,tcp,22,3
10.30.0.5,192.0.2.80,tcp,443,1
10.20.0.13,10.30.0.5,tcp,502,0"""
def zone(a):
ip = ipaddress.ip_address(a)
return next((z for z, net in zones.items() if ip in net), "unknown")
seen = set()
for line in flows.splitlines():
s, d, p, port, count = line.split(",")
key = (zone(s), zone(d), p, int(port))
if int(count) == 0:
print(f"SILENT {s:>12} -> {d:<12} {p}/{port} (declared flow with no traffic)")
continue
seen.add(key)
tag = "OK " if key in declared else "UNDECL "
print(f"{tag} {s:>12} -> {d:<12} {p}/{port} {key[0]}->{key[1]} n={count}")
for k in sorted(declared - seen):
print("NOT SEEN", k)
Expected output (Python 3.13.5):
OK 10.20.0.11 -> 10.30.0.5 tcp/502 station->relay n=1840
OK 10.20.0.12 -> 10.30.0.5 tcp/502 station->relay n=1790
OK 10.30.0.5 -> 10.40.8.20 tcp/443 relay->corp n=3600
OK 10.40.3.7 -> 10.30.0.5 tcp/22 corp->relay n=4
OK 10.30.0.5 -> 10.20.0.11 udp/123 relay->station n=96
UNDECL 10.40.3.7 -> 10.20.0.11 tcp/502 corp->station n=12
UNDECL 192.0.2.44 -> 10.30.0.5 tcp/22 vendor->relay n=3
UNDECL 10.30.0.5 -> 192.0.2.80 tcp/443 relay->vendor n=1
SILENT 10.20.0.13 -> 10.30.0.5 tcp/502 (declared flow with no traffic)
Write each finding as one line: what was observed, which class it belongs to, the most likely benign explanation, the most worrying explanation, and the next check. Do not "test" any path yourself; ask the owner.
Applied to the night export, the four lines look like this. Corporate 10.40.3.7 to PS-04 Modbus, 12 conversations: undeclared path; benign, an engineer's configuration tool on the wrong network; worrying, a compromised workstation reaching a PLC; next check, ask Water engineering who uses that host. External 192.0.2.44 to the relay on SSH: exposure; next check, why port 22 answers from outside. Relay to 192.0.2.80 HTTPS: undeclared egress. PS-09 silent: declared flow with zero traffic, raised with Water operations first.
2.6 Common errors and limits
- Reading a rule base and believing it describes traffic; rules are intentions, flows are facts.
- Treating "undeclared" as "malicious" — or as "harmless because it's internal".
- Ignoring silent declared flows; a station that stops talking is a service outage before it is a security question.
- Assuming a port identifies an application.
- Collecting flows only at the internet edge, where the most consequential paths (corporate to control) are invisible.
Flow records show who talked to whom, on which port, how much; they do not show content or intent, and they may be sampled. They are excellent for checking architecture and poor for proving what was done.
Exercises — chapter 2
EX004 — Classify and prioritize. Objective: classify flow differences and order the follow-up. Inputs: Lab 2 output. Constraints: four findings; at most one sentence of explanation each. Steps: (1) put each finding in a class from Chapter 2 ("Declared versus observed"); (2) rank them for follow-up by potential consequence to the water service; (3) name the owner you would contact first for each. Expected evidence: table. Rubric: 3 = corporate→station Modbus ranked first because it reaches the physical process, silent PS-09 treated as an operational priority, external SSH and relay outbound sensibly ordered, owners named; 2 = classes right, ranking weakly justified; 1 = ranks by count of conversations; 0 = none.
EX005 — Update the declaration. Objective: decide between changing the design and changing the network. Inputs: Water operations confirms the relay's outbound HTTPS was an operating-system update from a vendor mirror; the corporate→PLC traffic was an engineer's laptop. Constraints: every conduit needs source, destination, protocol/port, purpose and owner. Steps: (1) decide for each whether to add a conduit or remove the path; (2) for any added conduit, write its full entry; (3) for the engineer's access, propose the declared path that should replace it. Expected evidence: updated conduit list and rationale. Rubric: 3 = update path added narrowly (specific mirror, via proxy or update server, owner named) or replaced by an internal update source; engineering access moved to a declared, authenticated jump path, not corporate→PLC; 2 = sensible but incomplete entries; 1 = simply allows everything observed; 0 = none.
EX006 — Extend the lab. Objective: make the comparison tool more useful. Inputs: Lab 2. Constraints: standard library only; do not change the synthetic data except as stated. Steps: (1) add a flow from 10.50.1.9 to the relay on tcp/443; (2) modify the script so that addresses outside every zone are reported as "UNKNOWN ZONE" rather than matched; (3) show the new output. Expected evidence: code diff and output. Rubric: 3 = unknown address reported distinctly and the run shows it; 2 = works but message unclear; 1 = not run; 0 = none.
Chapter synthesis
Think of the network as zones joined by declared conduits, each with a purpose and an owner. Rules state intentions; flow records state facts. Comparing the two regularly — observed-and-declared, observed-but-undeclared, declared-but-silent, unknown — is cheap, safe and among the most valuable things an implementer does. In critical infrastructure the most consequential undeclared path is the one that reaches the physical process.
Continue studying — chapter 02 of 30
← Chapter 01 · Course chapter index · Chapter 03 →
Continue to the next authored lesson.