RustShield Modbus Gateway is a Rust proof of concept for stateful semantic filtering of Modbus TCP traffic in a local lab.
The gateway runs as a transparent TCP proxy between a simulated SCADA/client and a simulated PLC. It parses Modbus TCP frames, observes read responses, keeps a small in-memory view of process state, and blocks a protected Write Single Coil command when the observed state is unknown, stale, or unsafe.
This public showcase is a local-lab PoC. It demonstrates:
- strict Modbus TCP MBAP/PDU framing,
- transparent forwarding of allowed Modbus TCP ADUs,
- passive observation of
Read Holding Registers(0x03) andRead Input Registers(0x04) responses, - in-memory state freshness checks,
- configurable semantic policy decisions for
Write Single Coil(0x05), - fail-safe blocking for unknown/stale state,
- local read-only health/metrics,
- reproducible loopback evidence with a synthetic PLC simulator.
It does not provide:
- production OT security,
- certification or compliance,
- cryptographic authentication,
- TLS/VPN/segmentation,
- Modbus RTU support,
- real PLC validation,
- high availability,
- protection against all malicious clients, servers, or network attacks.
Do not deploy this PoC in a production OT network, on a real PLC, or in any environment where physical process safety depends on it. The included validation uses only loopback sockets and synthetic Modbus TCP frames.
| Component | Address |
|---|---|
| Gateway SCADA listener | 127.0.0.1:15020 |
| Simulated PLC | 127.0.0.1:5020 |
| Read-only metrics | 127.0.0.1:14600 |
The default lab policy protects Coil 0x0001 from being switched ON (0xFF00) unless Input Register 0x0001 for unit_id=1 has been observed recently and is at or below the safe threshold 90.
Implemented policy IDs:
UNKNOWN-STATE-001: block protected writes if required state is missing or stale.BOILER-SAFETY-001: block protected writes if the critical value is above threshold.SAFE-STATE-ALLOW-001: allow protected writes when state is fresh and safe.UNSUPPORTED-WRITE-001: block known unsupported write functions fail-safe.
The gateway validates TOML configuration for loopback-only transport, read-only metrics, and policy parameters. Defaults match the lab policy above.
[tcp]
listen_scada = "127.0.0.1:15020"
plc_addr = "127.0.0.1:5020"
[logging]
level = "info"
payload_logging = false
[metrics]
enabled = true
readonly_bind = "127.0.0.1:14600"
[security]
critical_unit_id = 1
critical_register_kind = "Input"
critical_register_address = 1
protected_coil = 1
max_safe_value = 90
state_max_age_ms = 5000metrics.enabled = false disables the local observability listener. When metrics
are enabled, metrics.readonly_bind is required and must remain loopback.
From this directory:
python3 scripts/phase4_e2e_evidence.pyThe script:
- runs
cargo fmt,cargo test,cargo check, andcargo clippy --all-targets --all-features -- -D warnings; - starts a synthetic PLC simulator on loopback;
- starts the real gateway;
- attempts a protected write before state is known and verifies it is blocked;
- reads a safe value and verifies a protected write is forwarded;
- reads an unsafe value and verifies a protected write is blocked;
- stores logs, metrics, command output, and a JSON summary under
evidence/.
Canonical local evidence included in this showcase:
evidence/phase4-20260510T052804Z/: fresh showcase-local end-to-end stateful semantic filtering regeneration.evidence/phase4-20260510T050510Z/: end-to-end stateful semantic filtering.evidence/phase2-20260510T043718Z/: proxy and parser validation.
cargo fmt --check
cargo test
cargo check
cargo clippy --all-targets --all-features -- -D warnings
cargo audit
cargo deny checkGitHub Actions CI runs these checks on push and pull_request.
| Path | Purpose |
|---|---|
SECURITY.md |
Vulnerability reporting scope for the lab PoC |
.github/workflows/ci.yml |
Public Rust quality gates |
src/config.rs |
Validated TOML runtime configuration |
src/modbus_codec.rs |
Strict Modbus TCP ADU extraction and MBAP validation |
src/transport.rs |
Async TCP proxy, state observation, policy enforcement |
src/industrial_state.rs |
In-memory observed register cache and freshness checks |
src/security_filter.rs |
Pure policy decisions for write commands |
src/observability.rs |
Local read-only health and metrics endpoint |
scripts/phase4_e2e_evidence.py |
Reproducible loopback evidence script |
docs/publication-review.md |
Local pre-publication checklist and sanitization notes |
This showcase is licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.