DMZ contains shared services
Historian mirrors, jump servers, and patch servers live in the DMZ. Neither IT nor OT connects directly to the other.
The previous chapter covered the threats OT networks face. The most effective defense against those threats is network segmentation: dividing the network into isolated zones so that a compromise in one zone does not spread to others.
Industrial protocols have no authentication. A device with network access reads and writes PLC registers. Segmentation limits which devices have network access to which zones. An attacker who compromises an engineering workstation in one cell cannot reach PLCs in another cell if the cells are in separate VLANs with firewall rules between them.
The DMZ (Demilitarized Zone) sits between OT and IT. It contains systems that communicate with both sides.
| Component | Purpose in DMZ |
|---|---|
| Historian mirror | Receives replicated data from the OT historian. IT users query the mirror, not the OT historian directly. |
| Jump server | Provides a single, audited entry point for remote access to OT. All remote sessions terminate here. |
| Patch server (WSUS/SCCM) | Downloads patches from the internet, stages them for OT deployment. OT devices pull patches from the DMZ, never from the internet. |
| Data aggregator | Collects, filters, and normalizes OT data before sending it to IT or cloud. Reduces the attack surface by limiting what data leaves OT. |
| Antivirus update server | Downloads signature updates and distributes them to OT endpoints. OT devices never connect to the internet for updates. |
| Syslog collector | Receives logs from OT devices. IT SIEM pulls logs from this collector, not directly from OT switches. |
Each component exists in the DMZ because it needs to communicate with both IT and OT. Placing it in the DMZ means neither IT nor OT has direct access to the other.
| Source | Destination | Allowed | Denied |
|---|---|---|---|
| IT | DMZ | Historian queries, jump server RDP | Direct access to OT |
| DMZ | OT | Historian replication (pull), patch distribution | Arbitrary connections |
| OT | DMZ | Syslog, SNMP traps, historian data push | Internet access |
| IT | OT | Never | Everything |
| OT | Internet | Never | Everything |
A data diode enforces one-way data flow at the physical layer. It uses a fiber-optic transmitter on the source side and a receiver on the destination side, with no return fiber. Data flows in one direction only. No software vulnerability, misconfiguration, or exploit can create a return path because the physical medium does not exist.
The transmitter converts data to light pulses. The receiver converts light pulses back to data. There is no return fiber, so no data can flow from IT to OT. The diode software handles protocol conversion: it receives TCP data on the OT side, strips the protocol headers, sends the raw data over the one-way link, and reconstructs the TCP session on the IT side.
| Product | Vendor | Key Feature |
|---|---|---|
| Waterfall Unidirectional Gateway | Waterfall Security | Supports OPC, Modbus, historian replication |
| Owl Cyber Defense | Owl | Cross-domain transfer, government certified |
| Hirschmann Eagle One | Belden | Integrated with Hirschmann ecosystem |
| Fox DataDiode | Fox-IT | High-throughput (up to 1 Gbps) |
Data diodes are appropriate for the highest-security zones (safety systems, critical infrastructure) where the risk of inbound compromise outweighs the inconvenience of one-way communication.
OT firewall rules differ from IT rules. They are protocol-specific, direction-specific, and source/destination-specific.
| Rule | Source | Destination | Protocol | Port | Action | Rationale |
|---|---|---|---|---|---|---|
| 1 | 192.168.10.50 (SCADA) | 192.168.20.0/24 (PLCs) | TCP | 502 | Permit | SCADA reads Modbus registers |
| 2 | 192.168.10.51 (Eng WS) | 192.168.20.0/24 (PLCs) | TCP | 102 | Permit | Engineering access (S7comm) |
| 3 | 192.168.20.0/24 (PLCs) | 192.168.10.60 (Historian) | TCP | 4840 | Permit | PLCs push OPC UA data |
| 4 | 192.168.50.0/24 (Mgmt) | 192.168.20.0/24 (Switches) | TCP | 22 | Permit | SSH management |
| 5 | 192.168.50.0/24 (Mgmt) | 192.168.20.0/24 (Switches) | UDP | 161 | Permit | SNMPv3 polling |
| 6 | Any | 192.168.20.0/24 | UDP | 67-68 | Deny | No DHCP in production |
| 7 | Any | Any | Any | Any | Deny | Implicit deny-all |
PROFINET RT uses EtherType 0x8892 at Layer 2. It cannot be routed or firewalled with IP-based rules. PROFINET traffic stays within its VLAN. The firewall protects the PROFINET VLAN by blocking all IP traffic into it except management access (SSH, SNMP) from the management VLAN.
| VLAN | Zone | Devices | Routing |
|---|---|---|---|
| 10 | Production | PLCs, drives, I/O modules | Routed to SCADA, historian only |
| 20 | SCADA | HMIs, SCADA servers | Routed to production, historian |
| 30 | Safety | SIS controllers, safety PLCs | No routing to any other VLAN |
| 40 | Engineering | Engineering workstations | Routed to production (controlled) |
| 50 | Management | Switch management, SNMP | Routed to all (SSH, SNMP only) |
| 4094 | Black hole | Unused ports | No routing, no traffic |
Safety systems (VLAN 30) have no routing to any other VLAN. They communicate only with their dedicated safety controllers on the same VLAN.
Remote access is a major attack vector. Secure it with defense in depth:
The following script reads a firewall rule list (CSV format) and checks for overly permissive rules: rules that allow “any” source, “any” destination, or broad port ranges to OT subnets.
import csvfrom dataclasses import dataclass
OT_SUBNETS = ["192.168.10.0/24", "192.168.20.0/24", "192.168.30.0/24"]
@dataclassclass Finding: rule_num: int severity: str message: str
def validate_rules(rules_csv: str) -> list[Finding]: findings = [] with open(rules_csv) as f: for row in csv.DictReader(f): num = int(row["rule"]) src, dst = row["source"], row["destination"] port, action = row["port"], row["action"].lower() if action != "permit": continue # Check for "any" source to OT subnets if src.lower() == "any" and dst in OT_SUBNETS: findings.append(Finding(num, "CRITICAL", f"Rule {num}: 'any' source permitted to OT subnet {dst}")) # Check for broad port ranges if "-" in port: low, high = map(int, port.split("-")) if high - low > 100: findings.append(Finding(num, "WARNING", f"Rule {num}: broad port range {port} to {dst}")) # Check for any/any if src.lower() == "any" and dst.lower() == "any": findings.append(Finding(num, "CRITICAL", f"Rule {num}: permit any-to-any detected")) return findings
for f in validate_rules("firewall_rules.csv"): print(f"[{f.severity}] {f.message}")Run this script after every firewall change to catch overly permissive rules before they reach production. The CSV format (rule,source,destination,protocol,port,action) matches the export format of most firewall management tools.
DMZ contains shared services
Historian mirrors, jump servers, and patch servers live in the DMZ. Neither IT nor OT connects directly to the other.
Data diodes enforce physics
No software exploit can create a return path through a data diode. Use them for the highest-security zones.
Validate rules automatically
Script-based rule validation catches overly permissive rules before they reach production. Run validation after every change.
Segmentation defines the network architecture. IEC 62443 provides the framework for determining what security level each zone requires and what controls to implement. The next chapter covers the practical application of IEC 62443 security levels to OT network design.