Cambium

Documentation

Cambium Server

Proxies and Networking

If you are setting up Cambium on a personal server for one game team or company, you can disregard this section. If you are in a situation where you need to have multiple organizations exist on a single server, or serve connections over plaintext, read ahead.

Serving plaintext

Setting disable_tls: true serves plain HTTP. This should be done when your Cambium server is behind a reverse proxy or entirely on a local network. You should never serve plain HTTP if your server is exposed to the open internet.

With disable_tls: true and no bind_address of your own, the server listens on 127.0.0.1 only and is unreachable from anywhere else, which is the configuration needed for a reverse proxy. Putting plaintext on a public interface takes two deliberate settings.

# A backend behind a proxy: correct, and unreachable except through it.
disable_tls: true

# Plaintext across a LAN you control: possible, but you have to say so.
disable_tls: true
bind_address: 0.0.0.0

The banner tells you which of the three states you are in every time the server starts: the certificate fingerprint when TLS is on, a quiet confirmation when plaintext is confined to loopback, and a loud warning when it is not.

Several servers, one machine

A Cambium server owns a port. Two servers mean two ports, and everyone has to know which one is theirs. A reverse proxy replaces that with names: one address, one port, one certificate, and each team reaches its own server by hostname.

This works because the hostname travels with the request twice — once in the TLS handshake, so the proxy knows which certificate to present, and again in the HTTP Host header once the connection is up. A single listener can read the name and route on it.

                                    ┌─► 127.0.0.1:9001   organization1
client ──► 1.2.3.4:443 ─► proxy ────┼─► 127.0.0.1:9002   organization2
                                    └─► 127.0.0.1:9003   organization3

Each Cambium server changes almost nothing: it still listens on one port and still behaves as though it were the only one. It just moves to loopback and stops doing its own TLS. Cambium servers still stay one-per-organization.

Nginx configuration

Repeat the upstream and server pair for each Cambium server sharing the proxy, changing the name, the certificate and the port.

upstream cambium_gameteam {
    server 127.0.0.1:9001;
    keepalive 16;
}

server {
    listen 443 ssl;
    server_name gameteam.example.com;

    ssl_certificate     /etc/letsencrypt/live/gameteam.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/gameteam.example.com/privkey.pem;

    client_max_body_size 1024m;

    location / {
        proxy_pass http://cambium_gameteam;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 120s;
    }
}

Note:

LineWhy it matters
client_max_body_size 1024mnginx defaults to 1 MB and answers anything larger with a 413 before Cambium sees it, so submits fail with an error that does not come from Cambium at all. Set it above your largest file; 0 means unlimited.
keepalive + proxy_http_version 1.1Without the upstream block and these two lines, nginx opens a new connection to Cambium for every single request — it defaults to HTTP/1.0 upstream and keeps no pool. Cambium is built around keep-alive, so leaving them out costs you most of it.
X-Forwarded-ForHow Cambium learns who the real client is. Without it every user looks like the proxy — see below.

Caddy configuration

Caddy needs almost nothing: it sets the forwarding headers itself, keeps an upstream connection pool by default, has no body-size limit to raise, and obtains certificates automatically.

gameteam.example.com {
    reverse_proxy 127.0.0.1:9001
}

filmteam.example.com {
    reverse_proxy 127.0.0.1:9002
}

Telling Cambium about your proxy

Behind a proxy every request arrives from the proxy, so without being told otherwise Cambium sees one client where you have twenty. That matters most for the login rate limit: it locks an address out after five failed attempts, and if every user shares one address then one person mistyping their password locks out the whole team.

The fix is trusted_proxies. List your proxy, and Cambium will read the real client address from the X-Forwarded-For header it sets.

trusted_proxies: 127.0.0.1

Leave it empty on a directly-reachable server. An empty list means Cambium ignores the header entirely and treats the connecting address as the client, which is what you want when there is no proxy, otherwise anyone could set the header themselves and sidestep the rate limit.

The web portal

Setting enable_web_portal: true serves a browser-facing page on the same port as the API. That split works the same whether or not a proxy is in front, which is why the configurations above route everything to one backend and let Cambium sort it out.

It follows organization features. The portal is browser access to the organization surface — availability, shared builds, team activity — so turning those off takes the portal with it, whatever this setting says. In the Administration page the browser switch sits directly under the organization one and dims when it is off.

With the portal off, a browser hitting the port gets nothing at all: no page, no error, just a closed connection. That is deliberate — an error page would carry a Server header naming the product and its version, which is free information for anyone scanning. It does not hide the server (the port is still open, and TLS still presents a certificate), it just stops it introducing itself to people who have no business here.

One thing to expect: a browser visiting a server that uses Cambium's own self-signed certificate gets a full-page security warning first. The desktop client checks the fingerprint instead, so it never sees one; a browser has no equivalent. In practice the portal belongs on a deployment with a real certificate, which is the same deployment that wants a proxy.

Checking it works

From a machine that is not the server:

  1. curl https://gameteam.example.com/api/server should return JSON naming that server. If two hostnames return the same name, the proxy is routing both to one backend.
  2. Connect the desktop client to the hostname, with no port. It should not ask you to verify a fingerprint — a certificate from a public authority is accepted automatically.
  3. Submit a file comfortably larger than a megabyte. A failure here is almost always the proxy's body limit rather than Cambium.
  4. Get one account's password wrong five times, then log in correctly as a different account from a different machine. The second login should work. If it is refused, trusted_proxies is not set and everyone is sharing one rate-limit bucket.

For monitoring, a plain TCP connection check is harmless, but an HTTPS request to /api/server tells you far more: it exercises the certificate, the proxy, the route and the server in one go.

When something is wrong

What you seeUsually means
Clients cannot reach the server after an upgrade, and the banner says plaintext on 127.0.0.1disable_tls: true now implies loopback. Set bind_address if you meant to serve the network directly.
Submits fail on larger files, smaller ones workThe proxy's request body limit. On nginx raise client_max_body_size.
One person's bad password locks everyone outtrusted_proxies is unset, so every user shares the proxy's address.
Every hostname reaches the same serverThe proxy is not routing on Host, or both blocks point at one port.
The browser warns about the certificateThe server is using its own self-signed certificate. Expected — use a CA-issued one for anything a browser touches.
The server will not start: address already in useAnother Cambium process still holds the port. Stop it before starting a new one.