NGINX Ingress controller version: ingress-nginx-3.8.0
Kubernetes version (use kubectl version): v1.18.8 and v1.19.1
Environment:
What happened:
After upgrading from ingress-nginx-3.7.1 to 3.8.0, Exact and Prefix path matching behaviour changed in a incompatible way.
What you expected to happen:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- http:
paths:
- path: /foo
pathType: Exact
backend:
serviceName: foo-service
servicePort: 5678
- path: /foo/
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
Give above Ingress rule:
I would expect this behaviour to not change in such incompatible way in v3.8.0
How to reproduce it:
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 8080
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
EOF
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/ingress-nginx-3.8.0/deploy/static/provider/kind/deploy.yaml
kind: Pod
apiVersion: v1
metadata:
name: foo-app
labels:
app: foo
spec:
containers:
- name: foo-app
image: hashicorp/http-echo:0.2.3
args:
- "-text=foo"
---
kind: Service
apiVersion: v1
metadata:
name: foo-service
spec:
selector:
app: foo
ports:
# Default port used by the image
- port: 5678
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- http:
paths:
- path: /foo
pathType: Exact
backend:
serviceName: foo-service
servicePort: 5678
- path: /foo/
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
In v3.8.0
curl localhost:8080/foo returns <html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
curl localhost:8080/foo/ returns foocurl localhost:8080/foo/bar returns <html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/ingress-nginx-3.8.0/deploy/static/provider/kind/deploy.yamlkubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/ingress-nginx-3.7.1/deploy/static/provider/kind/deploy.yamlcurl localhost:8080/foo returns foocurl localhost:8080/foo/ returns foocurl localhost:8080/foo/bar returns fooAnything else we need to know:
I suspect https://github.com/kubernetes/ingress-nginx/commit/cdd6437380c2085ae1321a1f66ab188376c8fd1c is the cause:
/kind bug
I would expect this behaviour to not change in such incompatible way in v3.8.0
Agree but unfortunately, this change is required to pass the ingress conformance tests
https://github.com/kubernetes-sigs/ingress-controller-conformance/blob/master/features/path_rules.feature
hmm, the real world scenario I'm dealing with is that I have below Ingress config in v3.7.1:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
serviceName: bar-service
servicePort: 5678
- path: /foo
pathType: Exact
backend:
serviceName: foo-service
servicePort: 5678
- path: /foo/
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
so that:
In v3.8.0 I tweaked it to:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
serviceName: bar-service
servicePort: 5678
- path: /foo
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
now that:
Is the 404 behaviour really expected or it's a bug?
- path: /foo/
Change that to path: /foo
Is the 404 behaviour really expected or it's a bug?
Yes. Same reason https://github.com/kubernetes-sigs/ingress-controller-conformance/blob/master/features/path_rules.feature#L161-L165
Note: you can check the output of the tests here https://aledbf.github.io/ingress-conformance-sample/features/path-rules.html
Do you mind sharing the reason behind this behaviour, cause it feels very nature to have /foooo fallback to the secondary match / instead of returning 404.
This also seems very confusing and dangerous to user, e.g.
//f with Prefix pathType, now suddenly all requests prefixed with /f either hit B or return 404. These changes are related to Ingress V1
https://github.com/kubernetes/enhancements/blob/master/keps/sig-network/20190125-ingress-api-group.md
This is topic still open for discussion or the decision is final?
This is topic still open for discussion or the decision is final?
There is nothing we can do about it. Ingress V1 was released in k8s 1.19.
Is the 404 behaviour really expected or it's a bug?
I think this is an unexpected behavior in NGINX. I suggested removing that validation but it was rejected.
ok, sigh... this 404 behaviour seems a bit wild to me cause imaging in order for a team to spin up a new service under a new path, they need to figure out all possible request paths it might overtake accidentally and add those rule individually as well.
Thanks for the explanation, I'll see what we can do
@aledbf the test prefix /aaa does not match request /aaaccc in the output you linked does not use an Ingress with an additional / prefix path so a 404 makes sense (since no path matches).
Could you confirm that the following ingress should also return a 404 for /foobar even though there is a / prefix path?
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
- path: /foo
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
When I use the above Ingress with the setup steps @kolorful documented I get the following result:
[me@localhost ~]$ curl localhost:8080/
foo
[me@localhost ~]$ curl localhost:8080/f
foo
[me@localhost ~]$ curl localhost:8080/fo
foo
[me@localhost ~]$ curl localhost:8080/foo
foo
[me@localhost ~]$ curl localhost:8080/foob
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
/ is 200 (matches / prefix path)/f is 200 (matches / prefix path)/fo is 200 (matches / prefix path)/foo is 200 (matches / and /foo prefix paths)/foob is 404 (matches / prefix path but does not match /foo prefix path)This last point is what I'd like clarification on because NGINX is returning a 404 even though the path does match one of the ones specified.
@kolorful @ailurarctos
Please test the fix (#6443) using the image gcr.io/k8s-staging-ingress-nginx/controller:v0.42.1-dev.0
@aledbf I tested the image and it works!
Verified that the / prefix path is chosen when the /foo prefix path does not match but the path still starts with /foo (e.g. /foob):
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
serviceName: bar-service
servicePort: 5678
- path: /foo
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
[me@localhost ~]$ curl localhost:8080/
bar
[me@localhost ~]$ curl localhost:8080/f
bar
[me@localhost ~]$ curl localhost:8080/fo
bar
[me@localhost ~]$ curl localhost:8080/foo
foo
[me@localhost ~]$ curl localhost:8080/foob
bar
[me@localhost ~]$ curl localhost:8080/foo/
foo
[me@localhost ~]$ curl localhost:8080/foo/bar
foo
Verified that the exact path "/foo" is chosen over the prefix path "/foo" but that the prefix path still matches any path that starts with "/foo/":
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- http:
paths:
- path: /foo
pathType: Exact
backend:
serviceName: bar-service
servicePort: 5678
- path: /foo
pathType: Prefix
backend:
serviceName: foo-service
servicePort: 5678
[me@localhost ~]$ curl localhost:8080/foo
bar
[me@localhost ~]$ curl localhost:8080/foo/
foo
[me@localhost ~]$ curl localhost:8080/foo/bar
foo
[me@localhost ~]$ curl localhost:8080/foobar
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
[me@localhost ~]$ # Above 404 is expected because there is no "/" prefix path.
I also checked that a prefix path with a trailing slash is treated identically to a prefix path without a trailing slash.
Thanks very much for the quick fix!
Thank you!
Most helpful comment
@aledbf I tested the image and it works!
Verified that the
/prefix path is chosen when the/fooprefix path does not match but the path still starts with/foo(e.g./foob):Verified that the exact path "/foo" is chosen over the prefix path "/foo" but that the prefix path still matches any path that starts with "/foo/":
I also checked that a prefix path with a trailing slash is treated identically to a prefix path without a trailing slash.
Thanks very much for the quick fix!