Can you forward traffic to a dynamic service using Traefik? - kubernetes

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

Related

Path or PathPrefix routing with Traefik on Kubernetes not working

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.

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.

How to bind gateway to a specific namespace?

I have the following scenario:
When the user A enter the address foo.example1.example.com in the
browser, then it should call the service FOO in the namespace
example1.
When the user B enter the address foo.example1.example.com in the
browser, then it should call the service FOO in the namespace
example2.
I am using istio, the question is, how to configure the gateway, that is bind specific to a namespace:
Look at an example of istio gateway configuration:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ns_example1
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "example1.example.com"
EOF
When I would deploy the gateway, then it will apply to current namespace but I would like to specify a namespace.
How to assign a gateway to specific namespace?
I think this link should answer your question.
There is many things You won't need, but there is idea You want to apply to your istio cluster.
So You need 1 gateway and 2 virtual services.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: foocorp-gateway
namespace: default
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 80
name: http-example1
protocol: HTTP
hosts:
- "example1.example.com"
- port:
number: 80
name: http-example2
protocol: HTTP
hosts:
- "example2.example.com"
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: example1
namespace: ex1
spec:
hosts:
- "example1.example.com"
gateways:
- foocorp-gateway
http:
- match:
- uri:
exact: /
route:
- destination:
host: example1.ex1.svc.cluster.local
port:
number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: example2
namespace: ex2
spec:
hosts:
- "example2.example.com"
gateways:
- foocorp-gateway
http:
- match:
- uri:
exact: /
route:
- destination:
host: example2.ex2.svc.cluster.local
port:
number: 80
EDIT
You can create gateway in namespace ex1 and ex2, then just change gateway field in virtual service and it should work.
Remember to add namespace/gateway, not only gateway name, like there.
gateways:
- some-config-namespace/gateway-name
Let me know if that help You.

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

Fully qualified domain name (FQDN) for services in Kubernetes not working? DNS-1035

I am following this concept guide on the kubernetes docs to connect to a service in a different namespace using the fully qualified domain name for the service.
service.yml
---
# declare front service
kind: Service
apiVersion: v1
metadata:
name: traefik-frontend-service
namespace: traefik
spec:
selector:
k8s-app: traefik-ingress-lb
tier: reverse-proxy
ports:
- port: 80
targetPort: 8080
type: NodePort
ingress.yml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: traefik-web-ui-ingress
namespace: traefik
annotations:
kubernetes.io/ingress.class: traefik
traefik.frontend.passHostHeader: "false"
traefik.frontend.priority: "1"
spec:
rules:
- host: example.com
http:
paths:
- path: /
backend:
serviceName: traefik-frontend-service.traefik.svc.cluster.local
servicePort: 80
But I keep getting this error:
The Ingress "traefik-web-ui-ingress" is invalid: spec.rules[0].http.backend.serviceName: Invalid value: "traefik-frontend-service.traefik.svc.cluster.local": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is 'a-z?')
The service name of traefik-frontend-service.traefik.svc.cluster.local:
starts with an alphanumeric character
ends with an alphanumeric character
only contains alphanumeric numbers or -
Not sure what I'm doing wrong here... unless a new ingress has to be created for each namespace.
This is by design to avoid cross-namespace exposure, In this thread is explained why this limitation on the ingress specification was intentional.
That means, the Ingress can only expose services within the same namespace.
The values provided should be the service name, not the FQDN.
If you really need to design this way, your other alternatives are:
Expose Traefik as a LB Service and then create a data service to provide the routing rules to traefik.
Use Contour Ingress (by heptio) to delegate the routing to other namespaces.
Using Contour would be something like this:
# root.ingressroute.yaml
apiVersion: contour.heptio.com/v1beta1
kind: IngressRoute
metadata:
name: namespace-delegation-root
namespace: default
spec:
virtualhost:
fqdn: ns-root.bar.com
routes:
- match: /
services:
- name: s1
port: 80
# delegate the subpath, `/blog` to the IngressRoute object in the marketing namespace with the name `blog`
- match: /blog
delegate:
name: blog
namespace: marketing
------------------------------------------------------------
# blog.ingressroute.yaml
apiVersion: contour.heptio.com/v1beta1
kind: IngressRoute
metadata:
name: blog
namespace: marketing
spec:
routes:
- match: /blog
services:
- name: s2
port: 80