Currently, the relays eBPF module only supports routing from IPv4 to IPv4 as well as IPv6 to IPv6. In general, TURN servers can also route from IPv4 to IPv6 and vice versa. Our userspace routing supports that but doing the same in the eBPF code is a bit more involved. We'd need to move around the headers a bit more (IPv4 and IPv6 headers are different in size), as well as configure the respective "source" address for each interface. Currently, we simply take the destination address of the incoming packet as the new source address. When routing across IP versions, that doesn't work. To gain some more insight into how often this happens, we add these additional maps and populate them. This allows us to emit a dedicated log message whenever we encounter a packet for such a mapping. First, we always do check for an entry in the maps that we can handle. If we can't we check the other map and special-case the error. Otherwise, we fall back to the previous "no entry" error. We shouldn't really see these "no entry" errors anymore now, unless someone starts probing our relays for active channels.
relay
This crate houses a minimalistic STUN & TURN server.
Features
We aim to support the following feature set:
- STUN binding requests
- TURN allocate requests
- TURN refresh requests
- TURN channel bind requests
- TURN channel data requests
Relaying of data through other means such as DATA frames is not supported.
Building
You can build the relay using: cargo build --release --bin firezone-relay
You should then find a binary in target/release/firezone-relay.
Running
The Firezone Relay supports Linux only. To run the Relay binary on your Linux host:
- Generate a new Relay token from the "Relays" section of the admin portal and save it in your secrets manager.
- Ensure the
FIREZONE_TOKEN=<relay_token>environment variable is set securely in your Relay's shell environment. The Relay expects this variable at startup. - Now, you can start the Firezone Relay with:
firezone-relay
To view more advanced configuration options pass the --help flag:
firezone-relay --help
Ports
By default, the relay listens on port udp/3478. This is the standard port for
STUN/TURN. Additionally, the relay needs to have access to the port range
49152 - 65535 for the allocations.
Portal Connection
When given a token, the relay will connect to the Firezone portal and wait for
an init message before commencing relay operations.
Metrics
The relay parses the OTLP_GRPC_ENDPOINT env variable.
Traces and metrics will be sent to an OTLP collector listening on that endpoint.
It is recommended to set additional environment variables to scope your metrics:
OTEL_SERVICE_NAME: Translates to theservice.name.OTEL_RESOURCE_ATTRIBUTES: Additional, comma-separated key=value attributes.
By default, we set the following OTEL attributes:
service.name=relayservice.namespace=firezone
The docker-init-relay.sh script integrates with GCE.
When OTEL_METADATA_DISCOVERY_METHOD=gce_metadata, the service.instance.id
variables is set to the instance ID of the VM.
Design
The relay is designed in a sans-IO fashion, meaning the core components do not cause side effects but operate as pure, synchronous state machines. They take in data and emit commands: wake me at this point in time, send these bytes to this peer, etc.
This allows us to very easily unit-test all kinds of scenarios because all inputs are simple values.
The main server runs in a single task and spawns one additional task for each allocation. Incoming data that needs to be relayed is forwarded to the main task where it gets authenticated and relayed on success.