Skip to main content

Securing a deployment

VRPC's access control is the broker's: who may connect, and which topics each connection may publish and subscribe. Every request is a topic that names agent, class, instance and function, so topic permissions decide who may call what. This guide shows how to set a deployment up so that they do.

1. Use your own broker, with TLS​

The default broker, broker.hivemq.com, is public: anybody can read and publish there. Use it to try VRPC, never for real data.

Connect over TLS (mqtts://, or wss:// from a browser) and have agents and clients verify the broker's certificate:

const fs = require('fs')
const tls = { ca: fs.readFileSync('/etc/ssl/broker-ca.pem') }

const agent = new VrpcAgent({ domain: 'factory', agent: 'line-1', broker: 'mqtts://broker.example.com:8883', tls })
const client = new VrpcClient({ domain: 'factory', broker: 'mqtts://broker.example.com:8883', tls })

Without the tls option, VRPC does not verify the certificate. Pass tls: { rejectUnauthorized: true } to verify against the system's certificate authorities.

2. Authenticate every connection​

Give agents and clients credentials the broker checks:

PeerWith tokenWith username and password
agentusername <domain>/<agent>, password the tokenas given
clientusername <domain>:client@<host>-<platform>-js, password the tokenas given

An agent without any credentials connects with a password derived from its host. That is no credential: do not let a broker accept it.

A client whose token expires can switch to a new one without dropping its connection, client.updateCredentials({ token }) (used on the next reconnect), or reconnect with it at once, client.reconnectWithToken(token).

3. Authorize by topic​

Confine every connection to the topics it needs. The protocol specification lists them; in short:

PeerMay publishMay subscribe
clientrequests <domain>/<agent>/<class>/<instance>/<function> it may call; its presence <domain>/<own MQTT client id>/+/__clientInfo__<domain>/<own MQTT client id>/#; agent and class info
agent<domain>/<own name>/#; answers and events to connections<domain>/<own name>/#; client presence <domain>/+/+/__clientInfo__

Two of these matter more than the rest:

  • A client may subscribe only under its own MQTT client id. A client's answers, callbacks and events travel on topics under its connection id, <domain>/<mqttClientId>/<secret>. A broker that confines each client to topics under its own MQTT client id keeps them private - and with them the connection id, which is a capability: whoever knows it can act as that connection towards agents.
  • Narrow the request topics. A client that may publish to <domain>/line-1/Press/+/readPressure can read pressures and nothing else. Wildcards in permissions follow the topic levels: agent, class, instance, function.

4. Offer only what may be called​

An agent calls exactly what its class info lists, nothing else - not emit, not a private helper, not a function an object acquires at run time (see Offering classes). Turn that around when you design a class: every published function is callable by everyone who may publish to its topic.

  • Keep functions that must stay internal private (_name), or out of the registered class.
  • Use isolated instances for state that belongs to one connection; nobody else can reach them.
  • Validate arguments in your functions: VRPC passes JSON as it came. Constructor arguments can be validated with a schema (Offering classes).

5. What VRPC protects on its own​

  • An agent takes the target of a request from its topic, which the broker authorized, never from its payload.
  • An agent answers only a sender that is a connection id of its own domain, and publishes events only to topics under the subscriber's connection id: no request can make it publish elsewhere.
  • Subscription ids are never listed to anybody; listings of registrations show labels only.
  • An isolated instance answers its owner's connection alone.

Reporting a vulnerability​

See SECURITY.md.