Is there an order to how an Ingress rule is chosen? - kubernetes

Is there an order to Ingress rule specification i.e. will the first qualifying rule be honored?
The intention of following spec is to route all requests that do not have headers Host: foo.com and Host: bar.com and route them to service3. I am not sure if the spec is syntactically correct and more so, if it will serve the desired purpose?
spec:
rules:
- host: foo.com
http:
paths:
- backend:
serviceName: service1
servicePort: http
path: /
- host: bar.com
http:
paths:
- backend:
serviceName: service2
servicePort: http
path: /a/b/c
- http:
paths:
- path: /a/b/c
backend:
serviceName: service3
servicePort: http
Don't think it matters, but I am using the Contour Ingress controller.

I am not familar with Contour, I just quickly browse the doc.
How Contour works with Ingress rule is not clear from its document. I think Contour perfer using its CRD IngressRoute to specify how request rule works.
So I infer your Ingress behavior from offical kubernetes ingress rule:
Request with host: foo.com will route to service1 or service3
Request with host: bar.com will route to service2 or service3
Other request will route to service3

What you have created in syntactically correct and should route http://*/a/b/c and http://*/a/b/c/* to service3 in most ingress controllers.
An Ingress definition is just data that is supplied to an ingress controller though. The implementation of converting that data into config is ingress controller specific.
Code
Contours route config looks to be rooted by a "virtualhost" name. In the route.go code I can't see any handling of the "no virtualhost" case.
From the route.go tests it looks like a virtualhost of * is how the default host is handled.
This sorting of virtualhosts would hopefully always put * in the right place for contour to default as you describe but then I think there is also an interface for this config to be applied to envoy, the actual proxy process.
So it would appear (without testing) that no matter what order you put the Ingress definition in, contour will sort out the default host routes for you as '*'
This kind of makes sense when you consider contour also supports a custom resource definition of IngressRoute which only allows one virtualhost per definition. These CRDs as a group have no specific ordering so the sort is required.

Related

Route incoming traffic to pods with labels specified in URL

I would like to use parameter passed in URL to route traffic to pods with specific labels. I can't tell in advance which values of app will be used, so it must somehow dynamically create Ingres rules/Services:
Example:
https://foo.bar.com/?app=application1 --> route to service/pods with label: app=application1
https://foo.bar.com/?app=application2 --> route to service/pods with label: app=application2
https://foo.bar.com/?app=non-existing-app --> route to service/pods without any label app.
I am wondering if it's possible using variables in path and corresponding services, here is the idea and pseudo-code:
kind: Ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /app=${APPLICATION}
backend:
serviceName: app-${APPLICATION}
servicePort: 8080

Setting service name dynamically based on path in Kubernetes Ingress

In a Kubernetes ingress, I'd like to dynamically change the service name to a match on a certain part of the path in an ingress. Something like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example
spec:
rules:
- host: example.com
http:
paths:
- path: /api/(^/+)/(.*)
backend:
serviceName: api-(&1)
servicePort: 80
Where &1 in the example above would resolve to the first regular expression match in the string /api/(^/+)/(.*)
So basically if a path is something like this /api/asset/fetch/anything-at-all, then it would direct the call to a service named api-asset
I know that this can potentially lead to some security issues, but if I force the prefix 'api-' to the service name then only services with the prefix 'api-' would technically be exposed to the internet. And all other services would still be protected inside the Kubernetes cluster. I can even change the prefix to be 'public-' for simplicity's sake.
Is this something that is possible? And what is the correct syntax to set it up?

k8s nginx ingress TLS rules: cert vs. paths

