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 1 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 1 cell does not reach PLCs in another cell when the cells are in separate VLANs with firewall rules between them.
The DMZ (Demilitarized Zone) sits between OT and IT. The DMZ 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 | Serves as a single, audited entry point for remote access to OT. Remote sessions terminate here. |
| Patch server (WSUS/SCCM) | Downloads patches from the internet and stages the patches for OT deployment. OT devices pull patches from the DMZ, never from the internet. |
| Data aggregator | Collects, filters, and normalizes OT data before sending the data to IT or cloud. The aggregator reduces the attack surface by limiting what data leaves OT. |
| Antivirus update server | Downloads signature updates and distributes the updates to OT endpoints. OT devices never connect to the internet for updates. |
| Syslog collector | Receives logs from OT devices. The IT SIEM pulls logs from this collector, not directly from OT switches. |
Each component exists in the DMZ because the component communicates with both IT and OT. Placing the component 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. The diode uses a fiber-optic transmitter on the source side and a receiver on the destination side, with no return fiber. Data flows in 1 direction only. No software vulnerability, misconfiguration, or exploit creates 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. No return fiber exists, so no data flows from IT to OT. The diode software handles protocol conversion: the software 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 the Hirschmann ecosystem |
| Fox DataDiode | Fox-IT | High-throughput (up to 1 Gbps) |
Data diodes are appropriate for the highest-security zones (safety systems, infrastructure) where the consequence of inbound compromise outweighs the inconvenience of one-way communication.
OT firewall rules differ from IT rules. OT rules 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. PROFINET RT traffic does not route and does not respond to IP-based firewall rules. PROFINET traffic stays within its VLAN. The firewall helps protect the PROFINET VLAN by blocking IP traffic into the VLAN 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. Safety systems communicate only with their dedicated safety controllers on the same VLAN.
Remote access is a major attack vector. Apply 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 the rules 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 creates a return path through a data diode. Use data diodes for the highest-security zones.
Validate rules automatically
Script-based rule validation catches overly permissive rules before the rules reach production. Run validation after every change.
Segmentation defines the network architecture. IEC 62443 provides the framework for determining the security level each zone requires and the controls to implement. The next chapter covers the practical application of IEC 62443 security levels to OT network design.