Obtain Source IP using NGINX Ingress Controller
Version Supported
This document is based on Cloud Container Engine version 1.21.
More details about the differences between Shared and Dedicated Loadbalancer :
https://docs.prod-cloud-ocb.orange-business.com/usermanual/elb/elb_pro_0004.html
Introduction
In this document we will explain how to conserve source IP address of a client using NGINX Ingress Controller and different type of Elastic LoadBalancer on Flexible Engine.
You can find more information’s about default CCE Ingress in the Help Center.
Using Shared Elastic LoadBalancer
First we will deploy our NGINX Ingress Controller and automatically provide a Shared ELB with following values.yaml file :
controller:
replicaCount: 1
service:
externalTrafficPolicy: Local
annotations:
kubernetes.io/elb.class: union
kubernetes.io/elb.autocreate:
'{
"type": "public",
"bandwidth_name": "cce-bandwidth-1551163379627",
"bandwidth_chargemode": "traffic",
"bandwidth_size": 5,
"bandwidth_sharetype": "PER",
"eip_type": "5_bgp",
"name": "test"
}'
Details about parameters :
externalTrafficPolicy: Local
This parameter is very important to avoid source NAT by default, you can find more details in Kubernetes documentation :
kubernetes.io/elb.class: union
This parameter specifies which type of loadbalancer it will provided
union = Shared Elastic LoadBalancer
performance = Dedicated Loabalancer
Deploy with Helm
Once it is done, you can now deploy your Helm Chart with your customized values.yaml file :
helm upgrade -f values.yaml --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
Wait up to a minute for the EIP and the Loadbalancer to connect.
You can see that IP address or FQDN with the following command :
kubectl get svc -n ingress-nginx
You can see that the EIP of the ELB is associated to the NGINX Controller with the following result :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.247.234.164 10.0.21.169,90.84.177.26 80:31360/TCP,443:32340/TCP 16h
ingress-nginx-controller-admission ClusterIP 10.247.198.139 <none> 443/TCP 16h
Edit Loadbalancer Listeners
In Flexible Engine Console select Elastic LoadBalancer menu
Edit as following :
- Edit your previously created ELB (test)
- Select Listeners
- Edit NGINX Listerners (k8s_TCP_443)
- Select Obtain Client IP Address
- Edit the second Listener as previsouly (k8s_TCP_80) and also Select Obtain Client IP Address
Using Dedicated Elastic LoadBalancer (beta)
First we will deploy our NGINX Ingress Controller and automatically provide a Dedicated ELB with following values.yaml file :
controller:
replicaCount: 1
service:
externalTrafficPolicy: Local
annotations:
kubernetes.io/elb.class: performance
kubernetes.io/elb.autocreate:
'{
"type":"public",
"bandwidth_name":"cce-bandwidth-1655142139284",
"bandwidth_chargemode":"traffic",
"bandwidth_size":5,
"bandwidth_sharetype":"PER",
"eip_type":"5_bgp",
"available_zone": ["eu-west-0a", "eu-west-0b"],
"l4_flavor_name": "L4_flavor.elb.s1.small"
}'
Details about parameters :
externalTrafficPolicy: Local
This parameter is very important to avoid source NAT by default, you can find more details in Kubernetes documentation :
kubernetes.io/elb.class: performance
This parameter specifies which type of loadbalancer it will provided
union = Shared Elastic LoadBalancer
performance = Dedicated Loabalancer
Deploy with Helm
Once it is done, you can now deploy your Helm Chart with your customized values.yaml file :
helm upgrade -f values.yaml --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
Wait up to a minute for the EIP and the Loadbalancer to connect.
You can see that IP address or FQDN with the following command :
kubectl get svc -n ingress-nginx
You can see that the EIP of the ELB is associated to the NGINX Controller with the following result :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.247.234.164 10.0.21.169,90.84.177.26 80:31360/TCP,443:32340/TCP 16h
ingress-nginx-controller-admission ClusterIP 10.247.198.139 <none> 443/TCP 16h
Loadbalancer Listeners
Obtain Client IP Address is activated by default on Dedicated LoadBalancer so you do not need to change anything here unlike to Shared ELB (see previously).
Testing
We can now deploy a simple echo service to verify that everything is working. The service will use the mendhak/http-https-echo image, a very useful HTTPS echo Docker container for web debugging.
First, copy the next manifest to a echo.yaml file:
apiVersion: v1
kind: Namespace
metadata:
  name: echo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-deployment
  namespace: echo
  labels:
    app: echo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echo
  template:
    metadata:
      labels:
        app: echo
    spec:
      containers:
      - name: echo
        image: mendhak/http-https-echo
        ports:
        - containerPort: 80
        - containerPort: 443
---
apiVersion: v1
kind: Service
metadata:  
  name: echo-service
  namespace: echo
spec:
  selector:
    app: echo
  ports:  
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: echo-ingress
  namespace: echo
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - http:
      paths:
        - path: "/"
          pathType: Prefix
          backend:
            service:
              name: echo-service
              port:
                number: 80
And deploy it on your cluster:
kubectl apply -f echo.yaml
Get echo ingress public IP :
kubectl -n echo get ingress echo-ingress
Now you can test it using the ingress public IP :
curl xxx.xxx.xxx.xxx
And you should get the HTTP parameters of your request, including the right source IP in the x-forwarded-for header:
{
  "path": "/",
  "headers": {
    "host": "xxx.xxx.xxx.xxx",
    "x-request-id": "76f9a0fb30c12b9549c2d8c48b7debd8",
    "x-real-ip": "XXX.XXX.XXX.XXX",
    "x-forwarded-for": "XXX.XXX.XXX.XXX",
    "x-forwarded-host": "xxx.xxx.xxx.xxx",
    "x-forwarded-port": "80",
    "x-forwarded-proto": "http",
    "x-original-uri": "/",
    "x-scheme": "http",
    "user-agent": "curl/7.68.0",
    "accept": "*/*"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "xxx.xxx.xxx.xxx",
  "ip": "x.x.x.x",
  "ips": [],
  "protocol": "http",
  "query": {},
  "subdomains": [],
  "xhr": false,
  "os": {
    "hostname": "echo-deployment-b97d6c86f-hl44p"
  }
}
You can double check your Public IP and verify if it match the x-forwarded-for field :
curl ifconfig.me