TLS & mTLS
Server TLS
qumo requires TLS 1.3 for its QUIC listener, configured via CERT_FILE /
KEY_FILE (see Configuration → Server
for the full variable reference).
For local development, generate a browser-trusted cert with mkcert:
mkcert -install
mkcert -cert-file certs/server.crt -key-file certs/server.key localhost 127.0.0.1 ::1
qumo relay(qumo playground needs no manual cert — it generates and trusts its own dev
certificate automatically.)
Relay peers (optional)
Relays authenticate each other with mutual TLS under a relay CA, a CA you run. Public certificates such as Let’s Encrypt’s are for browsers and other clients; they play no part in relay identity.
- Inbound: a native-QUIC session that presents a certificate
CA_FILEverifies is a relay peer when the certificate carries the peering namepeer.qumo.internal. A peer is served without a credential. A certificate without the name grants nothing: its session needs a credential like any client’s. A browser is never asked for a certificate. - Outbound: a relay dials each address in
PEERSpresenting its own peer certificate (PEER_CERT_FILE,PEER_KEY_FILE), asks for the server namepeer.qumo.internal, and verifies the certificate the peer answers with againstCA_FILEonly. The dialed relay answers that name with its peer certificate, not its public one. SoPEERSmay name peers by any address: a Consul name, an IP. - Itself: a relay whose
PEERSresolve to it (a group name) recognizes its own certificate on that session, drops it, and doesn’t retry.
What a peer certificate must contain
Issue one per relay from the relay CA:
- a DNS subject alternative name
peer.qumo.internal(the peering name); - the subject common name set to the relay’s identity, such as its node name; the relay logs it and never interprets it;
- extended key usages for both client authentication and server authentication, since a relay both dials and is dialed;
- a validity the relay is within; it refuses to start with an expired one.
A certificate from the same CA without the peering name grants nothing.
Whatever holds a peer certificate is trusted like a relay, so issue one only
to what runs under the same control as the relays. The HLS egress can connect
with one (RELAY_CERT_FILE), or with a signing key instead
(RELAY_SIGNING_KEY). Each holder needs a certificate of its own: a relay
drops a session that presents the relay’s own certificate, as itself.
With OpenSSL:
# The relay CA, once.
openssl ecparam -genkey -name prime256v1 -noout -out relay-ca.key
openssl req -x509 -new -key relay-ca.key -sha256 -days 3650 \
-subj "/CN=relay CA" -out relay-ca.crt
# One peer certificate per relay (here relay-1).
openssl ecparam -genkey -name prime256v1 -noout -out relay-1.key
openssl req -new -key relay-1.key -subj "/CN=relay-1" -out relay-1.csr
printf 'subjectAltName=DNS:peer.qumo.internal\nextendedKeyUsage=clientAuth,serverAuth\n' > relay-1.ext
openssl x509 -req -in relay-1.csr -CA relay-ca.crt -CAkey relay-ca.key -CAcreateserial \
-days 365 -sha256 -extfile relay-1.ext -out relay-1.crtThen on each relay: CA_FILE=relay-ca.crt, PEER_CERT_FILE=relay-1.crt,
PEER_KEY_FILE=relay-1.key, and PEERS for the relays it dials. Any CA
tooling works the same way (step-ca, Vault PKI). For development, mage cert
writes a relay CA and peer certificates (PEER_NAMES=a,b mage cert).
Issuing, renewing, distributing and revoking these certificates is the operator’s; the relay only reads the files.
- One certificate per relay. A relay recognizes itself by its own certificate. Two relays given the same one each take the other for themselves, drop the session without an error, and never peer.
- Renewal needs a restart. The relay reads its peer certificate once, at startup. Renew it on disk and restart the relay before the old one expires; a relay still running on an expired certificate can neither dial nor be dialed.
- A chain is fine. When an intermediate CA issues the certificate, put
the intermediate after the leaf in
PEER_CERT_FILEand the root inCA_FILE. - Revocation lists are not checked. A compromised certificate stays valid until it expires or the CA is replaced.