How to expose a single path of a service via ingress? (not the whole service) - kubernetes

I want my Jenkins to be only accessible in the private network via VPN to protect it from the dangers of the public internet. Nonetheless, I also want to use GitHub webhooks. So the Jenkins needs to expose a single path to the public internet.
To expose the whole service I could simply create an ingress rule like:
rules:
- host: example.com
http:
paths:
- path: /jenkins
pathType: Prefix
backend:
service:
name: jenkins-service
port:
number: 80
This would expose the WHOLE service at https://example.com/jenkins. But this is bad from a security perspective. Therefore I would like to expose only the webhook path of the service. I.e. /generic-webhook-trigger/invoke. So that it is the only URL you can reach from the public internet. Like this:
curl https://example.com/generic-webhook-trigger/invoke
Is that possible? Thanks for any suggestion!

So this doesn't seem to be possible with the native ingress functionalities but requires an ingress controller, like the Nginx Ingress Controller. These come with additional functionalities, like URI rewrite rules (see here).
In case of the Nginx Ingress Controller you need the nginx.ingress.kubernetes.io/rewrite-target and rewrite your ingress path to the desired path.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jenkins-ingress
namespace: jenkins
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /jenkins/generic-webhook-trigger/invoke
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /jenkins
pathType: Prefix
backend:
service:
name: jenkins-service
port:
number: 80
If you now visit https://example.com/jenkins then it will get internally rewritten to https://example.com/jenkins/generic-webhook-trigger/invoke. No redirects involved.

Related

Unable to access another site hosted with a different host name with kubernetes NGINX ingress

My modern application hosted within kubernetes works based on the host and the path app.dev.sandbox.com/ (root / path). I need access external legacy application hosted on EC2/VM with host name app-legacy.dev.sandbox.com via the kubernetes ingress. I want to be able to access the legacy sites pages with the same host name of application deployed on kubernetes like this app.dev.sandbox.com/nbs/login or app.dev.sandbox.com/nbs/homepage.do should actually request app-legacy.dev.sandbox.com/abc/login or app-legacy.dev.sandbox.com/nbs/homepage.do but preserve the modern hostname of app.dev.sandbox.com when displaying the page.
This is needed because, we are using strangler fig pattern to navigate between legacy and modern pages seamlessly. The request needs to be captured by the kubernetes ingress when the user clicks on certain links on the legacy pages. I am currently using the following external service and ingress resource.
apiVersion: v1
kind: Service
metadata:
name: nbs-legacy
spec:
type: ExternalName
externalName: app-legacy.dev.sandbox.com
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-resource
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-production"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/preserve-host: "true"
spec:
ingressClassName: nginx
tls:
- secretName: app.dev.sandbox.com
hosts:
- app.dev.sandbox.com
rules:
- host: app.dev.sandbox.com
http:
paths:
- path: /nbs #I would like the path to be appened to the nbs-legacy request. For example /nbs/login should be appened while requesting app-legacy.dev.sandbox.com/nbs/login
pathType: Exact
backend:
service:
name: nbs-legacy
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: modern-service
port:
number: 80
I am using nginx ingress. I tried with the above manifests but did not work.
I am expecting the following:
Navigate to external legacy application seamlessly while preserving the host name of the kubernetes.
Capture path from the request and pass that to legacy host name request. For example, /nbs/login or /nbs/dashboard should be passed when requesting legacy application pages.
Avoid routing of anything that starts with /nbs(eg: /nbs/abc) to the default root / path.

Traefik - Middleware redirectRegex doesn't work

