Kubernetes ingress-nginx preserve source IP

I have a vm that sits in front of the cluster. Currently it is running HAProxy (with use-proxy-protocol: "true"). My end goal is to allow the pods associated with the default backend to be able to read the actual source client source IP.

Here's a sample log of with use-proxy-protocol turned on:

10.244.0.0 - [10.244.0.0] - - [10/Jan/2018:23:06:42 +0000] "GET /platform/ping HTTP/1.1" 200 16 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" 367 0.002 [upstream-default-backend] 10.244.3.101:80 16 0.002 200
10.244.0.0 - [10.244.0.0] - - [10/Jan/2018:23:06:59 +0000] "GET /platform/ping HTTP/1.1" 200 16 "-" "curl/7.54.0" 91 0.074 [upstream-default-backend] 10.244.3.101:80 16 0.074 200
10.244.0.0 - [10.244.0.0] - - [10/Jan/2018:23:09:51 +0000] "PROXY TCP4 127.0.0.1 127.0.0.1 43088 80" 400 173 "-" "-" 0 0.001 [] - - - -
10.244.0.0 - [10.244.0.0] - - [10/Jan/2018:23:09:59 +0000] "PROXY TCP4 127.0.0.1 127.0.0.1 43092 80" 400 173 "-" "-" 0 0.001 [] - - - -
10.244.0.0 - [10.244.0.0] - - [10/Jan/2018:23:10:09 +0000] "PROXY TCP4 127.0.0.1 127.0.0.1 43096 80" 400 173 "-" "-" 0 0.002 [] - - - -
I0110 23:11:42.050971       5 controller.go:211] backend reload required
I0110 23:11:42.054732       5 event.go:218] Event(v1.ObjectReference{Kind:"ConfigMap", Namespace:"ingress-nginx", Name:"nginx-configuration", UID:"7539f546-f599-11e7-bee6-fa163e2f1153", APIVersion:"v1", ResourceVersion:"127044", FieldPath:""}): type: 'Normal' reason: 'UPDATE' ConfigMap ingress-nginx/nginx-configuration
I0110 23:11:42.138901       5 controller.go:220] ingress backend successfully reloaded...
127.0.0.1 - [127.0.0.1] - - [10/Jan/2018:23:11:56 +0000] "GET /platform/ping HTTP/1.1" 200 16 "-" "curl/7.47.0" 86 0.003 [upstream-default-backend] 10.244.3.101:80 16 0.003 200
142.xx.xxx.xx - [142.xx.xxx.xx] - - [10/Jan/2018:23:15:50 +0000] "GET / HTTP/1.1" 500 21 "-" "curl/7.47.0" 78 0.020 [upstream-default-backend] 10.244.3.101:80 21 0.020 500
142.xx.xxx.xx - [142.xx.xxx.xx] - - [10/Jan/2018:23:16:02 +0000] "GET /platform/bitcoin HTTP/1.1" 200 45 "-" "curl/7.47.0" 94 0.165 [upstream-default-backend] 10.244.3.101:80 45 0.165 200
216.249.49.20 - [216.249.49.20] - - [10/Jan/2018:23:16:16 +0000] "GET / HTTP/1.1" 500 21 "-" "curl/7.54.0" 78 0.002 [upstream-default-backend] 10.244.3.101:80 21 0.002 500
216.249.49.20 - [216.249.49.20] - - [10/Jan/2018:23:16:30 +0000] "GET /platform/bitcoin HTTP/1.1" 200 45 "-" "curl/7.54.0" 94 0.002 [upstream-default-backend] 10.244.3.101:80 45 0.002 200
216.249.49.20 - [216.249.49.20] - - [10/Jan/2018:23:16:43 +0000] "GET /platform/bitcoin HTTP/1.1" 200 45 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" 370 0.049 [upstream-default-backend] 10.244.3.101:80 45 0.049 200
216.249.49.20 - [216.249.49.20] - - [10/Jan/2018:23:16:44 +0000] "GET /favicon.ico HTTP/1.1" 404 9 "http://142.xx.xxx.xx/platform/bitcoin" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" 324 0.013 [upstream-default-backend] 10.244.3.101:80 9 0.013 404
216.249.49.20 - [216.249.49.20] - - [10/Jan/2018:23:17:04 +0000] "GET /platform/bitcoin HTTP/1.1" 200 45 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" 370 0.002 [upstream-default-backend] 10.244.3.101:80 45 0.002 200
216.249.49.20 - [216.249.49.20] - - [10/Jan/2018:23:17:07 +0000] "GET /platform/ping HTTP/1.1" 200 16 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" 367 0.002 [upstream-default-backend] 10.244.3.101:80 16 0.002 200
216.249.49.20 - [216.249.49.20] - - [10/Jan/2018:23:17:56 +0000] "GET /platform/ping HTTP/1.1" 200 16 "-" "curl/7.54.0" 91 0.002 [upstream-default-backend] 10.244.3.101:80 16 0.002 200
Logs from 1/10/18 10:17 PM to 1/10/18 11:17 PM UTC

