Skip to content

ConceptsPreview

Transports

How Nardeboon wraps and disguises every packet, and how it picks a transport that works on your network.

Nardeboon is built for networks that filter, inspect and probe traffic. Every packet is encrypted with WireGuard, obfuscated, and then carried by one of three transports. There is no unobfuscated mode.

Layers

From the inside out:

LayerWhat it does
WireGuardEnd-to-end encryption between your devices. WireGuard itself is unmodified.
Nardeboon headerRouting and relay information: version, message type, hop limit, traffic class and relay handle.
ObfuscationDisguises everything inside it, using keys independent of WireGuard's.
TransportCarries the result across the network.

The obfuscation layer:

  • hides WireGuard's fixed bytes;
  • pads away the fixed sizes of WireGuard's handshake messages (148 and 92 bytes);
  • sends a limited amount of cover traffic before handshakes and while idle;
  • adds a small random delay to handshakes only, never to data;
  • is versioned, so a new version can ship quickly if the current one is ever detected.

The three transports

In order of preference:

TransportWhat it isWhen it's used
nb-udpRaw UDPThe default: fastest, lowest overhead, no head-of-line blocking.
nb-tlsA length-framed TLS 1.3 stream to a relay's front door, on a port of its own (never 443 or 80)Networks that block UDP completely. Accepts head-of-line blocking.
nb-wssWebSocket over TLSWhen the only way out is an HTTP proxy or a CDN. Highest overhead.

Over nb-tls and nb-wss, Nardeboon's own connections use BBR congestion control wherever the system allows it (Linux, which is where relays run), so random packet loss on a bad link slows them far less than it would ordinary TCP.

On nb-udp, a path that loses packets at random gets forward error correction (FEC): the receiving device measures the loss and asks for just enough parity, at most half as much again as the data, to rebuild what's lost, so the connections inside the tunnel keep their speed. Nothing waits for it: only a lost packet, rebuilt, arrives a little late. A clean path gets no parity at all.

How a transport is chosen

You don't need to choose. Each node:

  • detects which transports work on the current network, and uses the best one;
  • checks again whenever the network changes, for example from Wi-Fi to mobile data;
  • counts transport overhead in the path score, from lowest to highest: UDP, TLS, WSS.

The tunnel MTU never changes when the transport changes mid-connection, so a switch doesn't change the packet size your applications see.

Traffic between a node and the control plane uses the same obfuscated transports, and is indistinguishable from data traffic.

Networks that cut what a device starts

Some networks let in connections that start elsewhere, but cut the ones a device starts itself after a few packets; some also block certain addresses outright for connections started inside. Many datacenters in Iran work this way. A device on such a network finds out by itself: nardeboon netcheck shows it under Flows it starts, and nardeboon status says so in a line.

From then on, the device never starts a connection to a device somewhere else. It asks the other side to start it, and answers:

  • Peers call it. When it wants a peer, it asks through the control plane, with the address its peers see it at, and the peer reaches out to it.
  • Relays call it. Your relays, and Global Relays on plans that include them, greet it where it is, so it can relay through them even when its country blocks a relay's address.
  • The control plane calls it. It holds its own session with the control plane, which connects to it, so it doesn't depend on any other device to stay managed.
  • It knocks when it has no other way. After a restart, or when no other device can pass its request on, it sends one short message that the network lets through, coded so that only that device could have sent it, and the control plane has its peers and relays call it, and calls it back itself. A brand-new device asks the same way, and signs in on the call with its auth key.

Two devices on networks like this in the same country start connections to each other as usual. The device needs an address the other side can reach, which a server in a datacenter has; a device behind a home or mobile network's NAT can't be called, and a way out for those, through a CDN, is planned.

Probe resistance

Censors look for VPN servers by connecting to them and watching how they respond. With Nardeboon, the first bytes of a connection must prove knowledge of a secret. Anything else gets no answer:

  • On UDP, there is total silence.
  • On TLS transports, an unauthenticated visitor sees an ordinary website.
  • Recorded messages can't be replayed.
  • The secret isn't built into the app. The control plane's comes with a device's auth key, or once someone lets the device in; each relay's front door has its own, which only the devices allowed to use that relay learn, from the signed network map.

Downgrade protection

Each node's supported transports and obfuscation versions are published in the signed network map. Once a tunnel is up, both sides verify, inside the tunnel, that what's in use matches what was announced. On a mismatch, they close the connection and log an audit event, so no one in the middle can quietly force a downgrade.

Check transports from the CLI

nardeboon netcheck reports on the current network in a few seconds: NAT type, where the device is seen from, what the network does with the connections it starts, IPv6, the status of each transport, the relays it reaches and how far they are, port mapping, and whether you’re on battery, so it knows when to probe less.

$ nardeboon netcheck
NAT type:         unknown
Seen from:        203.0.113.40 (by peers)
Flows it starts:  cut after a few packets (the network lets in only what others start)
IPv6:             no
Port mapping:     -
On battery:       no

Transports:
  nb-udp     ok
  nb-tls     blocked
  nb-wss     blocked

Relays:
  Frankfurt (Global Relay)  reaching, 75 ms
  am-vps                    reaching, 78 ms
  tr-vps                    reaching, 41 ms

Here the device is on a network that cuts the connections it starts: its peers and relays call it, so it reaches all of them over nb-udp anyway, and its own nb-tls connections, which it would have to start, are cut.

To test a peer over a specific transport, pass --transport to nardeboon ping:

$ nardeboon ping --transport nb-tls hetzner
pong from hetzner (100.83.20.5): direct 203.0.113.9:47391, nb-tls, 36 ms

If no path to the peer works right now, nardeboon ping exits with code 10 (unreachable).