My requests are being proxied through Cloudflare which sets a header indicating the country in a http header based on the IP address. I want to redirect the requests with certain paths based on this header in Nginx ingress controller. How do I do this?
- 
                    1Hello, could you clarify if you are trying to "rewrite" the request by a header directed to one svc or are you trying to route this requests to the different svc's by header? Please take a look here: https://stackoverflow.com/questions/26223733/how-to-make-nginx-redirect-based-on-the-value-of-a-header . Also as more of a pointer that a solution, Traefik and Ambassador have an option to run header based routing. – Dawid Kruk Aug 04 '20 at 13:19
 - 
                    Thank you @Dawid. I’m looking to rewrite requests to bunch of SVCs based on header. The answer suggested in that question is how I’m doing it currently in Nginx. But was wondering how to achieve it in `Ingress` resource definitions. Looks like I need to define a map with redirect URLs in the `http-snippet` config in the Nginx Ingress Controller and then use either `nginx.ingress.kubernetes.io/configuration-snippet` or `nginx.ingress.kubernetes.io/server-snippet` in the `Ingress resource` to do the redirection. Didn’t know Traefik and Ambassador have this option. Thanks, will check them out. – Sudarshan Murthy Aug 05 '20 at 13:00
 
1 Answers
Currently Ingress resource definition for nginx-ingress does not support header based routing.
I found a workaround to route a request by it's header (I've included the steps below) with following annotation:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($http_LocationHeader = "PL") { proxy_pass http://goodbye-service.default.svc.cluster.local:5678; }
Other possible solutions/workarounds:
- Use 
Traefik: Docs.traefik.io: Routing: Routers - Use 
Ambassador: Getambassador.io: Docs: Using: Headers - Use 
Istio: Istio.io: Traffic management: Request routing - Route the traffic from 
nginx-ingressto nginx pod/deployment/daemonset with header based routing logic included: Stackoverflow.com: How to have a header routing logic with nginx ingress-controller? (last comment under the answer) 
As for a workaround:
Assuming that (for example purposes):
- There are 2 Deployments: 
hello,goodbye - Both are associated with their services with names: 
hello-service,goodbye-service 
The Ingress resource will be configured in a way that hello should always answer, but with the addition of configuration-snippet the traffic will be redirected to goodbye.
Responses of this deployments:
|     hello      |    goodbye     |
|----------------|----------------|
| Hello, world!  | Hello, world!  |
| Version: 2.0.0 | Version: 1.0.0 | # notice the version
Example of hello deployment with a service attached to it:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  selector:
    matchLabels:
      app: hello
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: "gcr.io/google-samples/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50001"
---
apiVersion: v1
kind: Service
metadata:
  name: hello-service
spec:
  selector:
    app: hello
  ports:
    - name: hello-port
      port: 5678 # IMPORTANT
      targetPort: 50001
  type: NodePort
To get the goodbye deployment please substitute the hello for goodbye and change the image version to 1.0.
Ingress definition to reroute the request by a header looks like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-ingress 
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($http_LocationHeader = "PL") { proxy_pass http://goodbye-service.default.svc.cluster.local:5678; }
spec:
  rules:
  - host: 
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-service 
          servicePort: hello-port
By default this Ingress definition without the configuration-snippet would always route the traffic to hello-service and then to hello pods. By adding the:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      if ($http_LocationHeader = "PL") { proxy_pass http://goodbye-service.default.svc.cluster.local:5678; }
it will check if the header named LocationHeader is present and if it matches PL. If it does it will send the request to goodbye-service by it's DNS name.
Focusing on:
http://goodbye-service.default.svc.cluster.local:5678http://service_name.namespace.svc.cluster.local:port(dns name without values)
After applying this Ingress resource you should be able to send a request with LocationHeader=PL (with Postman for example) and get the response:
Hello, world!
Version: 1.0.0
Hostname: goodbye-5758448754-wr64c
When I tried to use
mapdirective I was getting following messages:
nginx: [emerg] "map" directive is not allowed here in /tmp/nginx-OMMITED
- 8,982
 - 2
 - 22
 - 45
 
- 
                    Thank you @Dawid for the detailed answer. I was able to achieve this using `configuration-snippet`. As for the map, it is not allowed in `server` and `location` contexts which is why you were seeing the error. It has to be injected using `http-snippet` while deploying the NGINX ingress controller. – Sudarshan Murthy Aug 13 '20 at 05:50