Ingress rewrite string is being ignored - kubernetes-ingress

The requirement is to access the burger service in https://meals.food.com/burger2.
The context path within the app is /burger.
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /burger/$2
spec:
rules:
- host: meals.food.com
http:
paths:
- backend:
service:
name: burger
port:
number: 80
path: /burger2(/|$)(.*)
pathType: Prefix
Upon checking the ingress controller logs:
[05/Jan/2022:13:54:11 +0000] "GET // HTTP/1.1" 304 0 "-" "Mozilla/5.0
(X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/96.0.4664.110 Safari/537.36" 957 0.002 [anotherservice-80] []
x.x.x.x:80 0 0.002 304 230200x023
Is my ingress config correct?
My suspicion is that something is altering the request between my request from the browser to ingress-controller.

Is my ingress config correct? My suspicion is that something is altering the request between my request from the browser to ingress-controller.
Your ingress config looks OK. I do not see any errors in it. He will act as follows:
The example address meals.food.com/burger2/blah-blah-blah will be rewirted to meals.food.com/burger/blah-blah-blah. If that was your intention then config is fine.
However you have got 304 HTTP code.
The HTTP 304 Not Modified client redirection response code indicates that there is no need to retransmit the requested resources. It is an implicit redirection to a cached resource. This happens when the request method is safe, like a GET or a HEAD request, or when the request is conditional and uses a If-None-Match or a If-Modified-Since header.
The equivalent 200 OK response would have included the headers Cache-Control, Content-Location, Date, ETag, Expires, and Vary.
In other words
When the browser receives a request, but does not know whether it has the latest version of a write, it sends a conditional validation request, communicating the last modified date and time to the server via the If-Modified-Since or If-None-Match header.
The server then checks these headers and determines if their values are the same. If so - the server will send back the HTTP 304 code and the browser will use the cached copy of the resource. If not, it means that the file has been modified, so the browser will save a new copy by sending HTTP 200 code.
In your case it looks as if someone tried to download the same (unchanged) resource multiple times and therefore got the code 304. If so, everything is fine.

Related

nginx returning index.html for js with 304 response

I have an nginx server that is showing a 200 response in my browser for requests like
https://server.com/app/static/js/2.8cc049f3.chunk.js
and showing a 304 on the server logs
"GET /static/js/2.8cc049f3.chunk.js HTTP/1.1" 304 0 "https://server.com/app"
There is another nginx in front of the nginx running on server.com that is removing the app from the path. Based on the nginx logs on server.com this is working correctly. The static folder is in my root /usr/share/nginx/html/.
The content my browser receives for the js file is the index.html. However when I login to the server and run
curl http://localhost/static/js/2.8cc049f3.chunk.js
I get the correct js content in response and in the logs the server prints 200
"GET /static/js/2.8cc049f3.chunk.js HTTP/1.1" 200 1570391 "-" "curl/7.80.0" "-"
Here is my nginx.conf
server {
listen 80;
root /usr/share/nginx/html/;
index index.html;
add_header X-Frame-Options "SAMEORIGIN";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
try_files $uri $uri/ /index.html;
}
location ~ ^/(static)/ {
gzip_static on;
gzip_types
text/plain
text/xml
text/css
text/comma-separated-values
text/javascript application/x-javascript
application/atom+xml;
expires max;
}
}
I've read that a 304 means the file hasn't changed and tells your browser to use its local cache, so I cleared my browser cache. I also restarted nginx, thinking that the first request would give a 200 response on the server but it was still a 304.
Based on the local curl request being successful I don't think there is anything wrong with my nginx.conf. I don't know if nginx somehow has the index.html cached as the content of my js, or if I didn't clear my browser cache correctly.
I'm also confused why the response code is 304 on the server but 200 in my browser.
The HTTP 304 says "Not-Modified". This is because you are using another NGINX Proxy server in front of the NGINX Server we are talking about.
The 1st NGINX is requesting a resource on the 2nd NGINX and this one answers "Hey that file was not modified since the last time you have asked".
In this case it would be very helpful to check the configuration of the 1st NGINX Proxy instance or your turn of the caching in the first one and proxy_cache off; and check the result.

Nginx Ingress Controller trailling slash with HTTPS redirect

