X-Forwarded-For und Proxy-Protocol

14 Oktober, 2020

Achim Ledermüller
Achim Ledermüller
Senior Manager Cloud

Der Exil Regensburger kam 2012 zu NETWAYS, nachdem er dort sein Wirtschaftsinformatik Studium beendet hatte. In der Managed Services Abteilung ist er für den Betrieb und die Weiterentwicklung unserer Cloud- Plattform verantwortlich.

von | Okt. 14, 2020

Du willst wissen wie du in deinem Kubernetes Cluster an die IP-Adressen deiner Clients kommst? In fünf Minuten hast du den Überblick!

Mehr Hilfe bei der Konfiguration von Kubernetes notwendig?
Unsere MyEngineer® sind jederzeit für dich da!

Vom HTTP-Client zur Anwendung

Im Tutorial zum nginx-Ingress-Controller zeigen wir wie man eine Anwendung öffentlich erreichbar macht. Im Fall der NETWAYS Cloud bedient sich dein Kubernetes Cluster an einem Openstack-Loadbalancer, welcher die Client-Anfragen an einen nginx-Ingress-Controller im Kubernetes Cluster weitersendet. Dieser verteilt dann alle Anfragen an die entsprechenden Pods. Bei all dem Herumschieben und Weiterleiten der Anfragen gehen ohne weitere Konfiguration die Verbindungsdetails der Clients verloren. Da das Problem nicht erst seit Kubernetes auftritt, wird auf die alt bewährten Lösungen X-Forwarded-For oder Proxy-Protocol zurückgegriffen. Um im Buzzword-Bingo zwischen Service, Loadbalancer, Ingress, Proxy, Client und Anwendung nicht die Übersicht zu verlieren, kannst du dir im folgenden Beispiel den Weg einer HTTP-Anfrage vom Client bis zur Anwendung durch die Komponenten eines Kubernetes Clusters ansehen.

Client IP-Adressen mit X-Forwarded-For

Verwendest du HTTP, kann die Client IP-Adresse im X-Forwarded-For (XFF) gespeichert und weiter transportiert werden. XFF ist ein Eintrag im HTTP-Header und wird von den meisten Proxy-Servern unterstützt. Im Beispiel setzt der Loadbalancer dazu die Client-IP-Adresse in den XFF-Eintrag und schickt die Anfrage weiter. Alle weiteren Proxy-Server und die Anwendungen können dadurch im XFF-Eintrag erkennen, von welcher Adresse die Anfrage ursprünglich gesendet wurde. In Kubernetes konfiguriert man den Loadbalancer über Annotations im Service Objekt. Setzt man dort loadbalancer.openstack.org/x-forwarded-for: true wird der Loadbalancer entsprechend konfiguriert. Wichtig ist jetzt natürlich auch noch, dass der nächste Proxy den X-Forwarded-For Header nicht wieder überschreibt. Im Fall eines nginx kann man dazu die Option use-forwarded-headers in dessen ConfigMap setzen.

---
# Service
kind: Service
apiVersion: v1
metadata:
  name: loadbalanced-service
  annotations:
    loadbalancer.openstack.org/x-forwarded-for: "true"
spec:
  selector:
    app: echoserver
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
---
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx
data:
  use-forwarded-headers: "true"

Da der HTTP-Header verwendet wird, ist es nicht möglich HTTPS-Verbindungen mit der Client-IP-Adresse anzureichern. Hier muss man entweder das TLS/SSL-Protokoll am Loadbalancer terminieren oder auf das Proxy-Protocol zurückgreifen.  

Client-Informationen mit Proxy-Protocol

Verwendet man X-Forwarded-For ist man offensichtlich auf HTTP beschränkt. Um auch HTTPS und anderen Anwendungen hinter Loadbalancern und Proxys den Zugriff auf die Verbindungsoption der Clients zu ermöglichen, wurde das sogenannte Proxy-Protocol erfunden. Technisch wird dazu vom Loadbalancer ein kleiner Header mit den Verbindungsinformationen des Clients hinzugefügt. Der nächste Hop (hier nginx) muss natürlich ebenfalls das Protokoll verstehen und entsprechend behandeln. Neben klassischen Proxys unterstützten auch andere Anwendungen wie MariaDB oder postfix das Proxy-Protocol. Um das Proxy-Protocol zu aktivieren, musst du das Service Objekt mit der Annotation loadbalancer.openstack.org/proxy-protocol versehen. Für den annehmenden Proxy muss das Protocol ebenfalls aktiviert werden. 

---
# Service Loadbalancer
kind: Service
apiVersion: v1
metadata:
  name: loadbalanced-service
  annotations:
    loadbalancer.openstack.org/proxy-protocol: "true"
spec:
  selector:
    app: echoserver
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
---
# NGINX ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx
data:
  use-proxy-protocol: "true"

In den meisten Fällen wirst du aber auf das Helm-Chart des nginx-Ingress-Controller zurückgreifen. Dort ist eine entsprechende Konfiguration sogar noch einfacher.  

nginx-Ingress-Controller und Helm

Benutzt man Helm, um den nginx-ingress-controller zu installieren, ist die Konfiguration sehr übersichtlich. Das Proxy-Protocol wird über die eine Helm-Values-Datei sowohl für den nginx als auch für den Loadbalancer aktiviert:

nginx-ingress.values: 

---
controller:
  config:
    use-proxy-protocol: "true"
  service:
    annotations:
      loadbalancer.openstack.org/proxy-protocol: true
    type: LoadBalancer 

helm install my-ingress stable/nginx-ingress -f nginx-ingress.values

Ob alles so funktioniert wie erwartet, kannst du am einfachsten mit Hilfe des Google Echoserver testen. Dabei handelt sich um eine kleine Anwendung, die den HTTP-Request einfach an den Client zurück gibt. Wie im Tutorial zum nginx-Ingress-Controller beschrieben, benötigen wir dazu ein Deployment mit Service und Ingress. Ersteres startet den Echoserver, der Service macht diesen im Cluster erreichbar und der Ingress konfiguriert den nginx so, dass die Requests an das Deployment weitergeleitet werden. 

---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echoserver
spec:
  selector:
    matchLabels:
      app: echoserver
  replicas: 1
  template:
    metadata:
      labels:
        app: echoserver
    spec:
      containers:
      - name: echoserver
        image: gcr.io/google-containers/echoserver:1.8
        ports:
          - containerPort: 8080
---
# Service
apiVersion: v1
kind: Service
metadata:
  name: echoserver-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: echoserver
---
# Ingress
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: echoserver-ingress
spec:
  rules:
  - host: echoserver.nws.netways.de
    http:
      paths:
        - backend:
            serviceName: echoserver-svc
            servicePort: 80

Zum Testen fakest du am besten noch deine /etc/hosts damit echoserver.nws.netways.de auf die öffentlichen IP-Adresse deines nginx-Ingress-Controller deutet. curl echoserver.nws.netways.de zeigt dir dann alles an, was der Echoserver von deinem Client weiß, inklusive der IP-Adresse im X-Forwarded-For Header.  

Fazit

Im Kubernetes Cluster ist das Proxy-Protocol für die meisten Anwendungsfälle wohl die bessere Wahl. Die bekannten Ingress-Controller unterstützen das Proxy-Protocol und TLS/SSL-Verbindungen können im K8s-Cluster konfiguriert und terminiert werden. Welche Informationen bei Deiner Anwendung ankommen, findest du am schnellsten mit Googles Echoserver raus.

 

Unser Portfolio

0 Kommentare

Einen Kommentar abschicken

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Wie hat Dir unser Artikel gefallen?