Overview
Voltrus Remote gives support engineers secure remote access to industrial PLC networks — without TeamViewer and without ad-hoc VPNs punched through the OT firewall.
A lightweight gateway enrols into a self-hosted WireGuard mesh (Headscale). The support engineer tunnels in and reaches the PLC subnet directly, so native engineering tools — TIA Portal, GX Works, SoMachine — talk to the PLC exactly as if they were on the local network.
Architecture
support engineer gateway (OT side) PLC
┌────────────┐ ┌──────────────┐ 192.168.10.0/24
│ client │◄── WireGuard ─►│ agent + WG │────────────► PLC @ .5
│ (--accept- │ mesh │ (advertise │ S7 TCP102 / Modbus TCP502
│ routes) │ UDP/443 or │ subnet) │
└─────┬──────┘ DERP └──────┬───────┘
│ │ HTTPS heartbeat
│ enrol (preauthkey) ▼
│ ┌─────────────────┐
│ │ control-api (Go)│ Voltrus auth, grants,
│ │ /health /nodes │ billing hooks, audit
│ └────────┬────────┘
│ │ Bearer /api/v1
│ ▼
└────── dashboard ───►┌─────────────────┐
(Next.js) │ Headscale │ WireGuard control plane
│ (self-hosted) │ pubkey registry + ACL
└─────────────────┘
Components
- Headscale — the WireGuard control plane. Holds pubkeys, enforces ACLs, issues preauthkeys, and runs the DERP relay (the fallback path through restrictive OT firewalls).
- agent (gateway, Go) — enrols via
tailscale up --login-server=<headscale>, advertises only the PLC subnet(s) behind it, and heartbeats liveness to the control API. - control-api (Go) — the Voltrus backend. Wraps the Headscale HTTP API and adds device health, access grants, and (later) billing, multi-tenancy, and audit.
- dashboard (Next.js) — gateway inventory and live status for the support engineer / admin.
Installation
Voltrus Remote ships as a Docker Compose stack: Headscale + control-api + dashboard. The gateway agent is a single Go binary deployed on the Linux machine at the edge of the OT network.
Control plane (your server / VPS)
cp .env.example .env
docker compose up -d headscale
docker exec voltrus-headscale headscale users create default
docker exec voltrus-headscale headscale apikeys create # -> HEADSCALE_API_KEY
echo "VOLTRUS_DEVICE_TOKEN=$(openssl rand -hex 32)" # -> .env
docker compose up -d
# dashboard: http://localhost:3000
# control-api: http://localhost:8081
Gateway agent (OT edge, Linux)
The agent drives the official tailscale client at your Headscale — it does not reimplement WireGuard crypto. It needs NET_ADMIN to manage the tunnel interface.
# enrol a gateway and advertise the PLC subnet behind it
./voltrus-remote-agent enrol \
--hostname siteA-gw \
--routes 192.168.10.0/24
Quick Start
- Bring up the control plane on a server reachable from the OT network over outbound HTTPS/UDP.
- Enrol a gateway on the Linux machine at the OT edge. It advertises only the specific PLC CIDR (e.g.
192.168.10.0/24). - Connect as the support engineer with
--accept-routes. Your engineering laptop now has a route to the PLC subnet. - Open your engineering tool (TIA Portal, GX Works, SoMachine) and point it at the PLC's local IP — e.g.
192.168.10.5. S7 on TCP/102 or Modbus on TCP/502 reach the PLC directly over the tunnel.
Configuration
Subnet advertisement
The gateway advertises only the PLC CIDR you specify — not the whole corporate LAN. This is enforced both at the agent and by the Headscale ACL. A site with PLCs at 192.168.10.0/24 advertises exactly that prefix.
ACL policy (default-deny)
Headscale enforces a policy.hujson ACL. The default stance is deny: an engineer can reach only the RFC1918 PLC subnets explicitly listed, never the wider corporate LAN or the public internet through the tunnel.
DERP relay fallback
When symmetric NAT prevents a direct WireGuard path, traffic falls back to the Headscale DERP relay over TCP/443 — the port almost every OT firewall already allows outbound for HTTPS. Direct UDP/41641 is preferred when reachable; DERP is the safety net.
Security Model
| Threat | Mitigation |
|---|---|
| PLC exposed to the internet | Outbound-only agent. No inbound ports opened on the OT firewall. |
| Engineer reaches corp LAN / internet | ACL is default-deny; destinations limited to the advertised PLC subnets. |
| Lateral movement across sites | Each subnet router advertises only its specific PLC CIDR. |
| Private key theft | The WireGuard private key never leaves the device. Headscale holds the public key only. |
| Standing access | Roadmap: time-boxed grants (e.g. 2h), default-deny between sessions. |
Crypto is not reimplemented — the agent drives the audited Noise/WireGuard implementation in the official tailscale client.
Troubleshooting
Gateway shows online but PLC unreachable
Symptom: The dashboard shows the gateway healthy, but ping 192.168.10.5 fails from the engineer client.
Fix: The engineer client must connect with --accept-routes, or it will not install the advertised subnet route. Then confirm the advertised CIDR actually covers the PLC IP and that the ACL permits the engineer's identity to that subnet.
Tunnel only works over DERP (no direct path)
Symptom: Traffic flows but latency is high; logs show derp relay.
Fix: Symmetric NAT on the OT side is blocking UDP/41641. DERP over TCP/443 is the intended fallback. If you need a direct path, allow outbound UDP/41641, or lower MTU on the gateway interface (try 1280) to rule out fragmentation.
Enrolment fails (preauthkey rejected)
Symptom: tailscale up against the Headscale login server errors out.
Fix: Confirm the preauthkey is valid and unused, the agent's clock is correct (Noise handshake is time-sensitive), and the gateway can reach the Headscale control URL over HTTPS.
Engineering tool can discover but not connect
Symptom: TIA Portal sees the PLC but the connection drops.
Fix: Some protocols open additional ephemeral ports. Confirm the PLC's full service port set (S7 = TCP/102; Modbus = TCP/502) is within the advertised subnet and permitted by the ACL. Check MTU — S7 connection setup is sensitive to fragmentation.
Roadmap
Voltrus Remote is in active development. The MVP delivers reach-PLC; the rest follows.
- MVP (now): reach-PLC over a self-hosted WireGuard mesh. Linux gateway.
- Next: time-boxed access grants with expiry scheduler and default-deny between sessions.
- Then: session recording / audit (pcap on gateway, or RustDesk recording) and screen-share.
- Later: billing, multi-tenant isolation, Windows HMI agent, and an on-prem (air-gapped) Headscale option.
Support
- Email: support@voltrus.id
- Product page: /voltrus-remote/
Voltrus Remote is pre-launch. To join the early-access cohort for your deployment, reach out directly.