Preserve client source IP

Hi !

TL;DR - I wan’t to use the IPWhiteList middleware but Traefik (as a k8s ingress controller) can’t read the client source IP address.

Here’s my configuration. K8s is installed on a Debian host with kubeadm:

kubeadm init --pod-network-cidr 10.244.0.0/16

I use Flannel:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

My Traefik ingress controller is exposed by a clusterIP service with an externalIP (here 172.28.128.7 is an example):

apiVersion: v1
kind: Service
metadata:
  name: traefik-svc
  labels:
    app: traefik

spec:
  selector:
    app: traefik
  ports:
    - name: web
      protocol: TCP
      port: 80
    - name: websecure
      protocol: TCP
      port: 443
  externalIPs:
    - 172.28.128.7

Everything's working fine. I can access to my services from IngressRoute etc. but I can't preserve the source client IP. I have installed a "WhoAmI" service and it returns the IP of the Traefik ingress controller as the source IP:

→ curl -v http://whoami.example.com/
*   Trying 172.28.128.7...
* TCP_NODELAY set
* Connected to whoami.example.com (172.28.128.7) port 80 (#0)
> GET / HTTP/1.1
> Host: whoami.example.com
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 402
< Content-Type: text/plain; charset=utf-8
< Date: Tue, 11 Feb 2020 13:35:58 GMT
<
Hostname: whoami-deployment-bd6b677dc-bsvst
IP: 127.0.0.1
IP: 10.244.0.5
RemoteAddr: 10.244.0.4:52864
GET / HTTP/1.1
Host: whoami.example.com
User-Agent: curl/7.64.1
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 10.244.0.1
X-Forwarded-Host: whoami.example.com
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: traefik-deployment-9cff6f74d-jrglg
X-Real-Ip: 10.244.0.1

Anyone can help? I'm not sure if the problem is Traefik/Flannel or k8s but I'm not able to make it works...

Thanks !

Here's my Traefik deployment configuration :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: traefik-deployment
  labels:
    app: traefik

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik

    spec:
      serviceAccountName: traefik
      containers:
        - name: traefik
          image: traefik:v2.1
          env:
            - name: NS1_API_KEY
              value: $NS1_API_KEY
          ports:
            - name: web
              containerPort: 80
            - name: websecure
              containerPort: 443
          args:
            - --api.dashboard
            - --log.level=info
            - --log.filepath=/traefik.log
            - --log.format=json
            - --accesslog.filepath=/access.log
            - --accesslog.format=json
            - --entrypoints.web.Address=:80
            - --entrypoints.websecure.Address=:443
            - --providers.kubernetescrd
            - --certificatesresolvers.ns1.acme.email=me@example.com
            - --certificatesresolvers.ns1.acme.storage=/certs/acme.json
            # - --certificatesresolvers.ns1.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
            - --certificatesresolvers.ns1.acme.dnschallenge.provider=ns1
          volumeMounts:
            - name: traefik-certs-volume
              mountPath: /certs
      volumes:
        - name: traefik-certs-volume
          persistentVolumeClaim:
            claimName: traefik-pvc

Hello @remyduthu

Thanks for using Traefik.

Seems that you should update the Kubernetes service by adding

externalTrafficPolicy: local

in order to preserve source IP addresses.

Here is the link to the detailed configuration on the Kubernetes website: Using Source IP | Kubernetes

Additionally, if you use external proxy in front of Traefik, than Traefik must also trust the X-Forwarded-* headers coming from it, which can be achieve by adding a forwardedHeaders config on the entrypoint,

See that link for more details: EntryPoints - Traefik

Hope that helps,
Jakub