I am struggling to get my nginx ingress (on AWS EKS) working with path rules and TLS.
The ingress is from
here
A snippet from the Ingress looks like:
spec:
tls:
- hosts:
- example.com
secretName: ingress-tls
rules:
- host: example.com
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 443
This ingress creates the AWS network load balancer, with a URL like
https://xyz.elb.us-west-1.amazonaws.com/
I am updating the
ingress-tls
secret with a certificate using
cert-manager.
When I access the ingress using the NLB URL
https://xyz.elb.us-west-1.amazonaws.com/api, I get
GOOD: Correct routing based on the path rules from the ingress definition (i.e. it ​goes to my
api-service as expected)
BAD: Certificate errors since I'm not accessing the ingress with the domain that the certificate is for.
When I access the ingress using the correct domain e.g.
https://example.com/api which is what I want to do, I get:
BAD:
404, it doesn't respect my path rules, and goes to
upstream-default-backend instead.
GOOD: certificate all good, it’s the one for
example.com that
cert-manager configured.
I tried removing the
host: example.com from the
rules:, which gives me:
GOOD: Correct routing based on the path rules from the ingress definition
BAD: Certificate errors, it serves up the default ingress “Fake” certificate instead of the one for
example.com, I guess since the
host is missing from the rules, though not sure of the exact reason.
Can someone please help me get
GOOD
GOOD
I’m at a loss here.
After staring at this for several more hours, and digging through the nasty chunk of lua that is the
nginx.conf for this, I found it! Maybe someday someone will have this problem, and might find this useful.
The problem was:
rules:
- host: example.com
- http:
This is defining (I think) a
host with no forwarding rules, then then some
http forwarding rules without a host. What I had intended was obviously that the forwarding rules would be for the host.
And that would be:
rules:
- host: example.com
http:
I have to say that I'm now even less of a fan of YAML than I was previously, if that's even possible.

How do I block paths in my (k8s) ingress?

I have a subset of paths that I expose with an ingress.
I'd like to block them for anyone coming through the ingress.
I'm trying this with a GCE Ingress
Adding a rule like:
- host: my.example.com
http:
paths:
- backend:
serviceName: dead-end-backend
servicePort: 80
path: /private
This backend is designated dead-end using Nginx default backend deployment/service but it's not working well.
I'm not asking how to use the default-backend (but it solve it).
I'm asking for a proper way to do this.
I'm not sure it's the best thing to do but what I did was use a "default-backend" service which returns error for anything.
Then I added each route I wanted to block to get to that default backend.
Like the saying - if it's silly, but it's working, then it's not silly.

Can later ingress subpaths override earlier ingress parent paths?

I have a Kubernetes ingress that I want to be the default for all paths on a set of hosts, provided there is not a more specific match:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: default-ing
spec:
rules:
- host: host1.sub.example.com
http:
paths:
- backend:
serviceName: my-default-service
servicePort: http
# Note: here we specify the root path intended as a default
path: /
- backend:
serviceName: my-default-service
servicePort: http
path: /route/path/to/default
A second ingress defines a custom service for a specific path:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: special-ing
spec:
rules:
- host: host1.sub.example.com
http:
paths:
- backend:
serviceName: special-service
servicePort: http
path: /special
I would expect that the order of adding/deleting the ingresses would not matter, or at least I could have some way of indicating that the path: / in default-ing is always to be ordered last.
When I try the above, the routing is fine as long as I add special-ing before default-ing (or alternatively, add default-ing, then the special-ing, then delete default-ing and re-add it again). When I add them as default-ing, then special-ing, requests to /special are routed to my-default-service instead of special-service.
I want the order of adding/deleting to be independent of the routing that is generated by nginx-ingress-controller, so that my kubectl manipulations are more robust, and if one of the ingresses is recreated nothing will break.
I'm using nginx-ingress-controller:0.19.0
Thanks for any help you can offer!
The short answer is no. I believe your configs should be disallowed by the nginx ingress controller or documented somewhere. Basically what's happening when you have 2 hosts rules with the same value: host1.sub.example.com one is overwriting the other in the server {} block in the nginx.conf that your nginx ingress controller is managing.
So if your add default-ing before special-ing then special-ing will be the actual config. When you add special-ing before default-ing then default-ing will be your only config, special-ing is not supposed to work at all.
Add special-ing, and the configs look something like this:
server {
server_name host1.sub.example.com;
...
location /special {
...
}
location / { # default backend
...
}
...
}
Now add default-ing, and the configs will change to like this:
server {
server_name host1.sub.example.com;
...
location /route/path/to/default {
...
}
location / { # default backend
...
}
...
}
If you add them the other way around the config will look like 1. in the end.
You can find more by shelling into your nginx ingress controller pod and looking at the nginx.conf file.
$ kubectl -n <namespace> exec -it nginx-ingress-controller-pod sh
# cat /etc/nginx/nginx.conf
Update 03/31/2022:
It seems like on newer nginx ingress controller versions. All the rules with the same host get merged into a server block in the nginx.conf