diff --git a/src/pages/manage/reverse-proxy/index.mdx b/src/pages/manage/reverse-proxy/index.mdx index e6dc9f23..d43da645 100644 --- a/src/pages/manage/reverse-proxy/index.mdx +++ b/src/pages/manage/reverse-proxy/index.mdx @@ -53,11 +53,11 @@ The service mode determines how the proxy handles traffic between clients and yo | Mode | Layer | Description | |------|-------|-------------| | **HTTP** | L7 | TLS termination at the proxy, HTTP-level forwarding. Supports path-based routing, host header forwarding, redirect rewriting, and browser-based authentication (SSO, password, PIN). | -| **TCP** | L4 | Raw TCP relay. The proxy accepts TCP connections on a dedicated port and forwards them to your backend. | -| **UDP** | L4 | UDP relay with session tracking. The proxy accepts UDP packets on a dedicated port and forwards them to your backend. Sessions are reaped after an idle timeout. | +| **TCP** | L4 | Raw TCP relay. The proxy accepts TCP connections on the service's listen port and forwards them to your backend. | +| **UDP** | L4 | UDP relay with session tracking. The proxy accepts UDP packets on the service's listen port and forwards them to your backend. Sessions are reaped after an idle timeout. | | **TLS** | L4 | TLS passthrough with SNI-based routing. The proxy inspects the TLS ClientHello to read the SNI hostname and forwards the encrypted connection to your backend without terminating TLS. | -L4 services (TCP, UDP, TLS) listen on a dedicated port on the proxy cluster. Depending on the cluster, the port may be auto-assigned or you can specify one manually. The proxy cluster's `supports_custom_ports` capability determines whether manual port selection is available. +L4 services (TCP, UDP, TLS) listen on a port on the proxy cluster, and several services can share a port. TCP and UDP services can have their port auto-assigned or, on clusters that support custom ports (`supports_custom_ports`), chosen by you. TLS services always need a port you choose. See [Port allocation for L4 services](#port-allocation-for-l4-services) and [How services share ports](#how-services-share-ports). L4 services do not support browser-based authentication (SSO, password, PIN) or header authentication because there is no HTTP layer. You can use [access restrictions](/manage/reverse-proxy/authentication#access-restrictions) (IP CIDR, country, and CrowdSec rules) to protect L4 services. @@ -360,15 +360,17 @@ Within a service, you can: ## Port allocation for L4 services -L4 services (TCP, UDP, TLS) require a dedicated port on the proxy cluster. How ports are assigned depends on whether the proxy cluster supports custom port selection: +L4 services (TCP, UDP, TLS) listen on a port on the proxy cluster. How ports are assigned depends on whether the proxy cluster supports custom port selection: - **Custom port selection available**: you choose the exact port the proxy listens on. This is useful when clients expect a well-known port (e.g., 5432 for PostgreSQL, 3306 for MySQL). Self-hosted proxy clusters support this when configured to allow it. -- **Auto-assigned ports only**: the proxy cluster automatically assigns an available port. This is the case for NetBird's shared cloud proxy clusters, where port allocation is managed to avoid conflicts between accounts. The assigned port is shown in the service details after creation. +- **Auto-assigned ports only**: the proxy cluster automatically assigns an available port to TCP and UDP services. This is the case for NetBird's shared cloud proxy clusters, where port allocation is managed to avoid conflicts between accounts. The assigned port is shown in the service details after creation. -The dashboard indicates whether a proxy cluster supports custom ports when you select the domain. If custom ports are not supported, the listen port field is read-only and populated after creation. +TLS services are the exception: they always need a listen port that you choose, including on clusters without custom port selection, because TLS services share ports by SNI hostname. Creating a TLS service without a listen port fails. + +The dashboard indicates whether a proxy cluster supports custom ports when you select the domain. If custom ports are not supported, the listen port field of a TCP or UDP service is read-only and populated after creation. For a TLS service, the field stays editable. - Each port on a proxy cluster can only be used by one service at a time. If you specify a port that is already in use by another service, creation will fail. L4 listen ports also cannot conflict with the proxy's tunnel port. + A port on a proxy cluster can be shared by any number of TLS services, told apart by their SNI hostname, plus at most one TCP service and at most one UDP service. The TCP service receives every connection that matches no TLS service (or, on the main port, no HTTP service). Creating a TCP or UDP service fails if the port already has one of that protocol, and creating any service fails if another service already uses its domain. L4 listen ports also cannot conflict with the proxy's tunnel port. @@ -376,7 +378,7 @@ The dashboard indicates whether a proxy cluster supports custom ports when you s - **Self-hosted Docker deployments:** The default Docker Compose configuration only routes port 443 (via Traefik TLS passthrough) to the proxy container. L4 services that listen on additional TCP or UDP ports require you to manually expose those ports in your `docker-compose.yml`. See the [migration guide](/selfhosted/migration/enable-reverse-proxy#exposing-l4-ports) for instructions. + **Self-hosted Docker deployments:** The default Docker Compose configuration only routes port 443 (via Traefik TLS passthrough) to the proxy container, where the proxy's main port is `8443` (`NB_PROXY_ADDRESS`). A TLS service given listen port `443` in such a deployment therefore listens on a custom port inside the container, not on the main port. L4 services that listen on additional TCP or UDP ports require you to manually expose those ports in your `docker-compose.yml`. See the [migration guide](/selfhosted/migration/enable-reverse-proxy#exposing-l4-ports) for instructions. ### How services share ports @@ -387,17 +389,19 @@ The proxy's main port always runs an SNI router that peeks at the TLS ClientHell - Each incoming connection is matched against configured domains. - If the SNI hostname matches an **HTTP** service, the connection is forwarded to the HTTP reverse proxy for L7 handling. - If the SNI hostname matches a **TLS** service, the encrypted connection is passed through directly to the backend without TLS termination. -- If the SNI hostname does not match any service, the connection falls through to the HTTP reverse proxy, which returns an error since no matching service exists. -- Connections with no SNI (e.g., TLS 1.3 with Encrypted Client Hello) follow the same fallback path. +- If the SNI hostname does not match any service, the connection goes to the TCP service on that port if there is one, and otherwise falls through to the HTTP reverse proxy, which returns an error since no matching service exists. +- Connections with no SNI, and connections that are not TLS at all, follow the same fallback path. - Do not configure an HTTP and a TLS service with the same hostname. The HTTP route takes priority and the TLS service becomes unreachable. Use different domain names for each. + A domain can belong to only one service, so an HTTP and a TLS service cannot share a hostname: creating the second one fails with `domain already taken`. Use a different domain for each. -**On custom ports**, TLS and TCP services can coexist: +**On custom ports**, any number of TLS services and at most one TCP service can share the same port: - TLS connections are matched by SNI and passed through to the backend. - Connections that do not match any SNI route (or are not TLS at all) fall back to the TCP relay. - This is useful for running a TLS passthrough alongside a plain TCP catch-all on the same port. +- If the port has no TCP service, connections that match no TLS service are dropped. +- HTTP services are served only on the main port. On a custom port, a request for an HTTP service's hostname matches no route, so it goes to the TCP service if there is one and is dropped otherwise. ## Path-based routing