I have a simple website running in my Kubernetes cluster and exposed to the Internet using Traefik. My Ingress object looks like this (the only things I've changed here are the name and domain names):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
kubernetes.io/ingress.class: traefik
name: my-cool-website
namespace: default
spec:
rules:
- host: my-cool-website.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-cool-website
port:
number: 80
- host: www.my-cool-website.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-cool-website
port:
number: 80
tls:
- hosts:
- my-cool-website.com
- www.my-cool-website.com
secretName: my-cool-website-tls
This works. It allows me to access the site either from my-cool-website.com or from www.my-cool-website.com. But what I'd like to have happen is that if someone visits the former, that Traefik automatically redirects them to the latter. I found a couple of guides online that recommended creating a Traefik middleware, so I did just that, but unfortunately it doesn't work as intended. Here is my middleware definition:
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: my-cool-website-force-www
spec:
redirectRegex:
regex: ^https?://my-cool-website.com/(.*)
replacement: https://www.my-cool-website.com/${1}
And then I add the following annotation back to the ingress object:
traefik.ingress.kubernetes.io/router.middlewares: my-cool-website-force-www
But as soon as I do that, it breaks my web app. By that I mean, when that annotation is applied, instead of serving my website, I start seeing a generic nginx page that looks like this when I try to access the domain (and also it does not do the redirect):
I have to assume this Hello World page is being served by Traefik as some sort of generic default page, as it definitely does not originate from my pod. So that tells me something about this middleware (or perhaps how I'm calling it with the annotation) isn't working. What am I missing?
I figured it out, by port-forwarding to the Traefik Dashboard and looking at the service there. It was showing an error for the middleware not found. I then clicked over to the middlewares and realized that they end up with a longer canonical name. So in my annotation I had to change the reference from my-cool-website-force-www to default-my-cool-website-force-www#kubernetescrd and then everything worked.

Multiple FastAPI services on the same page

I am using FastAPI to create multiple services.
It works in a somewhat strange way, but basically I have a generic FastAPI service and a CI/CD that downloads multiple installable packages to create different services from it. It's all packaged neatly, I create multiple Docker images, then they are served by Kubernetes using deployments, services (separate pods, one container per pod) and ingress.
Now, I've configured ingress to serve those services from the same host. The only thing that is different for those services is the app root path. So for example I have:
foo.example.com/foo - 1st service
foo.example.com/bar - 2nd service
If I go to foo.example.com/foo/docs, I am greeted with Swagger documentation and I can select the app root path at the top of the page. Unfortunately, there's only one option: /foo. Same for the second service, but this time it's /bar.
Now I know that those services run in separate containers in separate pods in my k8s cluster, but I was wondering if there's a way to for example go to foo.example.com or to a particular service like foo.example.com/foo and have all the services listed in Swagger's drop-down menu, so I can select any of them and be redirected to it. It would be huge improvement from a user experience point of view.
#Edit, Ingress manifest file:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /$1
external-dns/manage-entries: "true"
spec:
rules:
- http:
paths:
- path: /foo/(.*)
pathType: Prefix
backend:
service:
name: foo-service
port:
number: 80
host: test.example.com
- http:
paths:
- path: /bar/(.*)
pathType: Prefix
backend:
service:
name: bar-service
port:
number: 80
host: test.example.com
- http:
paths:
- path: /baz/(.*)
pathType: Prefix
backend:
service:
name: baz-service
port:
number: 80
host: test.example.com

REST URI with NGINX Ingress Controller

I'm trying to configure NGINX Ingress controller as the correct entry point to my Kubernetes cluster. Inside the cluster, I've created two REST Web services as well as frontend application. I'm trying to achieve the following scenario.
When the ingress IP is hit without any parameters it should be routed
to the frontend app. Example: 192.168.1.20 should lead to frontend
service on port 80.
When parameters are given, the request should be
routed to correct REST service. Example:
192.168.1.20/first-rest/api/flower?id=1 should route the request to
the first-rest service so that it could return the flower with id =
1.
I can correctly access the frontend application but when trying to access any REST service I'm getting 404 error or no response at all. First-rest, Second-rest and frontend are running correctly and are accessible when configured as LoadBalancer services. With Nginx, they are configured as ClusterIp services.
My ingress configuration
------------------------
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: main-routes
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- http:
paths:
- path: /first-rest(/|$)(.*)
backend:
serviceName: first-rest
servicePort: 8090
- path: /second-rest(/|$)(.*)
backend:
serviceName: second-rest
servicePort: 9000
- path: /(.*)
backend:
serviceName: frontend
servicePort: 80
It seems like NGINX is cutting short my URL parameters that are required for my REST API. Is there any way to pass the right URL path so that `192.168.1.20/first-rest/api/flower?id=1` would be routed to `[first-rest add and port]/api/flower?id=1` ?
You need to specify the ingress path type otherwise , depending on the ingress class specific implementation it will default to either exact or prefix (I assume in your case it is defaulting to exact)
So, you need to do something like
spec:
rules:
- http:
paths:
- path: /first-rest
pathType: Prefix
backend:
serviceName: first-rest
servicePort: 8090
see docs on ingress path here

Minikube | Ingress Service - Bad Request

I'm working on a single-node cluster which works fine with docker-compose but the reconfiguration of the same setup using Minikube Ingress Controller gives me a Bad Request response.
Bad Request
Your browser sent a request that this server could not understand.
Reason: You're speaking plain HTTP to an SSL-enabled server port.
Instead use the HTTPS scheme to access this URL, please.
My Ingress looks like this:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: emr-cluster-ip-service
port:
number: 443
- path: /?(.*)
pathType: Prefix
backend:
service:
name: erp-cluster-ip-service
port:
number: 8069
How to fix this?
You are exposing HTTPS service on HTTP ingress, which is not the right thing to do. You might want to do one of the following:
Configure TLS-enabled ingress.
Configure TLS passthough on ingress object.
In both cases you also need to set nginx.ingress.kubernetes.io/ssl-redirect: "true"