I'm trying to set up simple rule for my backend service secured by basic authentication. It works perfectly fine when I try to send request with curl or postman, the issue is when my frontend application tries to do the same. As I understand browsers sends preflight requests (OPTIONS method) which is used for CORS policy check. The issue is that Authorization header is not being added to this request which resolves with server responding 401. Is there a way to make Ingress omit authorization for specific methods?
Here is my Ingress (nginx) config:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: somesome-routing
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'
spec:
rules:
- host: somesome.com
http:
paths:
- path: /
backend:
serviceName: backend-service
servicePort: 80
I ended up with workaround including two ingresses - one secured which routes traffic to frontend application and second one insecure which routes to my own nginx based api gateway which itself defines authentication rules:
#secure-ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: secure-routing
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-staging"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'
spec:
rules:
- host: somesome.com
http:
paths:
- path: /
backend:
serviceName: frontend-service
servicePort: 80
#ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: routing
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-staging"
spec:
rules:
- host: api.somesome.com
http:
paths:
- path: /
backend:
serviceName: api-gateway-service
servicePort: 80
# api-gateway/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
log_format timed_combined '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time';
access_log /var/log/nginx/access.log timed_combined;
client_max_body_size 20M;
default_type application/octet-stream;
include mime.types;
keepalive_timeout 65;
resolver ${RESOLVER} ipv6=off;
sendfile on;
server {
listen 80;
server_name localhost;
gzip on;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain application/json application/xml;
location ~* ^/backend_path {
add_header 'Access-Control-Allow-Headers' 'authorization,content-type,iplanetdirectorypro';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, OPTIONS, DELETE';
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_send_timeout 6000;
proxy_read_timeout 6000;
proxy_pass http://${BACKEND}$request_uri;
}
}
}
Auth or external auth breaks preflight / CORS because the default template does not handle OPTIONS requests the correct way. In fact, the auth handling is the same for all HTTP methods.
So the question to answer is "How can I handle OPTIONS requests differently?" or "How can I apply the auth only to specific methods".
You can achieve this via configuration snippets. (Look here: nginx-ingress docs)
You can find a similar question here: How can I put basic auth on specific HTTP methods in ngnix ingress?
Related
I have many services operating happily behind an Nginx Ingress on EKS for some time. Recently I've been trying to deploy a next.js app behind this Ingress but I can't get it to work.
The only solutions I can find online seem to be adding various header as an annotation in the ingress.yaml but these have no effect. I can't get passed the 404. A blank page is loaded as next.js can't load the files it needs.
The 404 stems from next.js rather than Nginx, so the request is still at least reaching the container. The app works correctly when run locally using Docker.
I've tried variations of the config below with no success. Not sure if the rewrite is interfering with things, but doesn't seem to be.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /$3
nginx.ingress.kubernetes.io/configuration-snippet: |
location /app {
#proxy_pass ; Is this needed in an Ingress?
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_redirect off;
}
labels:
app: app-ingress
spec:
rules:
- host: example.com
http:
paths:
# Any path containing "file"
- path: /(/|$)(((.*).*(file).*))
pathType: Prefix
backend:
service:
name: file-api
port:
number: 80
- path: /app
pathType: Prefix
backend:
service:
name: nextjs-service
port:
number: 80
Am I using the wrong values in the annotation or is my approach wrong entirely?
I am trying to achieve below proxy_pass settings, basically one of the services is listing to subdomain.example.com/guacamole but I want to serve it as subdomain.example.com
location / {
proxy_pass http://guacamole:8080/guacamole/;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_cookie_path /guacamole/ /;
access_log off;
# allow large uploads (default=1m)
# 4096m = 4GByte
client_max_body_size 4096m;
}
Below is nginx ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: guacamole-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- subdomain.example.com
rules:
- host: subdomain.example.com
http:
paths:
- path: /guacamole
backend:
serviceName: service-guacamole-frontend
servicePort: 8080
I tried using nginx.ingress.kubernetes.io/rewrite-target: / but it didn't work.
Replacing path: /guacamole with path: / should do the trick.
rules:
- host: subdomain.example.com
http:
paths:
- path: / # replace `/guacamole` with `/`
backend:
serviceName: service-guacamole-frontend
servicePort: 8080
You should use the app-root annotation.
From nginx-ingress docs:
If the Application Root is exposed in a different path and needs to be redirected, set the annotation nginx.ingress.kubernetes.io/app-root to redirect requests for /.
Try to use:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: guacamole-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/app-root: /guacamole
spec:
tls:
- hosts:
- subdomain.example.com
rules:
- host: subdomain.example.com
http:
paths:
- path: /
backend:
serviceName: service-guacamole-frontend
servicePort: 8080
Here you can find another example.
I have a Rancher cluster (v2.4.5) running on custom nodes with the following configuration:
External machine (example.com):
Runs Rancher server on port 8443;
Runs NGINX with (among other unrelated stuff) the following basic configuration:
user nginx;
worker_processes 4;
worker_rlimit_nofile 40000;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 8192;
}
http {
upstream rancher_servers {
least_conn;
server <MY_NODE_IP>:443 max_fails=3 fail_timeout=5s;
}
server {
listen 443 ssl http2;
server_name example.com service1.example.com service2.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://rancher_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 900s;
}
}
}
Internal machine (MY_NODE_IP):
Runs Rancher agent (etcd/control plane/worker)
Firewall rules are OK, I can deploy minor web-apps with stuff running on port 80 only and get redirected automatically to HTTPS. An example of YAML I'm using to deploy stuff is the following:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: www-deployment
labels:
app: www
spec:
replicas: 1
selector:
matchLabels:
app: www
template:
metadata:
labels:
app: www
spec:
containers:
- name: www
image: my-www-image
---
kind: Service
apiVersion: v1
metadata:
name: www-service
spec:
selector:
app: www
ports:
- port: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: www-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: service1.example.com
http:
paths:
- path: /
backend:
serviceName: www-service
servicePort: 80
The problem is when I try to deploy a service that runs on both ports 80 and 443 but, when requested on port 80, automatically redirects to port 443. When that's the case, if I specify the Ingress like below (with port 443), I get a Bad Gateway response not from the host machine NGINX. I can tell that because my host machine runs nginx/1.18.0 and the response comes from nginx/1.17.10.
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: www-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: service1.example.com
http:
paths:
- path: /
backend:
serviceName: www-service
servicePort: 443
But then, if I change the configuration above to servicePort: 80 I keep getting ERR_TOO_MANY_REDIRECTS, because it enters an infinite loop of redirecting from anything to https://anything.
Am I doing anything wrong here? How can I do a workaround to make these things work?
Found it out. Turns out that the only thing I needed to do was to tell the nginx-ingress-controller that I was expecting HTTPS connections. Final YAML for exposing the service is the following:
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: www-ingress
annotations:
ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
rules:
- host: service1.example.com
http:
paths:
- path: /
backend:
serviceName: www-service
servicePort: 443
I am running kubernetes v1.16 under docker desktop for windows. I have installed the nginx-ingress controller v1.7.9 using helm. I have update my hosts file to have the following entry
127.0.0.1 application.local
I have a backend service named hedgehog-service.
The following ingress definition correctly forwards request to the backend.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ml-zoo-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: application.local
http:
paths:
- path: /hedgehog/
backend:
serviceName: hedgehog-service
servicePort: 80
curl application.local/hedgehog works as expected and hits the backend service.
However in order to correctly use the backend service I need to rewrite the target removing the url prefix /hedgehog. Hence I have the following ingress definition
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ml-zoo-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: application.local
http:
paths:
- path: /hedgehog(/|$)(.*)
backend:
serviceName: hedgehog-service
servicePort: 80
As indicated here: https://kubernetes.github.io/ingress-nginx/examples/rewrite/#rewrite-target
Now when I curl application.local/hedgehog/test the ingress controller does not communicate with the backend but according to the logs attempts to open a file
2020/06/23 12:46:48 [error] 708#708: *792 open() "/etc/nginx/html/hedgehog/test" failed (2: No such file or directory), client: 192.168.65.3, server: application.local, request: "GET /hedgehog/test HTTP/1.1", host: "application.local"
192.168.65.3 - - [23/Jun/2020:12:46:48 +0000] "GET /hedgehog/test HTTP/1.1" 404 153 "-" "curl/7.65.3" "-"
Here is the content of etc/nginx/conf.d/default-ml-zoo-ingress
# configuration for default/ml-zoo-ingress
upstream default-ml-zoo-ingress-application.local-hedgehog-service-80 {
zone default-ml-zoo-ingress-application.local-hedgehog-service-80 256k;
random two least_conn;
server 10.1.0.48:80 max_fails=1 fail_timeout=10s max_conns=0;
}
server {
listen 80;
server_tokens on;
server_name application.local;
location /hedgehog(/|$)(.*) {
proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
client_max_body_size 1m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering on;
proxy_pass http://default-ml-zoo-ingress-application.local-hedgehog-service-80;
}
}
Does anyone know why my URLs are not getting rewritten and the requsts delivered to the backend service?
Thanks in advance!
OK having played around with this for hours, once I had written the question my next google turned up an answer.
I installed nginx using helm with the following repo stable/nginx-ingress. However according to this issue https://github.com/kubernetes/ingress-nginx/issues/5756 that is in fact a legacy repository. I uninstalled my controller and changed the repository to ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
and everything appears to be working as expected. Still not sure why the previous controller installation failed but I can get back to work :)
EDIT: For the aid of others that might end up here - in hindsight I am wondering if the reinstallation simply meant that I deleted and recreated my ingress which might have solved the original problem. In other words make sure you try recreating the ingress before reinstalling the ingress controller with helm.
Hi nginx extraordinaire's
I am using nginx as a load balancer and reverse proxy to play the role of an external-facing API gateway to interface applications and api's hosted in kubernetes. these are all exposed via ingress
The issue I am facing is that nginx is giving me 404s when I try to access a standard angular web app via the /test URL
See error:
Nginx was set up using the following config - nginx.conf
events {
worker_connections 1024;
}
http {
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
ssl on;
ssl_certificate /etc/letsencrypt/live/myhostname/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myhostname/privkey.pem;
client_max_body_size 1G;
upstream k8snodes {
server 192.168.2.10;
server 192.168.2.11;
}
server {
listen 443 ssl;
location / {
proxy_pass http://k8snodes/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
}
include /etc/nginx/conf.d/*.conf;
}
}
Sitting behind /test is a Kubernetes ingress controller that serves the angular application. I can confirm that application can be accessed fine when directly through ingress so there is something that nginx is not happy with.
Ingress config
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
creationTimestamp: "2019-09-14T07:43:49Z"
generation: 2
name: test-ingress
namespace: default
resourceVersion: "12193067"
selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/test-
ingress
uid: f8c5ea11-9caf-431e-9a18-19bd3981eece
spec:
rules:
- host: myhostname
http:
paths:
- backend:
serviceName: test-svc
servicePort: 80
path: /test
status:
loadBalancer:
ingress:
- {}
Is there something I have done wrong on the Nginx config? I have other API's that are reverse proxied fine it seems to be the web applications that try to serve static files that are giving issues.