The context

A large refinery operates two sensitive units:

  • Unit A — Atmospheric distillation: driven by a Siemens S7-1517F-3 PN/DP CPU on subnet 10.20.10.0/24. ATEX zone 1 atmosphere, SIL 2 on the process safety functions.
  • Unit B — Hydrocracker: driven by a Siemens S7-1518F-4 PN/DP CPU on subnet 10.20.20.0/24. ATEX zone 1 atmosphere, SIL 3 on some ESD loops.

The business requirement is simple: Unit B must continuously read 12 process variables from Unit A (cut temperatures, flow rates, column level) to optimize its own control. Acceptable latency: 200 ms. No round-trip command — flow A → B only.

The cybersecurity requirement is not: the two PLCs cannot sit on the same IP range. A compromise of A must not allow an attacker to pivot to B. Triton/Trisis (2017) demonstrated that, once inside the SIS of Unit A, an attacker could have disabled the explosion protections.

This is the typical situation faced by tens of thousands of industrial sites worldwide.

What you must NOT do

Three classic anti-patterns that solve the business problem but break the OT posture.

Anti-patternWhy it “works”Why it’s dangerous
Put both CPUs on the same VLAN”Simple, they talk directly over S7”A VLAN is not real segmentation. A compromised PLC sees the other. And Put/Get S7 requires enabling dangerous functions (force outputs, stop CPU).
Route the traffic through the head-office IT firewall”The central firewall sees everything”The IT firewall is not qualified for industrial protocols. It lets S7 through without inspection, and it becomes a single point of failure. Worse: a WAN outage cuts the control loop.
Site-to-site IPsec VPN between the two units”Encrypted tunnel, logical isolation”Once the tunnel is up, both networks see each other. Ransomware that encrypts A spreads to B in 30 seconds (cf. NotPetya / Maersk 2017).

The 3 OT-compliant options

┌────────────────────────────────────────────────────────────────────────────────┐
│  Option 1 — PN/PN COUPLER             ▶ Physical wiring, no IP routing         │
│                                                                                │
│    CPU A ── PROFINET ── ┤PN/PN├ ── PROFINET ── CPU B                          │
│                                                                                │
│    + Maximum security (galvanic, absolute isolation)                           │
│    + No IP/firewall configuration — Plug & Play                                │
│    + Certified for SIL if you pick the safety variant                          │
│    − Limited throughput (1,408 bytes / direction)                              │
│    − Direct physical cable → suited to adjacent units only                     │
│    − No native audit trail or logs                                             │
└────────────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────────────┐
│  Option 2 — OPC UA via SCALANCE SC + INDUSTRIAL DMZ       ◀ RECOMMENDED ▶     │
│                                                                                │
│  Unit A            ┌───────────── DMZ ───────────┐          Unit B             │
│  10.20.10.0/24     │  10.20.99.0/24              │          10.20.20.0/24     │
│                    │                              │                            │
│  CPU A ───SC1──┤ FW1 │── OPC UA proxy ───┤ FW2 │── SC2 ─── CPU B              │
│   (OPC UA          │                              │            (OPC UA         │
│   server)          │                              │             client)        │
│                                                                                │
│    + End-to-end OPC UA Sign + Encrypt encryption                               │
│    + Mutual authentication via X.509 certificates                              │
│    + Fine-grained firewall rules (one port, one IP, one certificate)           │
│    + Full audit (who, when, which variables)                                   │
│    + Scalable: a 3rd unit can be added without touching A or B                 │
│    − Longer configuration (certificates, rules)                                │
└────────────────────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────────────────────┐
│  Option 3 — UNIDIRECTIONAL DATA DIODE                                          │
│                                                                                │
│    CPU A ──TX── ▶▶▶ DIODE ▶▶▶ ──RX── CPU B   (optical hardware)               │
│                                                                                │
│    + Ultimate security: feedback physically impossible                         │
│    + Ideal for nuclear, defense, SIL 4 functional safety                       │
│    − High cost (€10–50k per diode)                                            │
│    − No handshake — needs a loss-tolerant protocol                             │
│    − No acknowledgment → hard to debug                                          │
└────────────────────────────────────────────────────────────────────────────────┘

For our case (continuous A → B flow, 12 variables, 200 ms latency, no nuclear), option 2 is the right trade-off: robust, auditable, scalable. That’s what we deploy.

Selected architecture — OPC UA + industrial DMZ

