Kubernetes: Issue with 2 Ingress object with regex path - kubernetes

I have 2 ingress objects
first-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: first-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt"
acme.cert-manager.io/http01-edit-in-place: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
tls:
- hosts:
- www.example.com
secretName: example-tls
rules:
- host: www.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: first-service
port:
number: 80
second-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: second-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /$2
cert-manager.io/cluster-issuer: "letsencrypt"
acme.cert-manager.io/http01-edit-in-place: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
tls:
- hosts:
- www.example.com
secretName: example-tls
spec:
rules:
- host: www.example.com
http:
paths:
- path: /test(/|$)(.*)
pathType: Prefix
backend:
service:
name: second-service
port:
number: 80
The expectation is:
www.example.com/test/whatever -> second-service
www.example.com -> first-service
What I saw is that both www.example.com/test/whatever and www.example.com reach to the first-service
If I change the second-ingress to replace the regex with a static path, it will work. www.example.com/test/whatever will hit the second-service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: second-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt"
acme.cert-manager.io/http01-edit-in-place: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
tls:
- hosts:
- www.example.com
secretName: example-tls
spec:
rules:
- host: www.example.com
http:
paths:
- path: /test
pathType: Prefix
backend:
service:
name: second-service
port:
number: 80
Any idea why regex does not work? I need the rewrite-target rule, which is the reason I use the regex

The regex that you posted is exactly the same as from the example from the NGINX Ingress Controller docs, but the yaml file is wrong (one mistake with the double spec, below more information) - I tested your yamls files on Kubernetes v.1.21, NGINX Controller version v.1.0.2 and Cert Manager v1.6.1.
A few notes/thoughts about what may be wrong:
Instead of using twice spec: (second-ingress.yaml) , use spec only once. I did not observe the change in the behaviour in the prefixes itself, but in the kubectl get ing command for the second ingress there was no 443 port specified. When I ran kubectl get ing second-ingress -o yaml, there was missing TLS part.
Before (wrong setup):
second-ingress.yaml file:
spec:
tls:
- hosts:
- www.example.com
secretName: example-tls
spec:
rules:
kubectl get ing output (missing port 443 for the second ingress):
user#cloudshell:~/ingress-two-services $ kubectl get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
first-ingress <none> www.example.com xx.xx.xx.xx 80, 443 5h7m
second-ingress <none> www.example.com xx.xx.xx.xx 80 50m
kubectl get ing second-ingress -o yaml output (missing tls part):
user#cloudshell:~/ingress-two-services $ kubectl get ing second-ingress -o yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
...
spec:
rules:
- host: www.example.com
http:
paths:
- backend:
service:
name: second-service
port:
number: 80
path: /test(/|$)(.*)
pathType: Prefix
status:
...
After (good setup):
second-ingress.yaml file:
spec:
tls:
- hosts:
- www.example.com
secretName: example-tls
rules:
kubectl get ing output:
user#cloudshell:~/ingress-two-services $ kubectl get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
first-ingress <none> www.example.com xx.xx.xx.xx 80, 443 5h18m
second-ingress <none> www.example.com xx.xx.xx.xx 80, 443 61m
kubectl get ing second-ingress -o yaml output:
user#cloudshell:~/ingress-two-services $ kubectl get ing second-ingress -o yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
...
spec:
rules:
- host: www.example.com
http:
paths:
- backend:
service:
name: second-service
port:
number: 80
path: /test(/|$)(.*)
pathType: Prefix
tls:
- hosts:
- www.example.com
secretName: example-tls
status:
...
Another thing worth noting that the modified configuration for the second ingress is behaving differently than the one that does not work for you.
Using nginx.ingress.kubernetes.io/rewrite-target: /$2 + path: /test(/|$)(.*):
www.example.com/test/ will rewrite the request to the pod to /
www.example.com/test/whatever will rewrite the request to the pod to /whatever
However, using only path: /test:
www.example.com/test/ will rewrite the request to the pod to /test
www.example.com/test/whatever will rewrite the request to the pod to /test/whatever
Please make sure that you are using a proper setup for which your app is designed to.
Other tips:
make sure that ingress is applied by running kubectl get ing command
get the logs of the NGINX Ingress Controller. Get the name of the Ingress Controller pod (kubectl get pods -n ingress-nginx) and then run kubectl logs -n ingress-nginx ingress-nginx-controller-{...}. Check how your requests are handled and where (which service) they are forwarded to
check the logs of the pods from the deployments and check how they are handling request (kubectl logs {pod-name})
if you are using some out-dated version of the Kubernetes better upgrade it

