Nginx Ingress rules not find a match - regex

I'm struggling on an Ingress configuration in yaml because the pattern matching seems not to work.
I would like the frontend-lb ClusterIP Service for the frontend deployment to respond to any of these:
https://example.com
https://example.com/home
https://example.com/login
... any other without /api/
And the backend-lb ClusterIP Service for the backend deployment to respond to any of these:
https://example.com/api/...
The yaml for the ingress rules is the following:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- secretName: example-com-tls
hosts:
- example.com
rules:
- host: example.com
http:
paths:
- path: /api
backend:
serviceName: gateway-lb
servicePort: 80
- path: /
backend:
serviceName: frontend-lb
servicePort: 80
The result is that every backend url is recognized as a frontend url and gives back 404 Not Found.
I've tried many other regexp and also I've tried to exclude /api in the frontend path (!?api).* but with no success.
UPDATE:
What I see with the logs it's like in the backend the url path gets blanked because if I call:
https://example.com/api/javalin-api-gateway/login
I get this error:
Not found. Request is below context-path (context-path:
'/javalin-api-gateway')
While when I call the frontend with a specific url path:
https://example.com/home
The /home controller is effectively called (it doesn't get blanked).
If I call the backend service directly (if the service is a LoadBalancer) with the same url:
http://192.168.64.17:31186/javalin-api-gateway/login
I get the right response, signal that the backend part is working properly.
How is possible that only the backend service doesn't receive the complete path?

Related

Traefik Apply Middleware On Domain Names

There's a need to apply a request body size limit to certain domain names via Traefik.
The Traefik middleware is:
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: request-limits
spec:
buffering:
maxRequestBodyBytes: 10485760
retryExpression: "IsNetworkError() && Attempts() < 2"
And this can be applied globally via:
additionalArguments:
- --entrypoints.websecure.http.middlewares=traefik-request-limits#kubernetescrd
How can this middleware be applied to certain domain names?
What I've tried - remove the above additionalArguments, and replace it with:
http:
routers:
http-specific:
rule: "HostRegexp(`{name:(.*-)(service|mock|proxy)\\.(.*)\\.(example\\.com}`)"
entrypoints:
- websecure
middlewares:
- request-limits
service:
- noop#internal
However - the above route is not getting created.
Any tips or pointers would be much appreciated.
Traefik Feature Status
List item
https://github.com/traefik/traefik/issues/5098

Hosting django on aks behind nginx-ingress

I am trying to host a django website on Azure kubernetes service behide nginx-ingress, and I would like my django web show under a path.
e.g. when access the default admin site, I would like to access it at http://example.com/django/admin instead of http://example.com/admin
I tried the configure below, when I access http://example.com/django/admin it will forward me to http://example.com/admin and show me 404 error from default ingress backend, as I set django debug to ture I assume this mean ingress did not send my request to my django service
# path example
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /django(/|$)(.*)
so I try to curl -I -k http://example.com/django/admin, and it show something like below
HTTP/1.1 301 Moved Permanently
Server: openresty/1.15.8.2
Date: Wed, 06 Nov 2019 04:14:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Location: /admin/
The same thing happen to any valid page in the site, if I curl -I -k http://example.com/django/any_valid_page it show below:
HTTP/1.1 301 Moved Permanently
Server: openresty/1.15.8.2
Date: Wed, 06 Nov 2019 04:14:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Location: /any_valid_page/
I wonder it is caused by I am doing the test with the default django development web server? (i.e. python manage.py runserver)
If I try to host it at root like below, everything is fine...
# root example
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /
Trying adding this
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
ingress.kubernetes.io/rewrite-target: /django
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- backend:
serviceName: django-service
servicePort: 80
path: /django
Starting in Version 0.22.0, ingress definitions using the annotation
nginx.ingress.kubernetes.io/rewrite-target are not backwards
compatible with previous versions. In Version 0.22.0 and beyond, any
substrings within the request URI that need to be passed to the
rewritten path must explicitly be defined in a capture group. So make
sure you have right version.
When using SSL offloading outside of cluster it may be useful to enforce a redirect to HTTPS even when there is no TLS certificate available. This can be achieved by using the nginx.ingress.kubernetes.io/force-ssl-redirect: "true" annotation in the particular resource.
I think your Ingress configuration file should look like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: django-ingress
labels:
app: django
namespace: default
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: example.com
http:
paths:
- path: /django(/|$)(.*)
backend:
serviceName: django-service
servicePort: 80
If you get 404 error, there is possible solution:
Please change https to http in the curl command?:
curl --resolve your-host:80:xx.xxx.xx.xxx http://my-host:80
To get the IP from kubectl get ing command, it is necessary to
enable the reporting Ingress status feature. Take a look on: reporting-ingress-status.
There is the default server in the Ingress controller. It returns
the Not Found page with the 404 status code for all requests for
domains for which there are no Ingress rules defined. Those requests
are not shown in the access log.
Since you're getting a 404, this means that the host header of your
requests doesn't match with the host field in the Ingress resource.
To set the host header in curl, please see previous curl
commands. Optionally, you can also do:
curl http://<ip> -H "host: example.com"
Please take a look on ngnix-ingress, server-side-https-enforcement-nginx.
This is a problem from Django's side. Whenever the admin is not logged in, the /django/admin results in a redirect to /admin/. In this case, if you just replace /django/admin/ with /django/admin/ in the browser URL field it will work and open django admin login.
So basically Django's built-in redirect conflicts with the Ingress's rewrite module.

Ingress Controller on Minikube not routing correctly flask POST request

I have a simple Flask app. It worked fine when I connected to it via port-forwarding to send the HTTP Post request directly to the Service.
from flask import Flask, request
import redis
from rq import Queue
from worker import job_worker
UPLOAD_FOLDER = './uploads/'
app = Flask(__name__)
r = redis.Redis()
q = Queue(connection = r)
#app.route('/', methods=['POST'])
def upload():
scale = int(request.form['scale'])
q.enqueue(job_worker, scale)
return ""
if __name__ == "__main__":
app.run()
I also have a simple index.html file in an nginx container which is served at port 80. It does an ajax POST request to "/upload". Which if you look at the ingress controller, should convert that to a port 5000 request and strip away the "upload"
The flask app gets served at port 5000
Here is the ingress controller:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: emoji-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /upload
backend:
serviceName: emoji-backend
servicePort: 5000
- path: /
backend:
serviceName: emoji-frontend
servicePort: 80
And for completeness, the emoji-backend service:
apiVersion: v1
kind: Service
metadata:
name: emoji-backend
labels:
app: emoji-backend
tier: backend
spec:
type: LoadBalancer
ports:
- port: 5000
selector:
app: emoji-backend
tier: backend
I get a 502 bad gateway without really any indication except the ingress log does say this:
2019/09/29 21:41:04 [error] 2021#2021: *78651 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.64.1, server: _,
request: "POST /upload HTTP/2.0", upstream: "http://172.17.0.4:5000/", host: "192.168.64.5", referrer: "https://192.168.64.5/"
"http://172.17.0.4:5000/" is the correct endpoint and port for the emoji-backend service.
Adding the following line fixed it:
app.run(debug=True,host='0.0.0.0',port=5000)
However, it took me a while to figure that out because at first when I tried it my docker image was not updating when I re-deployed.

Capturing groups in istio

How can I use a part of matched URI in destination rule in istio?
Trying to achieve something like this:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
spec:
http:
- match:
- uri:
regex: "^/foo/(.+)/?$"
route:
- destination:
host: bar-$1
port:
number: 80
As far as I know this is not possible at all.
Cant provide you any confirmation link on this.
The similar question was here in the past:
can istio support route to different service by dynamic part of uri path

SSL issues with jinja HTTPS load balancer - Google Cloud

I have been able to deploy this load balancer as a HTTP load balancer, and am now trying to deploy this as a HTTPS load balancer. I am attempting this with .JUNJA and .YAML on Google Cloud.
I keep getting the error: Invalid value for field 'resource.sslCertificates[0]': 'www.example.com'. The\\ URL is malformed.
resources:
- name: web-lb-hc #Create a health check for the backend
type: compute.v1.httpsHealthCheck
properties:
port: 443
requestPath: /
- name: web-url-map #Required to map url to backend
type: compute.v1.urlMap
properties:
defaultService: $(ref.backend.selfLink)
- name: backend
type: compute.v1.backendService #Deployment of backend for VM's
properties:
port: 443
portName: https
protocol: HTTPS #Defeined HTTP port for communication with backends
backends:
- name: backend
balancingMode: UTILIZATION
capacityScaler: 1.0
group: $(ref.web-ins-group.selfLink)
maxUtilization: 0.8
connectionDraining:
drainingTimeoutSec: 300
healthChecks:
- $(ref.web-lb-hc.selfLink)
- name: web-http-proxy
type: compute.v1.targetHttpsProxy
properties:
urlMap: $(ref.web-url-map.selfLink)
sslCertificates: ["www.example.com"]
- name: web-ipaddress
type: compute.v1.globalAddress
- name: web-http-forwardingrule #Creation of forwarding rule
type: compute.v1.globalForwardingRule
properties:
target: $(ref.web-http-proxy.selfLink)
IPAddress: $(ref.web-ipaddress.address)
IPProtocol: TCP #Chosen protocol
portRange: 443-443
Any help with this would be much appriciated!
sslCertificates is an array of urls.
To determine the url for an SSL certificate follow these steps.
Get a list of SSL certificates:
gcloud compute ssl-certificates list
Get the details for the SSL certificate:
gcloud compute ssl-certificates describe NAME
In the returned data near the bottom look for selfLink. This is the value you want to use. This value will look like this:
https://www.googleapis.com/compute/v1/projects/development/global/sslCertificates/production-lb