Traefik 2 http to https redirect with tls not working - kubernetes

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

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.

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

Istio: RequestAuthentication jwksUri does not resolve internal services names

Notice
The root cause of this is the same than Istio: Health check / sidecar fails when I enable the JWT RequestAuthentication, but after further diagnose, I have reworded to simply (trying to get help)
Problem
I'm trying to configure RequestAuthentication (and AuthorizationPolicy) in an Istio mesh. My JWK tokens are provided by an internal OAUTH server (based in CloudFoundry) that is working fine for other services.
My problem comes when I configure the uri for getting the signing key, linking to the internal pod. In this moment, Istio is not resolving the name of the internal pod. I'm getting confused because the microservices are able to connect to all my internal pods (mongodb, mysql, rabbitmq) including the uaa. Why the RequestAuthentication is not able to do the same?
UAA Service configuration (notice: I'm also creating a virtual service for external access, and this is working fine)
apiVersion: apps/v1
kind: Deployment
metadata:
name: uaa
spec:
replicas: 1
selector:
matchLabels:
app: uaa
template:
metadata:
labels:
app: uaa
spec:
containers:
- name: uaa
image: example/uaa
imagePullPolicy: Never
env:
- name: LOGGING_LEVEL_ROOT
value: DEBUG
ports:
- containerPort: 8090
resources:
limits:
memory: 350Mi
---
apiVersion: v1
kind: Service
metadata:
name: uaa
spec:
selector:
app: uaa
ports:
- port: 8090
name: http
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-auth
spec:
hosts:
- "kubernetes.example.com"
gateways:
- gw-ingress
http:
- match:
- uri:
prefix: /oauth
rewrite:
uri: "/uaa/oauth"
route:
- destination:
port:
number: 8090
host: uaa
- match:
- uri:
prefix: /uaa
rewrite:
uri: "/uaa"
route:
- destination:
port:
number: 8090
host: uaa
RequestAuthentication: Notice the parameter jwksUri, looking for uaa hostname.
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
name: "ra-product-composite"
spec:
selector:
matchLabels:
app: "product-composite"
jwtRules:
- issuer: "http://uaa:8090/uaa/oauth/token"
jwksUri: "http://uaa:8090/uaa/token_keys"
forwardOriginalToken: true
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: "ap-product-composite"
spec:
selector:
matchLabels:
app: "product-composite"
action: ALLOW
rules:
- to:
- operation:
methods: ["GET","POST","DELETE","HEAD","PUT"]
paths: ["*"]
Error log (in istiod pod)
2021-03-17T09:56:18.833731Z error Failed to fetch jwt public key from "http://uaa:8090/uaa/token_keys": Get "http://uaa:8090/uaa/token_keys": dial tcp: lookup uaa on 10.96.0.10:53: no such host
2021-03-17T09:56:18.838233Z info ads LDS: PUSH for node:product-composite-5cbf8498c7-nhxtj.chp18 resources:29 size:134.8kB
2021-03-17T09:56:18.856277Z warn ads ADS:LDS: ACK ERROR sidecar~10.1.4.2~product-composite-5cbf8498c7-nhxtj.chp18~chp18.svc.cluster.local-8 Internal:Error adding/updating listener(s) virtualInbound: Provider 'origins-0' in jwt_authn config has invalid local jwks: Jwks RSA [n] or [e] field is missing or has a parse error
Workaround
For the moment, I have declared the OAUTH server as external, and redirected the request, but this is totally inefficient.
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: se-auth
spec:
hosts:
- "host.docker.internal"
ports:
- number: 8090
name: http
protocol: HTTP
resolution: DNS
location: MESH_EXTERNAL
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: vs-auth
spec:
hosts:
- "kubernetes.example.com"
gateways:
- gw-ingress
http:
- match:
- uri:
prefix: /oauth
rewrite:
uri: "/uaa/oauth"
route:
- destination:
port:
number: 8090
host: "host.docker.internal"
- match:
- uri:
prefix: /uaa
rewrite:
uri: "/uaa"
route:
- destination:
port:
number: 8090
host: "host.docker.internal"
---
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
name: "ra-product-composite"
spec:
selector:
matchLabels:
app: "product-composite"
jwtRules:
- issuer: "http://uaa:8090/uaa/oauth/token"
jwksUri: "http://host.docker.internal:8090/uaa/token_keys"
forwardOriginalToken: true
Workaround 2:
I have workarounded using the FQDN (the fully qualified name) in the host name. But this does not solve my problem, because links the configuration file to the namespace (I use multiple namespaces and I need to have only one configuration file)
In any case, my current line is:
jwksUri: "http://uaa.mynamespace.svc.cluster.local:8090/uaa/token_keys"
I'm totally sure that is a silly configuration parameter, but I'm not able to find it! Thanks in advance
jwksUri: "http://uaa:8090/uaa/token_keys" will not work from istiod, because http://uaa will be interpreted as http://uaa.istio-system.svc.cluster.local. That's why your workaround are solving the problem.
I don't understand why your workaround 2 is not a sufficient solution. Let's say your uaa service runs in namespace auth. If you configure the jwksUri with uaa.auth.svc.cluster.local, every kubernetes pod is able to call it, regardless of it's namespace.
I had a very similar issue which was caused by a PeerAuthentication that set mtls.mode = STRICT for all pods. This caused the istiod pod to fail to retrieve the keys (as istiod seems to not use MTLS when it performs the HTTP GET on the jwksUri).
The solution was to set a PeerAuthentication with mtls.mode = PERMISSIVE on the Pod hosting the jwksUri (which in my case was dex).
Some example YAML:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default-mtls
namespace: my-namespace
spec:
mtls:
## the empty `selector` applies STRICT to all Pods in `my-namespace`
mode: STRICT
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: dex-mtls
namespace: my-namespace
spec:
selector:
matchLabels:
my_label: dex
mtls:
## the dex pods must allow incoming non-MTLS traffic because istiod reads the JWKS keys from:
## http://dex.my-namespace.svc.cluster.local:5556/dex/keys
mode: PERMISSIVE

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