Path or PathPrefix routing with Traefik on Kubernetes not working - kubernetes

I'm using Traefik 2.7.0 on an AKS Kubernetes Cluster 1.22.6.
Currently, everything routes to the same service:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: api
namespace: namespace1
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.my-domain.com`)
kind: Rule
services:
- name: api
namespace: namespace1
port: 80
tls:
secretName: api-my-domain-com-cert
I'm currently in the process of externalizing an API resource from this service to a dedicated new service ("/users") because there will be other services in the future that will need the same functionality.
What I'm trying (and failing) to do, is to route calls to "/users" to the new service:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: api
namespace: namespace1
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.my-domain.com`) && Path(`/users`)
kind: Rule
services:
- name: users-api
namespace: namespace2
port: 80
- match: Host(`api.my-domain.com`)
kind: Rule
services:
- name: api
namespace: namespace1
port: 80
tls:
secretName: api-baywa-lusy-com-cert
I tried Path(..) and PathPrefix(..). No success. Everything is still routed to the old service. The new service has slightly different output. So I can tell with certainty that it's still routed to the old service.
Adding the priority manually didn't help either:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: api
namespace: namespace1
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.my-domain.com`) && Path(`/users`)
kind: Rule
priority: 2000
services:
- name: users-api
namespace: namespace2
port: 80
- match: Host(`api.my-domain.com`)
kind: Rule
priority: 1000
services:
- name: api
namespace: namespace1
port: 80
tls:
secretName: api-baywa-lusy-com-cert
Am I Missing something here? Any help is appreciated!
Thanks,
best regards,
Pascal

You can only expose services in the same namespace as your IngressRoute resource. If you watch the logs of your Traefik pod when you deploy your IngressRoute, you should see something like:
time="2023-01-26T13:57:17Z" level=error msg="service namespace2/users-api not in the parent resource namespace namespace1" providerName=kubernetescrd ingress=namespace1 namespace=namespace1
To do what you want, you need to create two separate IngressRoute resources, one in namespace1 and one in namespace2.
In namespace1:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
labels:
app: old-api
name: old-api
namespace: namespace1
spec:
entryPoints:
- web
routes:
- kind: Rule
priority: 1000
match: Host(`api.my-domain.com`)
services:
- name: old-api
port: 80
In namespace2:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
labels:
app: new-api
name: new-api
namespace: namespace2
spec:
entryPoints:
- web
routes:
- kind: Rule
priority: 2000
match: Host(`api.my-domain.com`) && PathPrefix(`/users`)
services:
- name: new-api
port: 80
You can find all the files I used to test this configuration here.
I don't know if the explicit priorities are necessary or not; it worked for me without them but maybe I was just lucky so I left them there. I would generally assume that a "more specific route" takes precedence over a "less specific route", but I don't know if that's actually true.

Related

traefik ingressroute - URL should redirect to a default path

kubernetes ingress rules, AWS ALB rule has default path rule if no path is provided in the URL.
I couldn't figure out similar config in Traefik ingressroute.
Right now - https://example.com goes to the default wildfly page.
https://example.com/foo goes to the app login page.
How do I configure so that https://example.com would directly go to https://example.com/foo
Ingressroute:
spec:
entryPoints:
- web
- websecure
routes:
- kind: Rule
match: Host(`example.com`) && PathPrefix(`/`)
middlewares:
- name: https-redirect
namespace: ns
services:
- name: service-1
port: 80
Another ingressroute:
spec:
entryPoints:
- web
routes:
- match: Host(`example.com`)
kind: Rule
middlewares:
- name: https-redirect
services:
- name: service-1
port: 80
Middleware:
kind: Middleware
metadata:
name: https-redirect
spec:
redirectScheme:
scheme: https
permanent: true
According to Traefik docs, you can create a typical defaultBackend Kubernetes Ingress like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cheese
spec:
defaultBackend:
service:
name: stilton
port:
number: 80
You can then set priority for this Ingress to ensure it matches the right type of requests, as traefik.ingress.kubernetes.io/router.priority annotation. You can read more about this here.

Right namespace for VirtualService and DestinationRule Istio resources

I am trying to understand the VirtualService and DestinationRule resources in relation with the namespace which should be defined and if they are really namespaced resources or they can be considered as cluster-wide resources also.
I have the following scenario:
The frontend service (web-frontend) access the backend service (customers).
The frontend service is deployed in the frontend namespace
The backend service (customers) is deployed in the backend namespace
There are 2 versions of the backend service customers (2 deployments), one related to the version v1 and one related to the version v2.
The default behavior for the clusterIP service is to load-balance the request between the 2 deployments (v1 and v2) and my goal is by creating a DestinationRule and a VirtualService to direct the traffic only to the deployment version v1.
What I want to understand is which is the appropriate namespace to define such DestinationRule and a VirtualService resources. Should I create the necessary DestinationRule and VirtualService resources in the frontend namespace or in the backend namespace?
In the frontend namespace I have the web-frontend deployment and and the related service as follow:
apiVersion: v1
kind: Namespace
metadata:
name: frontend
labels:
istio-injection: enabled
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend
namespace: frontend
labels:
app: web-frontend
spec:
replicas: 1
selector:
matchLabels:
app: web-frontend
template:
metadata:
labels:
app: web-frontend
version: v1
spec:
containers:
- image: gcr.io/tetratelabs/web-frontend:1.0.0
imagePullPolicy: Always
name: web
ports:
- containerPort: 8080
env:
- name: CUSTOMER_SERVICE_URL
value: 'http://customers.backend.svc.cluster.local'
---
kind: Service
apiVersion: v1
metadata:
name: web-frontend
namespace: frontend
labels:
app: web-frontend
spec:
selector:
app: web-frontend
type: NodePort
ports:
- port: 80
name: http
targetPort: 8080
I have expose the web-frontend service by defining the following Gateway and VirtualService resources as follow:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: gateway-all-hosts
# namespace: default # Also working
namespace: frontend
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: web-frontend
# namespace: default # Also working
namespace: frontend
spec:
hosts:
- "*"
gateways:
- gateway-all-hosts
http:
- route:
- destination:
host: web-frontend.frontend.svc.cluster.local
port:
number: 80
In the backend namespace I have the customers v1 and v2 deployments and related service as follow:
apiVersion: v1
kind: Namespace
metadata:
name: backend
labels:
istio-injection: enabled
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: customers-v1
namespace: backend
labels:
app: customers
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: customers
version: v1
template:
metadata:
labels:
app: customers
version: v1
spec:
containers:
- image: gcr.io/tetratelabs/customers:1.0.0
imagePullPolicy: Always
name: svc
ports:
- containerPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: customers-v2
namespace: backend
labels:
app: customers
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: customers
version: v2
template:
metadata:
labels:
app: customers
version: v2
spec:
containers:
- image: gcr.io/tetratelabs/customers:2.0.0
imagePullPolicy: Always
name: svc
ports:
- containerPort: 3000
---
kind: Service
apiVersion: v1
metadata:
name: customers
namespace: backend
labels:
app: customers
spec:
selector:
app: customers
type: NodePort
ports:
- port: 80
name: http
targetPort: 3000
I have created the following DestinationRule and VirtualService resources to send the traffic only to the v1 deployment.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: customers
#namespace: default # Not working
#namespace: frontend # working
namespace: backend # working
spec:
host: customers.backend.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: customers
#namespace: default # Not working
#namespace: frontend # working
namespace: backend # working
spec:
hosts:
- "customers.backend.svc.cluster.local"
http:
## route - subset: v1
- route:
- destination:
host: customers.backend.svc.cluster.local
port:
number: 80
subset: v1
The question is which is the appropriate namespace to define the VR and DR resources for the customer service?
From my test I see that I can use either the frontend namespace, or the backend namespace. Why the VR,DR can be created to the frontend namespace or in the backend namespaces and in both cases are working? Which is the correct one?
Are the DestinationRule and VirtualService resources really namespaced resources or can be considered as cluster-wide resources ?
Are the low level routing rules of the proxies propagated to all envoy proxies regardless of the namespace?
A DestinationRule to actually be applied during a request needs to be on the destination rule lookup path:
-> client namespace
-> service namespace
-> the configured meshconfig.rootNamespace namespace (istio-system by default)
In your example, the "web-frontend" client is in the frontend Namespace (web-frontend.frontend.svc.cluster.local), the "customers" service is in the backend Namespace (customers.backend.svc.cluster.local), so the customers DestinationRule should be created in one of the following Namespaces: frontend, backend or istio-system. Additionally, please note that the istio-system Namespace isn't recommended unless the destination rule is really a global configuration that is applicable in all Namespaces.
To make sure that the destination rule will be applied we can use the istioctl proxy-config cluster command for the web-frontend Pod:
$ istioctl proxy-config cluster web-frontend-69d6c79786-vkdv8 -n frontend | grep "customers.backend.svc.cluster.local"
SERVICE FQDN PORT SUBSET DESTINATION RULE
customers.backend.svc.cluster.local 80 - customers.frontend
customers.backend.svc.cluster.local 80 v1 customers.frontend
customers.backend.svc.cluster.local 80 v2 customers.frontend
When the destination rule is created in the default Namespace, it will not be applied during the request:
$ istioctl proxy-config cluster web-frontend-69d6c79786-vkdv8 -n frontend | grep "customers.backend.svc.cluster.local"
SERVICE FQDN PORT SUBSET DESTINATION RULE
customers.backend.svc.cluster.local 80 -
For more information, see the Control configuration sharing in namespaces documentation.

Can you forward traffic to a dynamic service using Traefik?

Below is an example IngressRoute.
I would like to have something like this where the first part of the domain would map to a Kubernetes service without having to statically define the service name.
service1.api.test.com -> service1
service2.api.test.com -> service1
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute
namespace: default
spec:
entryPoints:
- web
routes:
- match: "HostRegexp(`{subdomain:[a-z]+}.api.test.com`)"
kind: Rule
services:
- name: whoami # can this be dynamic?
port: 80
No it's not possible. Because even if you are able to match the host regular expression it will not know under the hood which service the request needs to go to.
So you will need create two separate matches for different services in the same ingressRoute
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute
namespace: default
spec:
entryPoints:
- web
routes:
- match: "HostRegexp(`someservice.yourdomain.com`) && PathPrefix(`/`)"
kind: Rule
services:
- name: whoamiV1
port: 80
- match: "HostRegexp(`yourdomain.com`,`{subdomain:[a-zA-Z0-9-]+}.yourdomain.com`) &&PathPrefix(`/api/someservice/`)"
kind: Rule
services:
- name: whoamiV2
port: 80
For more details you can refer the docs

Why does Traefik v2 response 404 only over http

My problem is that my traefik ingress controller in my kubernetes cluster does response 404 page not found over http, BUT over https I get the real response from the service.
This happened after I added the TLS section to IngressRoute.
This is my IngressRoute:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: example-backend
namespace: example
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`api.example.com`)
kind: Rule
priority: 10
services:
- name: example-backend-service
port: 80
tls:
secretName: tls-secret # I'm using my own certificate, not Let's Encrypt
Why I don't get the real/same response like https does?
As the TLS applies on a router, you cannot have only one IngressRoute to handle the 2 cases.
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: example-backend
namespace: example
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.example.com`)
kind: Rule
priority: 10
services:
- name: example-backend-service
port: 80
tls:
secretName: tls-secret
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: example-backend-redirect
namespace: example
spec:
entryPoints:
- web
routes:
- match: Host(`api.example.com`)
kind: Rule
priority: 10
services:
# in this IngressRoute the service will be never called
# because of the redirect middleware.
- name: example-backend-service
port: 80
middlewares:
- name: https_redirect
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: https_redirect
spec:
redirectScheme:
scheme: https
permanent: true

Traefik 2 http to https redirect with tls not working

I want to set up http to https redirect in one IngressRoute, but with configuration below when I trying to access http endpoint traefik returns 404 not found error. If I remove tls section redirect works but tls not.
Can I have both working?
traefik version 2.1.0-rc2
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: console-web
namespace: dev
labels:
app: console-web
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`console.example.com`)
kind: Rule
services:
- name: console-web
port: 8080
middlewares:
- name: https-redirect
tls:
secretName: example-com-tls
This is an old issue however this might help someone. This might not directly work as i have not tested it. For kubernetes it should work in following way first you define how the middleware works
Untested Code
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-redirectscheme
spec:
redirectScheme:
scheme: https
Then define the IngressRoute
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingress1
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`somehost`)
kind: Rule
services:
- name: console-web
port: 8080
tls:
secretName: example-com-tls
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingress2
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`somehost`)
middlewares:
- name: test-redirectscheme
kind: Rule
services:
- name: console-web
port: 80
two ingress needed as one is redirecting the trafic to other. I also suppose if you dont have two ports you can reuse the previous one as it is going to be redirected to https anyway. Let me know if it does not does not work.
after spending hours on this for docker on this 404 issue for the http endpoint i found this https://stackoverflow.com/a/62093408/2442649