We are trying to edit our ingress-nginx.yml to make ingress-controllers pods debug traffic coming from a specific source IP.
Our setup is:
Kubernetes v1.13
Ingress-Controller v0.24.1
From NGINX and Kubernetes DOCs it appears there is no very easy way to debug traffic from a single ip (you cannot edit the nginx config directly). So, we would like to add the debug_connection directive to appear like this:
error_log /path/to/log;
...
events {
debug_connection 192.168.1.1;
}
The correct way to do it shall be through CustomAnnotations in a ConfigMap + a new ingress to enable the CustomAnnotation, so we tried this:
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app: ingress-nginx
data:
ingress-template: |
#Creating the custom annotation to make debug_connection on/off
{if index $.Ingress.Annotations "custom.nginx.org/debug_connection"}
{$ip := index $.Ingress.Annotations "custom.nginx.org/ip"}
{end}
{range $events := .Events}
events {
# handling custom.nginx.org/debug_connection
{if index $.Ingress.Annotations "custom.nginx.org/debug_connection"}
{end}
And:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: debugenabler
annotations:
kubernetes.io/ingress.class: "nginx"
custom.nginx.org/debug_connection: "on"
custom.nginx.org/ip: "192.168.1.1"
spec:
rules:
- host: "ourhostname"
http:
paths:
- path: /tea
backend:
serviceName: tea-svc
servicePort: 80
- path: /coffee
backend:
serviceName: coffee-svc
servicePort: 80
We applied ingress-nginx.yml with no errors. We see new lines in the nginx conf:
location /coffee {
set $namespace "test";
set $ingress_name "debugenabler";
set $service_name "coffee-svc";
set $service_port "80";
set $location_path "/coffee";
rewrite_by_lua_block {
lua_ingress.rewrite({
force_ssl_redirect = true,
use_port_in_redirects = false,
})
balancer.rewrite()
But still nothing as regard the debug_connection in the events block:
events {
multi_accept on;
worker_connections 16384;
use epoll;
}
How to insert debug_connection in the events context ?
For those who may face similar challenges, I actually managed to do it by:
Creating a ConfigMap with a new ingress-controller template file (nginx.tmpl) containing the debug_connection line (double check your ingress-controller version here, the file changes dramatically)
Creating a Volume which links at the Configmap (specifying Volume and Volumemount)
Creating a InitContainer which copy the content of the volume inside the /etc/nginx/template (this was needed to overcome probably permission-related issues) before the container start.
For point 2 and 3 you can add the relevant code at the end of a deployment or a pod code, I share an example:
volumes:
- name: nginxconf2
configMap:
name: nginxconf2
items:
- key: nginx.tmpl
path: nginx.tmpl
initContainers:
- name: copy-configs
image: {{ kubernetes.ingress_nginx.image }}
volumeMounts:
- mountPath: /nginx
name: nginxconf2
command: ['sh', '-c', 'cp -R /nginx/ /etc/nginx/template/']
Related
We would like to use annotation for redirecting requests to a different backend service based on url args (query)
Example:
https://example.com/foo?differentQueryString=0 -> service-a
https://example.com/foo/bar?queryString=0 - service-b
Notes: path does not matter, this can be either /foo/bar or /foo or /bar/foo
We followed up on this
Kubernetes NGINX Ingress controller - different route if query string exists
and this
Kubernetes ingress routes with url parameter
But we don't want to setup ConfigMap just for this and also we don't want to duplicate requests to the ingress but rewriting
This is what we tried
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($args ~ queryString=0){
backend.service.name = service-b
}
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-a
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: service-b
port:
number: 80
We were expecting to get the response but we got 502 from the Ingress Nginx
We managed to find a nice solution without rewriting and ConfigMap
Works great and also include Nginx Ingress metrics so we can do HPA accordingly
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($args ~ queryString=0){
set $proxy_upstream_name "default-service-b-80";
set $proxy_host $proxy_upstream_name;
set $service_name "service-b";
}
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-a
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: service-b
port:
number: 80
$proxy_upsteam_name convention is NAMESPACE-SERVICE_NAME-PORT
I have a configuration file like below. I deploy it to EKS cluster successfully. However, when I change the nginx-conf ConfigMap and run kubectl apply command, it doesn't seem to update the nginx.config in the pod. I tried to login to the pod and look at the file /etc/nginx/nginx.config but its content is still the old one.
I have tried to run kubectl rollout status deployment sidecar-app but it doesn't help.
And it shows the updated config map when I run kubectl describe configmap nginx-conf.
It seems the container doesn't take the config map change. How can I apply the changes without deleting the pod?
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-conf
data:
nginx.conf: |
user nginx;
worker_processes 1;
events {
worker_connections 10240;
}
http {
server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://localhost:9200/;
}
location /health {
proxy_pass http://localhost:9200/_cluster/health;
}
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sidecar-app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
name: sidecar-app
template:
metadata:
labels:
name: sidecar-app
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- name: http
containerPort: 8080
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
readOnly: true
volumes:
- name: nginx-conf
configMap:
name: nginx-conf
items:
- key: nginx.conf
path: nginx.conf
---
apiVersion: v1
kind: Service
metadata:
name: sidecar-entrypoint
spec:
selector:
name: sidecar-app
ports:
- port: 8080
targetPort: 8080
protocol: TCP
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: sidecar-ingress-1
namespace: default
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/group.name: sidecar-ingress
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/group.order: '2'
alb.ingress.kubernetes.io/healthcheck-path: /health
# alb.ingress.kubernetes.io/healthcheck-port: '8080'
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: sidecar-entrypoint
servicePort: 8080
Kubernetes treats POD and Configmap as a different/separate object and pods don't automatically restart on specific Configmap version.
There are few alternatives to achieve this.
1 ) Reloader: https://github.com/stakater/Reloader
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
spec:
template: metadata:
configHash annotation.
https://blog.questionable.services/article/kubernetes-deployments-configmap-change/
Use wave.
https://github.com/wave-k8s/wave
You can use kubectl rollout restart deploy/sidecar-app but this will restart the pods with zero downtime. Rolling updates allow Deployments' update to take place with zero downtime by incrementally updating Pods instances with new ones.
I have this base url api.example.com
So, ingress-nginx will get the request for api.example.com and it should do follow things.
Forward api.example.com/customer to customer-srv
It doesn't work, it forwards whole mtach to customer-srv i.e. /customer/requested_url
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: api.example.in
http:
paths:
- path: /customer/?(.*)
pathType: Prefix
backend:
service:
name: customer-srv
port:
number: 3000
I tried using rewrite annotation but that doesn't work either however this worked but this is not I want to achieve.
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: customer-srv
port:
number: 3000
For example,
api.example.com/customer should go to http://localhost:3000 not http://localhost:3000/customer
Here is the yaml I used:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: api.example.in
http:
paths:
- path: /customer/?(.*)
pathType: Prefix
backend:
service:
name: customer-srv
port:
number: 3000
For test purpouses I created an echo server:
kubectl run --image mendhak/http-https-echo customer
And then a service:
kubectl expose po customer --name customer-srv --port 3000 --target-port 80
I checked igress ip:
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-service <none> api.example.in 192.168.39.254 80 3m43s
And I did a curl to check it:
curl 192.168.39.254/customer/asd -H "Host: api.example.in"
{
"path": "/asd",
"headers": {
"host": "api.example.in",
...
}
Notice that the echo server echoed back a path that it received, and sice it received a path that got rewritten from /customer/asd to /asd it shows this exectly path (/asd).
So as you see this does work.
I have many domain pointing to Ingress Controller IP. I want to block /particular-path for all the domains/sites. Is there a way to do this.
I can use nginx.ingress.kubernetes.io/configuration-snippet: | for each site. But looking for way to do for all sites/domains/Ingress resource at once.
Controller used: https://kubernetes.github.io/ingress-nginx/
There are two ways to achieve this:
1. First one is with using server-snippet annotation:
Using the annotation nginx.ingress.kubernetes.io/server-snippet it
is possible to add custom configuration in the server configuration
block.
Here is my manifest for the ingress object:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
location ~* /admin-access {
deny all;
return 403;
}
spec:
rules:
- host: domain.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
Please note that using this approach :
This annotation can be used only once per host.
2. Second one is with usage of ConfigMaps and Server-snippet:
What you have to do is to locate your configMap:
kubectl get pod <nginx-ingress-controller> -o yaml
This is located the container args:
spec:
containers:
- args:
- /nginx-ingress-controller
- configmap=$(POD_NAMESPACE)/nginx-loadbalancer-conf
And then just edit it and place add the server-snippet part:
apiVersion: v1
data:
server-snippet: |
location /admin-access {
deny all;
}
This approach allows you to define restricted location globally for all host defined in Ingress resource.
Please note that with usage of server-snippet the path that you are blocking cannot be defined in ingress resource object. There is however another way with location-snippet via ConfigMap:
location ~* "^/web/admin {
deny all;
}
With this for every existing path in ingress object there will be ingress rule but it will be blocked for specific uri (In the example above it be be blocked when admin will appear after web). All of the other uri will be passed through.
3. Here`s a test:
➜ curl -H "Host: domain.com" 172.17.0.4/test
...
"path": "/test",
"headers": {
...
},
"method": "GET",
"body": "",
"fresh": false,
"hostname": "domain.com",
"ip": "172.17.0.1",
"ips": [
"172.17.0.1"
],
"protocol": "http",
"query": {},
"subdomains": [],
"xhr": false,
"os": {
"hostname": "web-6b686fdc7d-4pxt9"
...
And here is a test with a path that has been denied:
➜ curl -H "Host: domain.com" 172.17.0.4/admin-access
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.0</center>
</body>
</html>
➜ curl -H "Host: domain.com" 172.17.0.4/admin-access/test
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.0</center>
</body>
</html>
Additional information: Deprecated APIs Removed In 1.16. Here’s What You Need To Know:
The v1.22 release will stop serving the following deprecated API
versions in favor of newer and more stable API versions:
Ingress in the extensions/v1beta1 API version will no longer be
served
You cannot block specific paths. What you can do is point the path of the host inside your ingress to a default backedn application that says 404 default backedn for example.
you can apply it using the ingress annotation
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: channel-dev
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/whitelist-source-range: "27.110.30.45, 68.50.85.421"
name: dev-ingress
namespace: development
spec:
rules:
- host: hooks.dev.example.com
http:
paths:
- backend:
serviceName: hello-service
servicePort: 80
path: /incoming/message/
tls:
- hosts:
- hooks.dev.example.com
secretName: channel-dev
path https://hooks.dev.example.com/incoming/message/ will be only accessible from mentioned IPs other users will get 403 error and wont be able to access the URL.
just add this annotation in ingress
nginx.ingress.kubernetes.io/whitelist-source-range
I have a pod that responds to requests to /api/
I want to do a rewrite where requests to /auth/api/ go to /api/.
Using an Ingress (nginx), I thought that with the ingress.kubernetes.io/rewrite-target: annotation I could do it something like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api
backend:
serviceName: myapi
servicePort: myapi-port
What's happening however is that /auth/ is being passed to the service/pod and a 404 is rightfully being thrown. I must be misunderstanding the rewrite annotation.
Is there a way to do this via k8s & ingresses?
I don't know if this is still an issue, but since version 0.22 it seems you need to use capture groups to pass values to the rewrite-target value
From the nginx example available here
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.
For your specific needs, something like this should do the trick
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api/$2
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api(/|$)(.*)
backend:
serviceName: myapi
servicePort: myapi-port
I have created the following example that works and which I will explain. To run this minimal example, run these commands:
$ minikube start
$ minikube addons enable ingress # might take a while for ingress pod to bootstrap
$ kubectl apply -f kubernetes.yaml
$ curl https://$(minikube ip)/auth/api/ --insecure
success - path: /api/
$ curl https://$(minikube ip)/auth/api --insecure
failure - path: /auth/api
$ curl https://$(minikube ip)/auth/api/blah/whatever --insecure
success - path: /api/blah/whatever
As you'll notice, the ingress rewrite annotation appears to be very particular about trailing slashes. If a trailing slash is not present, the request will not be rewritten. However, if a trailing slash is provided, the request uri will be rewritten and your proxy will function as expected.
After inspecting the generated nginx.conf file from inside the ingress controller, the line of code responsible for this behavior is:
rewrite /auth/api/(.*) api/$1 break;
This line tells us that only requests matching the first argument will be rewritten with the path specified by the second argument.
I believe this is bug worthy.
kubernetes.yaml
---
apiVersion: v1
kind: Service
metadata:
name: ingress-rewite-example
spec:
selector:
app: ingress-rewite-example
ports:
- name: nginx
port: 80
protocol: TCP
targetPort: 80
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ingress-rewite-example
spec:
template:
metadata:
labels:
app: ingress-rewite-example
spec:
containers:
- name: ingress-rewite-example
image: fbgrecojr/office-hours:so-47837087
imagePullPolicy: Always
ports:
- containerPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-rewite-example
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /auth/api
backend:
serviceName: ingress-rewite-example
servicePort: 80
main.go
package main
import (
"fmt"
"strings"
"net/http"
)
func httpHandler(w http.ResponseWriter, r *http.Request) {
var response string
if strings.HasPrefix(r.URL.Path, "/api") {
response = "success"
} else {
response = "failure"
}
fmt.Fprintf(w, response + " - path: " + r.URL.Path + "\n")
}
func main() {
http.HandleFunc("/", httpHandler)
panic(http.ListenAndServe(":80", nil))
}