═════════════════════════════════════════════════════════════════════════════════
  UNIT A                     INDUSTRIAL DMZ                         UNIT B
  10.20.10.0/24 (VLAN 10)    10.20.99.0/24 (VLAN 99)                10.20.20.0/24 (VLAN 20)
═════════════════════════════════════════════════════════════════════════════════

  CPU S7-1517F-3 PN/DP       IPC547G — OPC UA proxy                 CPU S7-1518F-4 PN/DP
  10.20.10.10                10.20.99.10                            10.20.20.10
  OPC UA Server,             SIMATIC OPC UA                         OPC UA Client
  port 4840                  Aggregating Server                     → 10.20.99.10
       │                            │                                      │
       │ PROFINET                   │ Ethernet                             │ PROFINET
       │                            │                                      │
  SCALANCE XC208             SCALANCE XC208                         SCALANCE XC208
  10.20.10.20                10.20.99.20                            10.20.20.20
       │                            │                                      │
       │ Ethernet uplink            │                                      │ Ethernet uplink
       │ (fiber)                    │ (internal DMZ)                       │ (fiber)
       │                            │                                      │
  ┌────┴───────────┐                │                              ┌───────┴─────────┐
  │ SCALANCE       │                │                              │ SCALANCE        │
  │   SC632-2C     │                │                              │   SC632-2C      │
  │      FW1       │ ─.99.1 ────────┼─────────────── .99.2─        │      FW2        │
  │ eth0 : .10.1   │                │                              │ eth0 : .99.2    │
  │ eth1 : .99.1   │                                               │ eth1 : .20.1    │
  └────────────────┘                                               └─────────────────┘

═════════════════════════════════════════════════════════════════════════════════
  Allow FW1 : Proxy 10.20.99.10 ──► CPU A 10.20.10.10   tcp/4840  (OPC UA Sign+Encrypt)
  Allow FW2 : CPU B 10.20.20.10 ──► Proxy 10.20.99.10   tcp/4840  (OPC UA Sign+Encrypt)
  All other traffic → DENY + SIEM log
═════════════════════════════════════════════════════════════════════════════════

Hardware components used:

ReferenceRoleQuantity
6ES7517-3FP00-0AB0 — S7-1517F-3 PN/DPUnit A CPU (SIL 2)1
6ES7518-4FP00-0AB0 — S7-1518F-4 PN/DPUnit B CPU (SIL 3)1
6GK5208-0BA00-2AC2 — SCALANCE XC208Managed industrial switch (1× per zone: A, DMZ, B)3
6GK5632-2GS00-2AC2 — SCALANCE SC632-2CIndustrial L3 firewall / OPC UA DPI (FW1 between A↔DMZ, FW2 between DMZ↔B)2
6AG4112-2HH40-1AS2 — IPC547GIndustrial PC for the OPC UA proxy in the DMZ1

Architectural principles to keep in mind:

  1. The industrial DMZ (10.20.99.0/24) is a separate subnet, neither A nor B. It is what hosts the OPC UA proxy — not A, not B.
  2. No direct A ↔ B flow. All communication goes through the proxy, and each firewall allows only one unidirectional flow.
  3. The proxy aggregates: it reads the 12 variables from A and exposes them to B. If B makes an abnormal request, the proxy refuses — it does not “tunnel” through to A.
  4. Two separate firewalls in series — FW1 between Unit A and the DMZ, FW2 between the DMZ and Unit B. This is network defense in depth: compromising one firewall is not enough to reach the other unit.
  5. PROFINET stays inside each unit (PLC ↔ switch). PROFINET RT (layer 2) NEVER crosses an L3 firewall — it is OPC UA encapsulated over TCP/IP that bridges the zones.

Addressing plan and VLANs

SubnetVLANUseAllowed hosts
10.20.10.0/2410OT Unit A (PROFINET, S7)CPU A, HMI A, distributed sensors A
10.20.20.0/2420OT Unit B (PROFINET, S7)CPU B, HMI B, distributed sensors B
10.20.99.0/2499Industrial DMZOPC UA proxy, jump server, NDR
10.20.100.0/24100Switch/firewall managementAdmin console only

The VLANs are configured on the SCALANCE XC208 switches with a trunk towards the SCALANCE SC632-2C. No inter-VLAN routing at the switch level — the firewall decides.

Step-by-step configuration

Step 1 — Enable the OPC UA Server on CPU A (TIA Portal V19+)