Related

How to get the k8s ingress wildcard in the host and put it on the url path

I use k8s to deploy my services.
I hope when I access the ingress host foo.example.com and the ingress forward it to server:8000/proxy/foo. The subdomain foo is dynamic and can be changed to any word. The expected result is as below:
foo.example.com -> server:8000/proxy/foo
bar.example.com -> server:8000/proxy/bar
......
I knew the ingress host could use wildcards and use ingress-nginx rewrite can rewrite the url path. I use the ingress-nginx controller. The ingress yaml file like as below:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /proxy/${wildcard}/$1 # Could I use the host wildcard here?
name: nginx-forward
spec:
rules:
- host: *.example.com # I want to get the subdomain wildcard
http:
paths:
- backend:
service:
name: service
port:
number: 8080
path: /(.*)
pathType: Prefix
tls: # update this attribute
- hosts:
- *.example.com
secretName: my-secret
How could I use k8s ingress or other things to get what I want? Thanks.
You can use server snippet to get the subdomain:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/server-snippet: |
server_name ~^(?<subdomain>\w+)\.example\.com$;
nginx.ingress.kubernetes.io/rewrite-target: /proxy/$subdomain/$1
name: nginx-forward
spec:
rules:
- http:
paths:
- backend:
service:
name: service
port:
number: 8080
path: /(.*)
pathType: Prefix

K8S traffic to one service via two separate ingress (http + https)

So I have a bunch of services running in a cluster, all exposed via HTTP only ingress object, example:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: some-ingress
spec:
ingressClassName: nginx
rules:
- http:
paths:
- backend:
service:
name: some-svc
port:
number: 80
path: /some-svc(/|$)(.*)
pathType: Prefix
They are accessed by http://<CLUSTER_EXTERNAL_IP>/some-svc, and it works ofc.
Now I want to create an additional ingress object for every service which will force SSL connections and allow the use of a domain instead of an IP address.
The problem is that the newer SSL ingresses always return 404 while testing the connection.
The manifests are as follows:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "some-ingress-ssl"
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
ingress.kubernetes.io/app-root: "/some-svc"
spec:
tls:
- hosts:
- foobar.com
secretName: foobar-tls
rules:
- host: foobar.com
http:
paths:
- path: /some-svc(/|$)(.*)
pathType: Prefix
backend:
service:
name: some-svc
port:
number: 80
tests (foobar.com point to CLUSTER_EXTERNAL_IP):
> curl -I http://<CLUSTER_EXTERNAL_IP>/some-svc
HTTP/1.1 200 OK
> curl -I https://foobar.com/some-svc
HTTP/2 404
Is it possible to have both ingresses simultaneously? (one enforcing SSL, the other not)
If so what am I doing wrong here?
Figured out I was missing this annotation:
nginx.ingress.kubernetes.io/rewrite-target: /$2
in SSL ingress...
works like a charm now, maybe someone will find this usefull

How to create nginx ingress rules for services in 3 different namespaces in azure Kubernetes cluster

