Skip to content

[Infrastructure] Real client IPs on self-hosted installs (ServiceLB hides the source address) #717

Closed

Description

@lukepolo

The api never sees a visitor's real address unless the request came through Cloudflare's proxy.

What happens

  • traefik is exposed through k3s ServiceLB (svclb-traefik, klipper-lb), with externalTrafficPolicy: Local.
  • klipper-lb masquerades. Inside traefik, every TCP peer is the svclb pod's own address: prod showed 10.42.0.45 on every connection to :8443.
  • So traefik's X-Forwarded-For entry and the api's req.ip (Express trust proxy 1) are the svclb pod for every visitor.
  • Behind Cloudflare's proxy, the api reads CF-Connecting-IP instead, so those hosts get real addresses. Every other install and host (no Cloudflare, DNS-only hosts, game servers' HTTP and WebSocket) gets none.

Effect

  • ThrottlerBehindProxyGuard keys on CF-Connecting-IP. Without that header the tracker is undefined, so every user of an install shares one bucket per route. The auth routes are 10 a minute for the whole install.
  • Telemetry, clip view dedupe (viewerKey) and match-event log lines record the same shared address.
  • CF-Connecting-IP can be forged by anyone who reaches the origin directly. The api can't check that a request came from Cloudflare's IP ranges, because it never sees the connecting address.

To do

  1. Keep the source address at the gateway. Options to compare: a ServiceLB setting if one exists; traefik with hostPort and the Service as ClusterIP; traefik on hostNetwork; MetalLB. For each, cover existing installs, downtime while switching, port conflicts, multi-node behaviour, IPv6, MediaMTX and TURN, and REVERSE_PROXY installs.
  2. Then the api trusts CF-Connecting-IP and CF-IPCountry only when the connecting address is in Cloudflare's ranges. This is already written on the unpushed branch fix/client-ip-from-cloudflare in the api repo, with tests. It must not ship before step 1: on today's gateway it would put every user into one bucket.
  3. Installs behind a Cloudflare Tunnel or their own reverse proxy: decide which proxy addresses to trust.

Activity

  1. lukepolo commented on Nov 4, 2026

    @lukepolo
    ContributorAuthor

    Fixed by #731 (Traefik binds the host's ports, so backends see each visitor's address instead of ServiceLB's pod) and 5stackgg/api#1081 (installs without Cloudflare get a rate limit per visitor instead of sharing one). Behind Cloudflare, CF-Connecting-IP is still read as before; to make it unforgeable, let only Cloudflare reach the panel's ports or use a tunnel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions