Runbook: substation-rtu¶
Discovery¶
A sweep of the DMZ from contractors-gate turns up port 8080 on 10.10.5.14 alongside the other services on the segment. A direct probe reveals what is listening.
root@contractors-gate:~# curl -s http://10.10.5.14:8080/
{"endpoints":["GET /datapoints list all","GET /datapoints/<id> read one","POST /datapoints/<id> write one (body: {\"value\": ...})"],"feeder":"Dolly Sisters / Nap Hill","rtu":"uupl-substation"}
A REST API for a substation RTU. It names the feeder segment and documents its own interface. Nothing in the response mentions authentication.
Datapoint inventory¶
root@contractors-gate:~# curl -s http://10.10.5.14:8080/datapoints
Six datapoints appear: four measured floating-point values and two boolean breaker states.
ID |
Name |
Type |
Unit |
|---|---|---|---|
1 |
feeder_a_voltage |
13 |
kV |
2 |
feeder_b_voltage |
13 |
kV |
3 |
load_current |
13 |
A |
4 |
frequency |
13 |
Hz |
5 |
breaker_a_state |
1 |
(empty, true/false) |
6 |
breaker_b_state |
1 |
(empty, true/false) |
Type 13 is M_ME_NC_1 (measured floating-point) and type 1 is M_SP_NA_1 (single-point boolean). Both are standard IEC-104 type identifiers. The values are live: uupl-eng-ws polls the relay IEDs and the turbine PLC every ten seconds and pushes updates here via the REST API. Voltages reflect the LV bus scaled through the step-up transformer (220 V → 11.0 kV). Breaker states track the relay trip coils (true = closed = not tripped).
Reading a single datapoint¶
root@contractors-gate:~# curl -s http://10.10.5.14:8080/datapoints/4
{"id":4,"name":"frequency","type":13,"unit":"Hz","value":48.3}
Any of the six IDs work. The value is live, derived from the turbine PLC’s frequency register.
Writing values¶
The same endpoint accepts POST with a JSON body.
root@contractors-gate:~# curl -s -X POST http://10.10.5.14:8080/datapoints/4 \
-H 'Content-Type: application/json' \
-d '{"value": 47.2}'
{"id":4,"status":"ok","value":47.2}
The value is committed immediately. Any IEC-104 master connected to port 2404 receives a spontaneous update at once, without waiting for the next periodic report. A GET confirms the write:
root@contractors-gate:~# curl -s http://10.10.5.14:8080/datapoints/4
State is in-memory. Injected values persist until the next update from uupl-eng-ws, which fires every ten seconds and overwrites all six datapoints with fresh readings from the relay IEDs and PLC. A container restart resets values to rtu_config.json defaults until the first update cycle.
Correlated manipulation¶
Changing a single reading may look like a sensor fault. A more interesting inconsistency: set feeder_a_voltage to zero while breaker_a_state stays true (closed).
root@contractors-gate:~# curl -s -X POST http://10.10.5.14:8080/datapoints/1 \
-H 'Content-Type: application/json' \
-d '{"value": 0.0}'
An operator watching a SCADA display now sees a breaker that reads as closed on a dead feeder. In a real feeder segment, that combination points to a protection failure or a sensor fault rather than an injection. The data model imposes no consistency rules between related datapoints.
IEC-104 connection¶
Port 2404 carries the IEC 60870-5-104 protocol. The REST surface and the IEC-104 interface read from the same underlying data model: a write through REST appears as a spontaneous update on the IEC-104 side at once.
root@contractors-gate:~# nc -zv 10.10.5.14 2404
The connection succeeds. An IEC-104 client pointed at 10.10.5.14:2404, common address 20, sends STARTDT and receives a general interrogation response containing all six datapoints.
What you can know now¶
Access:
REST API at
10.10.5.14:8080: read and write all six datapoints, no authenticationIEC-104 at
10.10.5.14:2404: reachable from the DMZ, common address 20
Datapoints:
feeder_a_voltage (ID 1, kV), feeder_b_voltage (ID 2, kV)
load_current (ID 3, A), frequency (ID 4, Hz)
breaker_a_state (ID 5, bool), breaker_b_state (ID 6, bool): 1 = closed, 0 = open
Quick reference¶
root@contractors-gate:~# curl -s http://10.10.5.14:8080/datapoints list all datapoints
root@contractors-gate:~# curl -s http://10.10.5.14:8080/datapoints/4 read one by ID
root@contractors-gate:~# curl -s -X POST http://10.10.5.14:8080/datapoints/4 \
-H 'Content-Type: application/json' -d '{"value": 47.2}' write a value
root@contractors-gate:~# nc -zv 10.10.5.14 2404 IEC-104 port