Clusterward
← Zurück zum Blog
BetriebAktualisiert Florian Apel

Warum Ihre App hinter dem Load Balancer die falsche IP sieht

Alle Besucher kommen von derselben IP, das Login-Limit sperrt alle Kunden gleichzeitig, und die Allowlist lässt niemanden rein. Das ist kein Angriff, das ist der Load Balancer.

Titelbild: Warum Ihre App hinter dem Load Balancer die falsche IP sieht

Der Fehler zeigt sich immer erst im Betrieb. Die Anwendung läuft, die Domain antwortet, das Zertifikat ist grün. Dann sperrt das Login-Limit nach dem dritten Kunden alle weiteren aus, oder die IP-Allowlist, die nur das Büro zulassen soll, lässt auch das Büro nicht rein. Ein Blick in die Logs: Jeder Request kommt von derselben privaten Adresse.

Was passiert

Ein Scaleway Load Balancer arbeitet auf Layer 4. Er nimmt die TCP-Verbindung des Besuchers an und öffnet eine neue Verbindung zum Node, auf dem ingress-nginx läuft. Aus Sicht von nginx ist der Absender der Load Balancer. Die Adresse des Besuchers ist weg, bevor der erste HTTP-Header gelesen wird.

X-Forwarded-For hilft hier nicht: Der Header wird von einem HTTP-Proxy gesetzt, und der Load Balancer ist keiner. Er reicht Bytes durch.

Die Lösung: Proxy-Protokoll

Das Proxy-Protokoll ist ein kleiner Header, den der Load Balancer vor die TCP-Verbindung stellt: Quelladresse, Quellport, Zieladresse, Zielport. nginx liest ihn, wenn use-proxy-protocol aktiv ist, und kennt ab dann die echte Adresse. Beide Seiten müssen mitspielen:

  • Der Load Balancer muss das Protokoll senden. Bei Scaleway steuert das die Annotation service.beta.kubernetes.io/scw-loadbalancer-proxy-protocol-v2 am Service des Ingress-Controllers; der Cloud Controller Manager konfiguriert daraufhin den Load Balancer.
  • Der Controller muss es lesen. Bei ingress-nginx ist das use-proxy-protocol: "true" in der ConfigMap, bei Envoy Gateway eine ClientTrafficPolicy.

Fehlt eine Seite, bricht die Verbindung: Sendet der Load Balancer den Header und nginx erwartet ihn nicht, sieht nginx Müll statt HTTP. Erwartet nginx den Header und der Load Balancer sendet ihn nicht, wartet nginx vergeblich. Deshalb ist der Umbau eine Neuinstallation des Controllers mit beiden Werten zugleich, und für einige Sekunden antwortet kein Host des Clusters.

Warum das mehr ist als ein Log-Problem

Ohne echte Adressen sind drei Dinge kaputt, die man normalerweise für selbstverständlich hält:

  1. Rate-Limits. Ein Login-Limit pro Adresse sieht eine Adresse für alle. Nach den ersten Fehlversuchen irgendeines Besuchers sind alle gesperrt.
  2. Allowlists. Eine Zugriffsliste auf Büro-Adressen sieht das Büro nie, weil jeder als Load Balancer ankommt.
  3. Audit und Forensik. Wer hat sich wann von wo angemeldet? Die Antwort ist immer dieselbe Adresse. Welche Nachweise ein Prüfer dazu sehen will, beschreibt Audit & NIS2.

Wir haben das live erlebt: Ein Cluster mit mehreren Kundenanwendungen und einer Control Plane, alle hinter einem Load Balancer ohne Proxy-Protokoll. Die Rate-Limits der Control Plane zählten alle Kunden in einen Topf. Seitdem ist das Proxy-Protokoll in Clusterward für neue Cluster voreingestellt, und für bestehende ist der Umbau eine typisierte Bestätigung auf der Setup-Karte, siehe Cluster-Provisioning.

Und hinter Cloudflare?

Wer Hosts über den Cloudflare-Proxy leitet, hat eine zweite Schicht. Der Load Balancer sieht jetzt Cloudflares Edge-Adresse, und die echte Adresse des Besuchers steht im Header CF-Connecting-IP. Das Proxy-Protokoll liefert also die Cloudflare-Adresse, und die Anwendung muss den Header lesen, aber nur, wenn die Verbindung wirklich von einer Cloudflare-Adresse kommt. Sonst kann jeder den Header fälschen und sich als beliebige Adresse ausgeben.

