1

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?

Sudarshan Murthy
  • 359
  • 5
  • 16
  • 1
    Hello, 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 Answers1

16

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:


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:5678
  • http://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 map directive I was getting following messages:

  • nginx: [emerg] "map" directive is not allowed here in /tmp/nginx-OMMITED
Dawid Kruk
  • 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