ingress-nginx
ingress-nginx copied to clipboard
Redirect annotations are ignored on default backend.
What happened:
Create an ingress for the default backend like so:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/permanent-redirect: https://example.com
name: default-backend
namespace: nginx-ingress
spec:
defaultBackend:
service:
name: default-backend
port:
name: http
What you expected to happen:
Requests hitting the default backend were redirected.
Instead this annotation was ignored. It is may be related to https://github.com/kubernetes/ingress-nginx/issues/1405. This also doesn't work for nginx.ingress.kubernetes.io/temporal-redirect
.
A workaround is to use a snippet which works as intended:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
return 301 https://example.com;
spec:
defaultBackend:
service:
name: default-backend
port:
name: http
NGINX Ingress controller version
NGINX Ingress controller
Release: v1.2.1
Build: 08848d69e0c83992c89da18e70ea708752f21d7a
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: nginx/1.19.10
Kubernetes version (use kubectl version
):
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.8", GitCommit:"a12b886b1da059e0190c54d09c5eab5219dd7acf", GitTreeState:"archive", BuildDate:"1980-01-01T00:00:00Z", GoVersion:"go1.17.10", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.8", GitCommit:"7061dbbf75f9f82e8ab21f9be7e8ffcaae8e0d44", GitTreeState:"clean", BuildDate:"2022-03-16T14:04:34Z", GoVersion:"go1.16.15", Compiler:"gc", Platform:"linux/amd64"}
Environment:
- How was the ingress-nginx-controller installed:
Kubernetes Manifests
apiVersion: v1
items:
- apiVersion: v1
kind: Service
metadata:
annotations:
name: default-backend
namespace: nginx-ingress
spec:
ports:
- name: http
port: 8181
targetPort: 8181
selector:
app: nginx
type: ClusterIP
- apiVersion: v1
kind: Service
metadata:
labels:
external-dns: "true"
name: nginx
namespace: nginx-ingress
spec:
ports:
- name: http
port: 8080
targetPort: 8080
- name: https
port: 8443
targetPort: 8443
selector:
app: nginx
type: NodePort
- apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx
namespace: nginx-ingress
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- args:
- /nginx-ingress-controller
- --configmap=nginx-ingress/nginx-configuration
- --default-backend-service=nginx-ingress/default-backend
- --default-server-port=8181
- --election-id=ingress-controller-leader
- --http-port=8080
- --https-port=8443
- --watch-ingress-without-class=true
env:
- name: LD_PRELOAD
value: /usr/local/lib/libmimalloc.so
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: k8s.gcr.io/ingress-nginx/controller:v1.2.1@sha256:5516d103a9c2ecc4f026efbd4b40662ce22dc1f824fb129ed121460aaa5c47f8
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
livenessProbe:
failureThreshold: 2
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 1
name: nginx
ports:
- containerPort: 8080
hostPort: 80
- containerPort: 8443
hostPort: 443
resources:
requests:
cpu: 100m
memory: 100Mi
securityContext:
allowPrivilegeEscalation: true
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsNonRoot: true
runAsUser: 101
startupProbe:
failureThreshold: 60
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 2
successThreshold: 1
timeoutSeconds: 1
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
restartPolicy: Always
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
type: RollingUpdate
- apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
return 301 https://example.com;
name: default-backend
namespace: nginx-ingress
spec:
defaultBackend:
service:
name: default-backend
port:
name: http
kind: List
metadata:
resourceVersion: ""
selfLink: ""
How to reproduce this issue:
Install minikube/kind
- Minikube https://minikube.sigs.k8s.io/docs/start/
- Kind https://kind.sigs.k8s.io/docs/user/quick-start/
Install the ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml
Install an application that will act as default backend (is just an echo app)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/docs/examples/http-svc.yaml
Create an ingress (please add any additional annotation required)
echo "
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/permanent-redirect: https://example.com
name: default-backend
namespace: nginx-ingress
spec:
defaultBackend:
service:
name: http-svc
port:
name: http
" | kubectl apply -f -
make a request
POD_NAME=$(k get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx -o NAME)
kubectl exec -it -n ingress-nginx $POD_NAME -- curl -i localhost
@kevincox: This issue is currently awaiting triage.
If Ingress contributors determines this is a relevant issue, they will accept it by applying the triage/accepted
label and provide further guidance.
The triage/accepted
label can be added by org members by writing /triage accepted
in a comment.
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.
/remove-kind bug I could not reproduce the problem
Can you share the configuration you used?
% cat ~/bin/create.www.dev.mydomain.com.sh kubectl create ns mydomain && kubectl -n mydomain create secret tls wildcard.dev.mydomain.com --cert ~/myclusters/letsencrypt/mydomain/dev.mydomain.com/fullchain1.pem --key ~/myclusters/letsencrypt/mydomain/dev.mydomain.com/privkey1.pem && kubectl -n mydomain create deploy frontend --image nginx:alpine --port 80 && kubectl -n mydomain expose deploy frontend --port 80 && sleep 3 && kubectl -n mydomain create ing frontend --class nginx --rule www.dev.mydomain.com/*=frontend:80,tls=wildcard.dev.mydomain.com
That appears to be creating an ingress for a specific domain rather than for the default-backend. I also don't see where the redirect annotation is being applied in your script.
The annotation is not limited for its use on default-backend. Since this issue is related to the annotation, its implied obviously that the annotation was used
% k describe ing frontend
Name: frontend
Labels: <none>
Namespace: mydomain
Address: x.x.x.x
Ingress Class: nginx
Default backend: <default>
TLS:
wildcard.dev.mydomain.com terminates www.dev.mydomain.com
Rules:
Host Path Backends
---- ---- --------
www.dev.mydomain.com
/ frontend:80 (10.244.0.77:80)
Annotations: nginx.ingress.kubernetes.io/permanent-redirect: https://www.google.com
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 3s (x3 over 2m45s) nginx-ingress-controller Scheduled for sync
The annotation is still on an ingress with a specific host though, not the ingress for the default-backend.
What is the image being used to create the pod at the backend and how did you create the svc for that pod ? Its not crystal clear as to what problem has to be solved here because I am able to get google.com landing page, even though I sent request to www.dev.mydomain.com.
Lets use the response codes etc below. Can you write what you expect to see different from what is seen below ;
HTTP/1.1 301 Moved Permanently
Date: Tue, 21 Jun 2022 01:47:27 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://www.google.com
HTTP/2 200
content-type: text/html; charset=ISO-8859-1
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
date: Tue, 21 Jun 2022 01:47:27 GMT
server: gws
x-xss-protection: 0
x-frame-options: SAMEORIGIN
expires: Tue, 21 Jun 2022 01:47:27 GMT
cache-control: private
set-cookie: 1P_JAR=2022-06-21-01; expires=Thu, 21-Jul-2022 01:47:27 GMT; path=/; domain=.google.com; Secure
set-cookie: AEC=AakniGNTCUeIfP9DatS_09P2Oow_1s7QaNfok1mogjWjs3Mg3-7qDmzryQ; expires=Sun, 18-Dec-2022 01:47:27 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
set-cookie: NID=511=dLeU0f3ISPgpZ6aJYFehBrFTy4hKzpT7InZprN4o8ZnQ2TUlBcP5v8MM2J7emxG8LrTSMzuJ80sQ_nttI4BelE_DFfp5ZbVAkb1V-vA4Fj-YIr2iEQKfbeBrqsN5W15h3p6IOUi3aTvEvn2u9xEkdw0pNHh39sxirCjRB8Jbk-U; expires=Wed, 21-Dec-2022 01:47:27 GMT; path=/; domain=.google.com; HttpOnly
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Maybe it will be better, if you can write down steps by step instructions, that someone can copy/paste on their laptop and reproduce the problem. Make sure to provide exact copy/paste ease even for the backend pod, svc and ingress creation etc etc. You can just do http instead of https.
See the manifests in the original post. Everything is there.
It is using the built-in default service, but I don't think that should even matter because it should never actually be hit with the redirect configured.
Can you write what you expect to see different from what is seen below
The response you provide looks correct but is for a different configuration.
I think this is working as intended (I'm not a maintainer of this repo though and they might have a different say) and the reasoning could be found in the issue that you linked to (#1406, comment):
"This requirement makes sure you only get a redirect for the rule where the annotation is configured and not all the paths"
To elaborate, what I believe is supposed to happen is that nginx.ingress.kubernetes.io/permanent-redirect
should only be applied when the path that you made the request to actually matches one of the rules of any the Ingress
resource.
If this were not the case, it would be ambiguous as to where a request like curl INGRESS_IP -H "Host: abc.xyz"
should be sent to in the following case:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-1
annotations:
nginx.ingress.kubernetes.io/permanent-redirect: https://example.com
spec:
ingressClassName: nginx
defaultBackend:
service:
name: default-backend
port:
name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-2
spec:
ingressClassName: nginx
rules:
- host: 'foo.bar.io'
http:
paths:
- backend:
service:
name: foo-bar-service
port:
number: 80
path: /
pathType: Prefix
defaultBackend:
service:
name: default-backend
port:
name: http
---
The case you provided is basically ambiguous anyways. I think the kube Ingress is intended for a single IP per Ingress which ingress-nginx breaks. However in the case where you don't have multiple ingresses the custom snippet works so I don't see why the nginx.ingress.kubernetes.io/permanent-redirect
rule would be any different. I would expect it to basically be a safe short-form for the redirects.
the reasoning could be found in the issue that you linked to (https://github.com/kubernetes/ingress-nginx/issues/1406, https://github.com/kubernetes/ingress-nginx/issues/1405#issuecomment-333336305):
This seems to have a significant difference to me. In that case there is a host with no backend services which is very strange. In this case we have a default service which works for every path there.
I will let @aledbf verify the intended behaviour, since not allowing redirects to work in this was was explicitly disabled in #1768
And seeing this behaviour being forced explicitly, I feel that the fact you are using configuration-snippet
to redirect is more of a hack and not the expected usage.
I have the same problem with nginx.ingress.kubernetes.io/app-root
I wanted to create an ingress that does 2 things: redirect / to /approot, and provide a defaultbackend (for custom 404s). I tried
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: root-ingress
annotations:
nginx.ingress.kubernetes.io/app-root: /rootapp
spec:
defaultBackend:
service:
name: default-backend
port:
number: 80
but I have to add
# Needed for app-root redirect to work
rules:
- http:
paths:
- path: /
pathType: Exact
backend:
service:
name: default-backend
port:
number: 80
I have to reuse default-backend in my rules even though it will never be used, just because validation fails otherwise. Are there any plans to simplify this ?
Setting app-root and doing a redirect are 2 different functionalities. There is not enough information or justification or even a reasonable use-case description with regards to what problem you need to solve in your infrastructure and what problem exists in the ingress-nginx-controller.
I tested the app-root annotation and it works. I tested the redirect annotation and that too works. So even the original problem description does is not backed by valid information & justification for the expectation and neither is the most recent post here backed by enough information or justification to explain what seems to be a problem (that needs to be solved) in the controller.
To the state the obvious, If you send a request to a cluster, the controller attempts to matche the request to a rule and upon failure to match ANY rule, the default backend sends a response. So for the original problem in the first post, the expectation elaborated is like ;
- invalid or bad request is sent to controller
- default-backend's response is acknowledged as the designed behaviour because request did not match ANY rule
- but instead of default-backend sending a response to client who sent the request, the reporting user is expecting a redirect to example.com
The default-backend configured there is not coded to do a redirect when a client request from outside the cluster does not match any rule. Its designed to send a response to the client.
Now as for the latest post above by @jonenst , the annotation app-root is designed to go to the value configured in that annotation, in the context of the service (and ultimately the pod) that is configured in the ingress.spec.rules.http.paths.backend
field. So some useful relvant information and justification is needed here to explain why there is an expectation that the app-root annotation should consider the field ingress.spec.defaultBackend
, to even begin with. That field is there to deal with the case where the client request did not match any rule at all.
% k explain ingress.spec.defaultBackend KIND: Ingress VERSION: networking.k8s.io/v1
RESOURCE: defaultBackend <Object>
DESCRIPTION: DefaultBackend is the backend that should handle requests that don't match any rule. If Rules are not specified, DefaultBackend must be specified. If DefaultBackend is not set, the handling of requests that do not match any of the rules will be up to the Ingress controller.
hi @longwuyuan, thanks for taking the time to think about this. I can elaborate on the use case if you want. I can also probably write a repro repo if you need.
Context of my use case: I have several "apps" that work together. They each have an ingress with one rule that uses a prefix for the app (so the user visits app.com/app1/ or app.com/app2/).
Use case: implement the following:
- Users can also just visit app.com/, and need to be redirected to app.com/app1/.
- For users that mistakenly visit app.com/notapp1/, I want to display a custom 404.
I thought creating an ingress with the app-root annotation would solve the first requirement, so I created:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: root-ingress
annotations:
nginx.ingress.kubernetes.io/app-root: /app1
I couldn't kubectl apply this because it failed validation: you need either rules or a defaultBackend I tried adding as little as I could, but needed to add a full rule sending to a service (like in https://kubernetes.github.io/ingress-nginx/examples/rewrite/#app-root what is http-svc here, does it exist ? why is it required ?):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: root-ingress
annotations:
nginx.ingress.kubernetes.io/app-root: /app1
rules:
- http:
paths:
- path: /
pathType: Exact
backend:
service:
name: notimportant # here I can use a non existing (which will warn in the nginx controller logs or an existing service which is confusing, we don't care anyway since app-root redirects instead of calling the service
port:
number: 80
Then I thought that implementing the 404 requirement would allow me to delete the rule with the unused service, because I would add a defaultBackend anyway:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: root-ingress
annotations:
nginx.ingress.kubernetes.io/app-root: /rootapp
spec:
defaultBackend:
service:
name: default-backend
port:
number: 80
default-backend sends 404s to any request, so visiting app.com/notapp correctly returns a 404, but now 'app.com/' doesn't redirect on 'app.com/app1'. I had to add the rule again, and so I used the default-backend service in the rule just because it exists, and doesn't produce any warnings:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: root-ingress
annotations:
nginx.ingress.kubernetes.io/app-root: /rootapp
spec:
defaultBackend:
service:
name: default-backend
port:
number: 80
# Needed for app-root redirect to work
rules:
- http:
paths:
- path: /
pathType: Exact
backend:
service:
name: default-backend
port:
number: 80
The configuration I gave with only app-root and a defaultBackend felt very intuitive to me. It didn't feel intuitive that the app-root annotation only works if at least one rule matches, although now I see that it could be a reasonable behavior. Maybe that's just a documentation issue ?
Let me know what you think, or if you want me to do anything else. Jon
This is not a bug. This is related to using the annotations and features. There are not many resources to track support issues here on github. Please come to kubernetes slack and discuss this there because there are several great folks there who like to help and have the knowledge. You may even find someone who has the same use case.
From what you have discussed in the post above, its just going to be a long elaborate meticulous discussion, which will be fruitful only if this entire topic is broken down into a list of http requests you send and the behaviour you expect. On slack you should post that list for others to understand the problem you are trying to solve in your infrastructure.
Personally, I don't think you need the approot annotation. A simple ingress can define a rule that takes hostname
with path as /
, with pathType as Exact
and backend as app1
.
Hi @longwuyuan, thanks again for you time. Feel free to close this issue if it's not the right place to discuss. Because this is not a blocking problem, I don't want to impose more burden on the limited resources.
Regarding the approot annotation, in my usecase, I need the users to have the url change in their browser, not serving the app from / so I don't think that what you are suggesting would fulfil that requirement.
Hi @jonenst The idea is to discuss on slack as there are more people there and then update issue here if you find data on a bug or a problem in the controller.
There are several factors and tiny details that are not in the discussion. For example you can redirect one request but if all your requests contain hostname + path combo that needs a redirection, then its a different story.
Please discuss in the ingress-nginx-users channel in slack.
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs.
This bot triages issues and PRs according to the following rules:
- After 90d of inactivity,
lifecycle/stale
is applied - After 30d of inactivity since
lifecycle/stale
was applied,lifecycle/rotten
is applied - After 30d of inactivity since
lifecycle/rotten
was applied, the issue is closed
You can:
- Mark this issue or PR as fresh with
/remove-lifecycle stale
- Mark this issue or PR as rotten with
/lifecycle rotten
- Close this issue or PR with
/close
- Offer to help out with Issue Triage
Please send feedback to sig-contributor-experience at kubernetes/community.
/lifecycle stale
The original issue description here uses the phrase "default-backend" and claims that permanent-redirect annotation does not work.
I have tested both default-backend feature and the permanent-redirect annotation. Both work fine on their own. So after all the discussion done on this issue, it is safe to assume that there is some kind of assumption like a workload is deemed the default-backend first (which is supposed to contain custom error handling as that is what a default -backend or a custom-backend would do). And then on top of that effort for custom-error-handling, the traffic that heads to that default-backend is expected to be permanently redirected to google.com.
This is not a real use case in practical terms and also unclear in its description of reproducing it. I will close the issue as it is not tracked actively and adding up to the could of open issues without having a real action-item on it for the project.
Thanks
/close
@longwuyuan: Closing this issue.
In response to this:
The original issue description here uses the phrase "default-backend" and claims that permanent-redirect annotation does not work.
I have tested both default-backend feature and the permanent-redirect annotation. Both work fine on their own. So after all the discussion done on this issue, it is safe to assume that there is some kind of assumption like a workload is deemed the default-backend first (which is supposed to contain custom error handling as that is what a default -backend or a custom-backend would do). And then on top of that effort for custom-error-handling, the traffic that heads to that default-backend is expected to be permanently redirected to google.com.
This is not a real use case in practical terms and also unclear in its description of reproducing it. I will close the issue as it is not tracked actively and adding up to the could of open issues without having a real action-item on it for the project.
Thanks
/close
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.