Die saubere Regel lautet: Nimm die Adresse aus dem Proxy-Protokoll. Ist sie eine der veröffentlichten Cloudflare-Adressen, nimm stattdessen CF-Connecting-IP. Sonst ignoriere den Header. Die Liste der Cloudflare-Adressen ändert sich selten, aber sie ändert sich; wer sie hart kodiert, hat irgendwann falsche Adressen. Clusterward lädt sie täglich nach und wendet diese eine Regel für Login-Limits, Allowlists und Audit-Einträge an, beschrieben unter Sicherheit und Zugriff.

Checkliste

  • Zeigen die Logs Ihrer Anwendung eine einzige Adresse für alle Besucher? Dann fehlt das Proxy-Protokoll.
  • Ist es am Load Balancer aktiv, aber nicht im Controller, oder umgekehrt? Dann antwortet gar nichts.
  • Laufen Hosts hinter Cloudflare? Dann braucht die Anwendung die Regel für CF-Connecting-IP, mit Prüfung der Absenderadresse.
  • Nutzen Sie IP-Beschränkungen pro Host? Die wirken nur mit echter Adresse und nur bei Hosts, die nicht über Cloudflare laufen.

So prüfen Sie es in fünf Minuten

  1. Rufen Sie eine Ihrer Anwendungen von außen auf und sehen Sie in ihr Zugriffslog. Steht dort Ihre öffentliche Adresse oder eine private aus dem Cluster-Netz?
  2. Prüfen Sie den Service des Ingress-Controllers: Trägt er die Proxy-Protokoll-Annotation für den Scaleway Load Balancer?
  3. Prüfen Sie die ConfigMap des Controllers auf use-proxy-protocol.
  4. Ist nur eines von beiden gesetzt, antwortet der Host gar nicht. Ist keines gesetzt, antwortet er, aber mit falscher Adresse.

Der Umbau selbst ist eine Neuinstallation des Controllers mit beiden Werten, in einem Zug. Planen Sie dafür einige Sekunden Unterbrechung für jeden Host des Clusters ein, und machen Sie es nicht am Freitagnachmittag.

Warum X-Forwarded-For trotzdem gesetzt wird

Sobald nginx die echte Adresse kennt, setzt es selbst X-Forwarded-For und X-Real-IP für die Anwendung dahinter. Die Anwendung liest also weiterhin den Header, wie sie es von jedem Reverse Proxy gewohnt ist. Der Unterschied liegt nur davor: Ohne Proxy-Protokoll steht im Header die Adresse des Load Balancers, mit Proxy-Protokoll die des Besuchers. Frameworks, die Proxies vertrauen, brauchen dafür eine Einstellung wie TRUST_PROXY oder trusted proxies, sonst ignorieren sie den Header aus Vorsicht.

Ein Sonderfall: Health-Checks

Der Load Balancer prüft seine Backends regelmäßig. Diese Prüfungen kommen ohne Proxy-Protokoll-Header, weil sie nicht von einem Besucher stammen. ingress-nginx erwartet den Header aber jetzt. Bei Scaleway ist das gelöst, weil der Health-Check auf einen eigenen Port geht, den der Controller ohne Proxy-Protokoll bedient. Wer den Health-Check auf den Traffic-Port legt, sieht Backends, die als tot gelten, obwohl sie laufen.

Noch ein Grund: Sperren nach Fehlversuchen

Seit Kurzem hängt noch mehr an der echten Adresse. Clusterward sperrt eine Adresse nach zu vielen fehlgeschlagenen Anmeldungen und meldet eine Anmeldung von einer neuen Adresse per E-Mail. Hinter einem Load Balancer ohne Proxy-Protokoll träfe die Sperre alle Nutzer auf einmal, und jede Anmeldung käme scheinbar von derselben Adresse. Mehr unter Sicherheit & Zugriff.

Fazit

Das Proxy-Protokoll ist eine Zeile Konfiguration, die man am ersten Tag setzt oder am schlechtesten Tag nachrüstet. Neue Cluster sollten es von Anfang an haben. Alles, was auf Adressen basiert, hängt daran.

Echte Client-IP in Ihrem Cluster? Schreiben Sie uns, welcher Load Balancer und welcher Ingress-Controller bei Ihnen laufen. Wir sagen Ihnen, was für die echte Adresse nötig ist. Frage zum Setup stellen →

Quellen und weiterführende Links

Häufige Fragen

  • Weil ein Scaleway Load Balancer auf Layer 4 arbeitet. Er nimmt die TCP-Verbindung des Besuchers an und öffnet eine neue Verbindung zum Node, auf dem ingress-nginx läuft. Für nginx ist deshalb der Load Balancer der Absender, und die Adresse des Besuchers ist weg, bevor der erste HTTP-Header gelesen wird.