Nginx Ingress Controller trailling slash with HTTPS redirect
I'm trying to redirect requests from HTTP to HTTPS using an Ingress with Nginx Ingress Controller. My app is written in Django v3.0.7, my Nginx Controller is v0.46.0 and k8s v1.19.8.
I have the following ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: INGRESS-NAME
namespace: INGRESS-NS
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/
cert-manager.io/cluster-issuer: "ISSUER-NAME"
nginx.ingress.kubernetes.io/permanent-redirect-code: '308'
spec:
tls:
...
rules:
- host: MY-DOMAIN
http:
paths:
- path: /api/v1/?(.*)
pathType: Prefix
backend:
service:
name: SVC-NAME
port:
number: SVC-PORT
Requests at https://.../api/v1/get-token/, raise this error:
[05/May/2021:20:39:49 +0000] "POST /api/v1/get-token// HTTP/1.1" 404 => POST get an extra / at the end. But the same request with HTTP or https://.../api/v1/get-token (no trailing /) is fine.
If I remove the
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/
The redirect removes trailing / and it causes POST to turn into GET in all HTTP POST request causing an 403 - Method not allowed as shown in Nginx Logs:
[05/May/2021:20:54:52 +0000] "POST /api/v1/get-token HTTP/1.1" 308 164
[05/May/2021:20:54:53 +0000] "POST /api/v1/get-token HTTP/1.1" 301 0
[05/May/2021:20:54:53 +0000] "GET /api/v1/get-token/ HTTP/1.1" 405
but HTTP POST request works fine with http://.../api/v1/get-token// (two trailing /).
Is there a way to solve this problem? The 308 HTTP -> HTTPS redirect is important, so I can't remove it, but is there a way to force requests to have one, and only one, trailing /? Thanks.
There are two problems here
Problem #1
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/
causes requests sent to https://.../api/v1/get-token/ to end with HTTP 404 Not Found, but https://.../api/v1/get-token woks fine.
Why?
Because trailing / at the end of nginx.ingress.kubernetes.io/rewrite-target: /api/v1/$1/ rewrite is added to the URL, and /api/v1/get-token// leads to a resource that does not exist.
What to do about it?
Change path key to /api/v1/?(.*\b)/. I'm not 100% sure it will work, but it's worth a try.
or
Remove trailing / from rewrite.
Now, doing that causes problem #2.
Problem #2
Requests to https://.../api/v1/get-token ends with 405 Method Not Allowed.
Why?
First redirection works fine (HTTP 308), however request is redirected again with HTTP 301.
MDN article on HTTP 301 states:
Even if the specification requires the method (and the body) not to be altered when the redirection is performed, not all user-agents align with it - you can still find this type of bugged software out there. It is therefore recommended to use the 301 code only as a response for GET or HEAD methods and to use the 308 Permanent Redirect for POST methods instead, as the method change is explicitly prohibited with this status.
Basically HTTP 301 causes POST to become GET, and GET is not allowed, hence HTTP 405.
What to do about it?
Make sure not to redirect requests twice, especially with HTTP 301.

Error 404 in Nginx reverse proxy with iframe

I've been trying to run a reverse proxy to manage a graph of Grafana in an iframe. I'm using Apache on port 80 with the html page, Grafana server on port 3000 and Nginx with the reverse proxy on 8081.
The problem is that the proxy gives me a 404 error when I try to access the contents of the iframe. To test everything, I tried a local Grafana server configuration and a remote server configuration with 2 different iframes. The one connected to the local Grafana gives me a generic error of Grafana and a 404 when uploading the file, the remote one only 404.
Obviously each link taken individually works if I insert it on the browser, and without using the proxy it works the same way on the iframe.
I am not an Nginx expert and there will probably be some errors in the configuration. As a configuration file I'm using the Nginx default.conf file, so I haven't touched the nginx.conf because I don't think I need it for this purpose.
index.html
<h4>grafana local</h4>
<iframe src="http://localhost:8081/grafana/d-solo/KXusIR0Mk/test?orgId=1&from=1606211757397&to=1606233357397&panelId=2" width="450" height="200" frameborder="0"></iframe>
<h4>grafana remote</h4>
<iframe src="http://localhost:8081/salvo/d-solo/s-kg75yZz/stazione-meteo?orgId=1&panelId=8&from=1606215708391&to=1606237308391" width="450" height="200" frameborder="0"></iframe>
default.conf
server {
listen 8081;
listen [::]:8081;
server_name 127.0.0.1;
location /grafana/ {
#rewrite (grafana\/).* d-solo/KXusIR0Mk/test?orgId=1&from=1606211757397&to=1606233357397&panelId=2 break;
proxy_pass http://localhost:3000/;
}
location /salvo/ {
#rewrite (salvo\/).* d-solo/s-kg75yZz/stazione-meteo?orgId=1&panelId=8&from=1606215708391&to=1606237308391 break;
proxy_pass http://<ip_remoteGrafana>:<port>/;
}
}
PS: the ip and the port of the remote Grafana are not mine so I removed them, but in the code they are there ;)
access.log
::1 - - [26/Nov/2020:14:17:41 +0100] "GET /salvo/d-solo/s-kg75yZz/stazione-meteo?orgId=1&panelId=8&from=1606215708391&to=1606237308391 HTTP/1.1" 302 29 "http://localhost/grafana/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36"
::1 - - [26/Nov/2020:14:17:41 +0100] "GET /login HTTP/1.1" 404 197 "http://localhost/grafana/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36"
error.log
2020/11/26 14:17:41 [error] 101110#101110: *1 open() "/usr/share/nginx/html/login" failed (2: No such file or directory), client: ::1, server: 127.0.0.1, request: "GET /login HTTP/1.1", host: "localhost:8081", referrer: "http://localhost/grafana/"
What is this due to?
Apparently proxied Grafana needs authentication (redirect to /login), which is not compatible with your context path (it must be /salvo/login in your case). You will need redirect/content rewrite, which fill update all path to your used context /salvo/. That's complicated and not very reliable.
But you may somehow do that, but then you may discover that target Grafana doesn't allow to be embedded in the iframe or that Grafana cookies config (e.g. samesite, secure) may be also problem for your http app or Grafana auth is a problem. I would work with simple / context first to discover issues early and later also with more complicated context paths e.g. /salvo/.