In the properties of the S7-1517F CPU:

  1. Protection & Security → Connection mechanisms: uncheck Permit access with PUT/GET communication. (We disable legacy S7 — we will go ONLY through OPC UA.)
  2. OPC UA → Server → General: enable Activate OPC UA server. Port 4840 (standard).
  3. OPC UA → Server → Security:
    • Security policy: Basic256Sha256 — Sign and Encrypt only. Uncheck all the None and Sign only policies.
    • User authentication: uncheck Allow guest access. Check Allow authentication via user name and password + enable the user list.
    • Create a dedicated user, e.g. opcua-proxy-readonly with a strong password stored in a vault (Vault, CyberArk).
  4. OPC UA → Server → Server interfaces: create an interface that exposes ONLY the 12 required variables (and nothing else). Create a custom object type containing those 12 variables.

The Allow-list principle: publish only what is explicitly necessary. If you publish the entire CPU memory, the proxy gets far too much visibility.

Step 2 — Install the proxy’s X.509 certificate on CPU A

# Generate the CSR on CPU A (TIA Portal)
TIA Portal → CPU A → Certificate Manager →
  Generate certificate signing request (CSR) →
  Subject: CN=cpu-A-opcua, O=Raffinerie, C=FR

# The CSR is signed by the internal PKI (Microsoft AD CS, HashiCorp Vault,
# or Siemens SIMATIC Logon Service Center).

# Import the signed certificate onto CPU A and the proxy's certificate
# into the CPU's "Trusted certificates" store.

Always mutual certificates. The proxy authenticates the CPU, the CPU authenticates the proxy. Annual renewal automated via the PKI.

Step 3 — Configure the OPC UA proxy (Siemens SIMATIC OPC UA aggregating server)

On the IPC547G in the DMZ, install the SIMATIC OPC UA Aggregating Server (equivalent alternatives: Kepware KEPServerEX, Matrikon UA Tunneller).

# Aggregator configuration (simplified excerpt)
endpoints:
  # Endpoint exposed on the B side (interface 10.20.99.10)
  - name: server-for-unit-b
    url: opc.tcp://10.20.99.10:4840
    security: Basic256Sha256
    auth: certificate-only
    allowed_clients:
      - cn=cpu-B-opcua,o=Raffinerie  # ONLY B's certificate is accepted

  # Client towards CPU A (egress 10.20.99.10 → 10.20.10.10)
upstream:
  - name: cpu-a
    url: opc.tcp://10.20.10.10:4840
    security: Basic256Sha256
    user: opcua-proxy-readonly
    password_vault: vault://kv/opc-ua/cpu-a

  # Mapping: what is read on the A side is exposed on the B side
  variables:
    - source: "ns=3;s=UnitA.T_Coupe1"
      target: "Variables/TCoupe1"
      access: read-only
    - source: "ns=3;s=UnitA.T_Coupe2"
      target: "Variables/TCoupe2"
      access: read-only
    # ... 10 more

Three key points:

  • read-only on every exposed variable. Even if B is compromised, it cannot write into A.
  • certificate-only authentication: no password to steal on the B side.
  • Logging enabled: every session, every read, is logged to the SIEM (via syslog to Splunk / Wazuh).

Step 4 — Configure the two SCALANCE SC632-2C

The SCALANCE SC is an industrial L3 firewall that speaks OPC UA natively (stateful protocol inspection). We deploy two separate firewalls in series — a single round-trip per flow, a single allow rule per firewall.

# ═══════════════════════════════════════════════════════════════
# FW1 — between Unit A (eth0 = 10.20.10.1) and DMZ (eth1 = 10.20.99.1)
# ═══════════════════════════════════════════════════════════════
# Only allowed flow: DMZ proxy ──► CPU A (OPC UA read)
allow  src 10.20.99.10  dst 10.20.10.10  tcp/4840  proto OPC-UA-secure
allow  src 10.20.10.10  dst 10.20.99.10  established              # responses
deny   src any          dst any          log

# ═══════════════════════════════════════════════════════════════
# FW2 — between DMZ (eth0 = 10.20.99.2) and Unit B (eth1 = 10.20.20.1)
# ═══════════════════════════════════════════════════════════════
# Only allowed flow: CPU B ──► DMZ proxy (OPC UA client session)
allow  src 10.20.20.10  dst 10.20.99.10  tcp/4840  proto OPC-UA-secure
allow  src 10.20.99.10  dst 10.20.20.10  established              # responses
deny   src any          dst any          log

Two effective allow rules (one per firewall), everything else is denied and logged to the SIEM. This is the principle of network least privilege.