142.xx.xxx.xx is the IP of the HAProxy vm

216.249.49.20 is an external IP coming from the university. As you can see, the ingress pod can read external IP's passed from HAProxy with use-proxy-protocol: "true" Just fine.

But when I curl the address of HAProxy vm, I get:

demonfuse@Williams-MacBook-Pro ~/N/K/NGINX> curl 142.xx.xxx.xx/platform/ping
pong2 10.244.2.6   

10.244.2.6 is the IP of the ingress pod. I am confident ingress-nginx at this point has the real source IP.

Is there a way to forward the headers and real source IP to pods behind ingress-nginx via configmaps? From what I can tell here it most of it should be turned on by default.

How to reproduce:

  1. Install ingress-nginx on brand new cluster following the guide here
  2. Redirect traffic from HAProxy / external load balancer to ingress-nginx
  3. Go script

as follows:

import (
        "github.com/kataras/iris"
        "github.com/kataras/iris/context"
        //...
    )

    func main() {
        app := iris.New()
            app.Get("/platform/ping", func(ctx context.Context) {
            fmt.Println("connected with " + ctx.RemoteAddr() + "!")
            ctx.WriteString("pong2 " + ctx.RemoteAddr())
        })

        //...

        app.Run(iris.Addr(":80"), iris.WithoutServerError(iris.ErrServerClosed))
    }

Additional info:

Environment: Internet -> Dedicated HAProxy VM -> Bare metal OVH K8S Cluster (1 master, 2 worker)

configmap.yaml

apiVersion: v1
data:
  proxy-set-headers: "ingress-nginx/custom-headers"
  use-proxy-protocol: "true"
kind: ConfigMap
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app: ingress-nginx

custom_headers.yaml

apiVersion: v1
data:
  X-Forwarded-For: "142.xx.xxx.xxx"
kind: ConfigMap
metadata:
  name: custom-headers
  namespace: ingress-nginx

haproxy config

global
   maxconn 4096
   log 127.0.0.1 local0 notice
   maxconn 2000
   user haproxy
   group haproxy

defaults
   log   global
   mode   http
   retries   3
   option redispatch
   maxconn   2000
   timeout connect 5000
   timeout client  50000
   timeout server  50000

frontend TestServerTest
    bind 142.xx.xxx.xxx:80
    mode tcp
    default_backend TestServernodes

backend TestServernodes
    mode tcp
    server TestServer01 142.xx.xxx.xxx:80 send-proxy

Where and how did I made a mistake?

I have attempted a combination of X-Forwaded-For with the internal ingress pod IP, the external IP associated with the ingress service, and the public IP of the HAProxy vm. So far curling the external IP of the HAProxy still returns pong2 10.244.2.6 (internal IP of the ingress pod)

1 answer

  • answered 2018-01-11 20:43 William Yang

    I figured it out! The problem lies in the Iris web framework and has little to nothing to do with ingress-nginx.

    The solution is to read the remote headers manually in ctx.Application().ConfigurationReadOnly().GetRemoteAddrHeaders(). By default the Iris framework does not check for X-Forwarded-For and X-Real-Ip

    Hopefully this will be useful for those running reverse proxies to and from Kubernetes.