I have a pod that responds to requests to /api/
I want to do a rewrite where requests to /auth/api/ go to /api/.
Using an Ingress (nginx), I thought that with the ingress.kubernetes.io/rewrite-target: annotation I could do it something like this:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: myapi-ing annotations: ingress.kubernetes.io/rewrite-target: /api kubernetes.io/ingress.class: "nginx" spec: rules: - host: api.myapp.com http: paths: - path: /auth/api backend: serviceName: myapi servicePort: myapi-port
What's happening however is that /auth/ is being passed to the service/pod and a 404 is rightfully being thrown. I must be misunderstanding the rewrite annotation.
Is there a way to do this via k8s & ingresses?
NGINX Ingress Controller provides a robust feature set to secure, strengthen, and scale your containerized apps, including: Advanced app‑centric configuration – Use role‑based access control (RBAC) and self‑service to set up security guardrails (not gates), so your teams can manage their apps securely and with agility.
Ingress , or ingress rules are the rules that ingress controller follows to distribute the load. Ingress controller get the packet, checks ingress rules and determines to which service to deliver the packet. Nginx ingress controller uses LoadBalancer type service actually as entrypoint to the cluster.
ingress-nginx can be used for many use cases, inside various cloud provider and supports a lot of configurations. In this section you can find a common usage scenario where a single load balancer powered by ingress-nginx will route traffic to 2 different HTTP backend services based on the host name.
There are two popular Kubernetes Ingress controllers that use NGINX – both are open source and hosted on GitHub.
I don't know if this is still an issue, but since version 0.22 it seems you need to use capture groups to pass values to the rewrite-target value From the nginx example available here
Starting in Version 0.22.0, ingress definitions using the annotation nginx.ingress.kubernetes.io/rewrite-target are not backwards compatible with previous versions. In Version 0.22.0 and beyond, any substrings within the request URI that need to be passed to the rewritten path must explicitly be defined in a capture group.
For your specific needs, something like this should do the trick
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: myapi-ing annotations: ingress.kubernetes.io/rewrite-target: /api/$2 kubernetes.io/ingress.class: "nginx" spec: rules: - host: api.myapp.com http: paths: - path: /auth/api(/|$)(.*) backend: serviceName: myapi servicePort: myapi-port
I have created the following example that works and which I will explain. To run this minimal example, run these commands:
$ minikube start $ minikube addons enable ingress # might take a while for ingress pod to bootstrap $ kubectl apply -f kubernetes.yaml $ curl https://$(minikube ip)/auth/api/ --insecure success - path: /api/ $ curl https://$(minikube ip)/auth/api --insecure failure - path: /auth/api $ curl https://$(minikube ip)/auth/api/blah/whatever --insecure success - path: /api/blah/whatever
As you'll notice, the ingress rewrite annotation appears to be very particular about trailing slashes. If a trailing slash is not present, the request will not be rewritten. However, if a trailing slash is provided, the request uri will be rewritten and your proxy will function as expected.
After inspecting the generated nginx.conf
file from inside the ingress controller, the line of code responsible for this behavior is:
rewrite /auth/api/(.*) api/$1 break;
This line tells us that only requests matching the first argument will be rewritten with the path specified by the second argument.
I believe this is bug worthy.
kubernetes.yaml
--- apiVersion: v1 kind: Service metadata: name: ingress-rewite-example spec: selector: app: ingress-rewite-example ports: - name: nginx port: 80 protocol: TCP targetPort: 80 type: NodePort --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: ingress-rewite-example spec: template: metadata: labels: app: ingress-rewite-example spec: containers: - name: ingress-rewite-example image: fbgrecojr/office-hours:so-47837087 imagePullPolicy: Always ports: - containerPort: 80 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-rewite-example annotations: ingress.kubernetes.io/rewrite-target: /api kubernetes.io/ingress.class: "nginx" spec: rules: - http: paths: - path: /auth/api backend: serviceName: ingress-rewite-example servicePort: 80
main.go
package main import ( "fmt" "strings" "net/http" ) func httpHandler(w http.ResponseWriter, r *http.Request) { var response string if strings.HasPrefix(r.URL.Path, "/api") { response = "success" } else { response = "failure" } fmt.Fprintf(w, response + " - path: " + r.URL.Path + "\n") } func main() { http.HandleFunc("/", httpHandler) panic(http.ListenAndServe(":80", nil)) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With