Authorization

Who gets to use the relay

Every relay connection is authenticated before it is admitted, and admission is decided by NervesCloud rather than by the relay itself.

The handshake

  1. 1

    The device proves its key

    Before anything else, the relay handshake makes the endpoint prove it holds the private key for the public key it claims, by signing either keying material exported from the TLS session or a challenge the relay issues. The relay does this for every connection, authorized or not, so the identity that reaches the next step cannot be spoofed.

  2. 2

    The relay asks NervesCloud

    The relay knows the endpoint is who it says it is, but not whether it is one of ours. So it calls this service with the endpoint id and waits for a verdict. No policy lives on the relay itself, which is why access can be changed without redeploying one.

  3. 3

    NervesCloud answers with a fabric

    We look the endpoint up and reply with a decision and, for a known device, the organisation it belongs to. That name is what the relay uses to keep one customer's devices from reaching another's.

  4. 4

    The connection is admitted or refused

    An admitted endpoint can now be reached through the relay by others in its fabric. A refused one is disconnected during the handshake and never registered, so it cannot send a single packet.

The access hook

Each relay is configured with the URL of this service and a bearer token that identifies it to us. It makes one request per incoming connection, before that connection is registered.

POST /relay-access HTTP/1.1
Host: iroh.nervescloud.com
X-Iroh-NodeId: c8924b6c9b7a8528b1365ebec4b2e43b6edebef684f8521f12b8caaf6e1b2302
Authorization: Bearer <the relay's token>

A 200 carrying allow: true admits the endpoint. A denial refuses it. So does a bad status, or no answer at all.

HTTP/1.1 200 OK
Content-Type: application/json

{"allow": true, "fabric": "org-1234"}

Fabrics

Not deployed yet. The relays currently admit endpoints without applying fabric isolation. The fabric field is already part of the contract above so that nothing has to change here when it is.

A fabric is a name the relay compares verbatim when deciding whether to forward a datagram: two endpoints exchange traffic only if they were admitted into the same one. We use organisation ids, so one customer's devices cannot reach another's through a relay, even if an endpoint id leaks. A packet that would cross fabrics is dropped exactly as one addressed to an endpoint that is not connected, so it cannot be used to probe whether a device is online.

An endpoint admitted without a fabric is unrestricted, reaching every fabric and reachable from all of them. That is reserved for NervesCloud's own infrastructure.

What this does not cover

It admits endpoints, not conversations

The check runs once per relay connection and answers "may this endpoint use the relay". It is not asked again per peer, so pairing rules between two specific devices are not something a relay can enforce.

Direct connections never reach us

Once hole punching succeeds, traffic flows straight between the two devices and no relay is involved. Any rule that must hold on every connection has to be enforced by the device accepting it, checking the remote endpoint id itself.

Revocation applies at the next connection

Removing an endpoint stops it being admitted from then on, and a live connection can be dropped, but a device that is already connected is not interrupted by the change alone.