Chrome ignores 302 HTTPS to HTTP redirect of resource and causes CERT error

I have a page served via HTTPS. All content is public and no sensitive data is on this page. I also have resources (audio mp3) on a google cloud bucket which has no HTTPS support and is a subdomain (DNS C-NAME entry only), but is reachable via HTTPS but returns a Google certificate if accessed by https. Until now I had my server respond with a 302 redirect to my subdomain downgrading static mp3-resources from HTTPS to HTTP and it worked. See this request:
Request:
Request URL: https://x.com/q/ics/edit/r.6c19e2d99e220f648b3c1799ed05dc99.mp3
Request Method: GET
Status Code: 302
Remote Address: 172.217.19.115:443
Referrer Policy: no-referrer-when-downgrade
Response:
content-length: 259
content-type: text/html; charset=UTF-8
date: Wed, 24 Jun 2020 05:42:46 GMT
location: http://subdomain.x.com/ics/_resources/r.6c19e2d99e220f648b3c1799ed05dc99.mp3
server: Google Frontend
status: 302
x-cloud-trace-context: 3086726898e017b2b99858fcea43c5e0
But suddenly Chrome started ignoring location: http: and instead resolves the HTTPS version, which causes ERR_CERT_COMMON_NAME_INVALID and this fails the download.
I know this can be resolved by using a load-balancer, but that would almost double our hosting costs (maintenance/setup/learning costs not included!) and we really do not need a load balancer with AppEngine classic.
The problem occurred a few weeks ago the first time, but went away within a day, but is persistent now!
Does anyone know why Chrome is interpreting the redirect this way? Is this required by any specification?
Update: The only solution I found so far is to downgrade the whole page with a redirect from HTTPS to HTTP. That is really a sad solution.

HAProxy 1.5 - Serving static json file on 504 error

I'm trying to set up HAProxy to server a static JSON file on 504 errors. To test, we've set up the configuration file to timeout after 10 seconds, and to use the errorfile option:
defaults
log global
mode http
retries 3
timeout client 10s
timeout connect 10s
timeout server 10s
option tcplog
balance roundrobin
frontend https
maxconn 2000
bind 0.0.0.0:9000
errorfile 504 /home/user1/test/error.json
acl employee-api-service path_reg /employee/api.*
use_backend servers-employee-api if employee-api-service
backend servers-employee-api
server www.server.com 127.0.0.1:8000
Effectively, I'm trying to serve JSON instead of HTML on a timeout, so the backend service can fail gracefully. However, on testing, we could not get anything, neither HTML or JSON. On looking at the response, it simply says it failed, with no status code. Is my setup correct for errorfile? Does HAProxy 1.5 support this?
According to the documentation of errorfile:
<file> designates a file containing the full HTTP response. It is
recommended to follow the common practice of appending ".http" to
the filename so that people do not confuse the response with HTML
error pages, and to use absolute paths, since files are read
before any chroot is performed.
So, the file should contain a complete HTTP response but you're trying to serve JSON only.
The documentation further says that:
For better HTTP compliance, it is
recommended that all header lines end with CR-LF and not LF alone.
The example configuration, for example,
errorfile 503 /etc/haproxy/errorfiles/503sorry.http
shows the common practice of .http extension for the error file.
You can find samples of some default error files here.
Sample (504.http):
HTTP/1.0 504 Gateway Time-out
Cache-Control: no-cache
Connection: close
Content-Type: text/html
<html><body><h1>504 Gateway Time-out</h1>
The server didn't respond in time.
</body></html>
So, in your scenario, 504.http would be like this:
HTTP/1.0 504 Gateway Time-out
Cache-Control: no-cache
Connection: close
Content-Type: application/json
{
"message": "Gateway Timeout"
}
Also, you need to keep the file size under limit i.e. BUFSIZE (8 or 16 KB) as described in the documentation.
There might be some error logs for not serving your JSON file. You might want to look at HAProxy's logs again thoroughly. Just to be sure.