I have 3 services in 3 different namespaces I want my ingress rules to map to these backends, on path based routes.
Can someone please guide on the same.
I am using nginx ingress inside azure Kubernetes cluster.
A basic example with an assumption that your nginx ingress is working correctly inside your AKS would be following:
List of Pods with their Services:
Pod
Namespace
Service name
nginx
alpha
alpha-nginx
nginx
beta
beta-nginx
nginx
omega
omega-nginx
Ingress definition for this particular setup:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: alpha-ingress
namespace: alpha
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: "kubernetes.kruk.lan"
http:
paths:
- path: /alpha(/|$)(.*)
pathType: Prefix
backend:
service:
name: alpha-nginx
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: beta-ingress
namespace: beta
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: "kubernetes.kruk.lan"
http:
paths:
- path: /beta(/|$)(.*)
pathType: Prefix
backend:
service:
name: beta-nginx
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: omega-ingress
namespace: omega
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: "kubernetes.kruk.lan"
http:
paths:
- path: /omega(/|$)(.*)
pathType: Prefix
backend:
service:
name: omega-nginx
port:
number: 80
In this example Ingress will analyze and rewrite the requests for the same domain name to send the traffic to different namespaces i.e. alpha, beta, omega.
When you've have finalized your Ingress resource, you can use curl to validate your configuration.
curl kubernetes.kruk.lan/alpha | grep -i "<h1>"
<h1>Welcome to nginx from ALPHA namespace!</h1>
curl kubernetes.kruk.lan/beta | grep -i "<h1>"
<h1>Welcome to nginx from BETA namespace!</h1>
curl kubernetes.kruk.lan/omega | grep -i "<h1>"
<h1>Welcome to nginx from OMEGA namespace!</h1>
I'd encourage you to check following docs on rewrites:
Kubernetes.github.io: Ingress-nginx: Examples: Rewrite
PS: Pods are default nginx containers/images with added text to /usr/share/nginx/html/index.html

Kubernetes Ingress on Docker Desktop

I am trying to use Nginx ingress to access kubernetes dashboard on my local pc. The step I followed are:
Getting nginx ingress
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.48.1/deploy/static/provider/cloud/deploy.yaml
Getting kubernetes dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml
Applying this ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
spec:
rules:
- host: "kubernetes.docker.internal"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: kubernetes-dashboard
port:
number: 443
Checking that my host file has this line
127.0.0.1 kubernetes.docker.internal
If I try to open http://kubernetes.docker.internal/ on my browser I get "Http Error 400 this page isn't working", while on postman I get an error 400 with message "Client sent an HTTP request to an HTTPS server."
How can I resolve?
I resolved adding annotations section in ingress yaml.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
rules:
- host: "kubernetes.docker.internal"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: kubernetes-dashboard
port:
number: 443

Is it possible to use same hostname with multiple Ingress resources running in different namespaces?

I want to use the same hostname let's say example.com with multiple Ingress resources running in different namespaces i.e monitoring and myapp. I'm using Kubernetes nginx-ingress controller.
haproxy-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: haproxy-ingress
namespace: myapp
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
# fill in host here
- example.com
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: haproxy
port:
number: 80
grafana-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grafana-ingress
namespace: monitoring
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
tls:
- hosts:
- example.com
rules:
- host: example.com
http:
paths:
# only match /grafana and paths under /grafana/
- path: /grafana(/|$)(.*)
pathType: Prefix
backend:
service:
name: grafana
port:
number: 3000
When I'm doing curl example.com then it is redirecting me to the deployment running in namespace one(as expected) but when I'm doing curl example.com/grafana then still it is redirecting me to namespace one deployment.
Please help.
Yes it is possible.
There can be two issues in your case.
One is you don't need the regex path for grafana ingress. Simple /grafana path will be fine with path type Prefix as with path type Prefix any /grafana/... will be redirected associated service. So the manifest file will be:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grafana-ingress
namespace: monitoring
spec:
tls:
- hosts:
- example.com
rules:
- host: example.com
http:
paths:
- path: /grafana
pathType: Prefix
backend:
service:
name: grafana
port:
number: 3000
And the second issue can be the related service or deployment might not be the under same namespace monitoring. Please make sure the deployment/service/secret or other resources needed for grafana remains under the same namespace monitoring.