Immediate validation tests:

  • From any workstation in Unit A, ping 10.20.20.10 (CPU B) → timeout: FW1 blocks any egress not explicitly authorized. ✓
  • From any workstation in Unit B, ping 10.20.10.10 (CPU A) → timeout: double block by FW2 then FW1. ✓
  • From the DMZ proxy, ping 10.20.10.10OK: the FW1 rule allows it.
  • From the DMZ proxy, ping 10.20.20.10timeout: the proxy NEVER needs to initiate towards B (it is B that calls the proxy). FW2 blocks.

If any of these 4 tests does not give the expected result, there is a hole in the deny rule — to be fixed BEFORE going to production.

Step 5 — Enable OPC UA Deep Packet Inspection

The SCALANCE SC offers a DPI option that inspects the OPC UA content on top of the TCP header. Benefits:

  • Rejects Write requests even when the port and IP are authorized
  • Rejects requests to unauthorized nodes (NodeID allow-list)
  • Detects massive browsing attempts (attacker enumeration)
# OPC UA DPI policy applied on the SC632
methods_allowed: [Read, Browse, TranslateBrowsePath]
methods_denied:  [Write, Call, AddNodes, DeleteNodes]
namespace_allow: ["ns=3;s=UnitA.*"]   # only our allow-list
rate_limit:      "100 req / s"        # brute-force browsing detection

Step 6 — Enable the NDR / OT-IDS probe

A Dragos, Nozomi or Claroty CTD is connected on a mirror port of the SCALANCE XC208 on the DMZ side. It:

  • Builds the automatic inventory (CPU A, CPU B, proxy)
  • Learns the OPC UA traffic baseline over 7 days
  • Alerts on any deviation: new client, new variable read, new temporal pattern

The OT IDS is what detects a Triton in time. Indispensable under NIS2 (article 21 § 2 b).

Step 7 — Test and validate

TestMethodExpected result
Read a variable from BOPC UA Client block in CPU B → reads UnitA.T_Coupe1Correct value, latency < 200 ms
Direct ping CPU B → CPU Aping 10.20.10.10 from an IPC in VLAN 20Timeout (denied by firewall)
Write attempt from BOPC UA Write call to a variable in ABadUserAccessDenied
Massive browsing attemptScript that browses 10,000 nodes in 1 sDPI block beyond 100 req/s, SIEM alert
Fake client certificateConnection with a non-imported self-signed certBadCertificateUntrusted

Cyber checks to perform afterwards

  • IDS inventory: CPU A, CPU B and the proxy appear correctly. No unexpected device.
  • Centralized OPC UA logs: 100% of sessions are seen on the SIEM side, with timestamp and identity.
  • Proxy outage test: if the proxy goes down, B keeps running on its last known value (fallback logic in the CPU B program).
  • Certificate rotation: a PKI cron renews the certificates 30 days before expiry.
  • Backup: the IPC547G proxy image is backed up air-gapped quarterly.
  • Written incident plan: if A is compromised, who isolates the proxy? Procedure ≤ 15 min.
  • Vendor audit: Siemens maintenance goes through a PAM jump server (Wallix), not direct access to the proxy.

Classic pitfalls

  1. “Just for this test” — you add an allow any any firewall rule for 5 minutes to debug. You forget it. It stays for 3 years. Always open a ticket and remove the rule after debugging.
  2. Self-signed certificate on the proxy in production. It works, but the warning is ignored by operators who end up clicking on anything. Always a PKI-signed certificate.
  3. Shared opcua-proxy-readonly account across all proxies. If one proxy is compromised, the password allows reading the other CPUs. One account per (proxy, CPU) pair.
  4. No fallback logic on the B side: if the proxy goes down, B’s control loop receives Bad values and trips to safety — an unplanned shutdown. Always implement if quality != Good then use_last_known_value.
  5. DMZ “for now” on the same VLAN as IT. The industrial DMZ is a DEDICATED zone — neither IT nor OT. If you share it with IT, NotPetya can jump into it.
  6. Routed PROFINET: NEVER route PROFINET RT between VLANs. L2 multicast does not survive. PROFINET stays internal to each unit, OPC UA does the bridging.

Going further

  • The OT Cybersecurity hub covers the Purdue model and the multi-site interconnection section in detail.
  • The PLC hub details the Siemens S7-1500F CPUs and their OPC UA capabilities.
  • The Industrial networks hub compares PROFINET, OPC UA and the other protocols.
  • The IEC 62443 page gives the full normative framework for this architecture.

One last thing: what we just did looks like over-engineering the first time you see it. Four firewall interfaces, a proxy, certificates, DPI… Compare it to the cost of a Triton on your hydrocracker. The conversation is quick.