nginx ingress & rewrite-target
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))
}