Nginx "auth_request" like option for rate limiting - nginx

In Nginx I want to send a pre-request to another endpoint which checks for rate limiting violations. Basically exactly what auth_request does, but where auth_request only accepts authentication status codes (200, 401, 403), I want it to only allow rate limiting codes (200 or 429).
Is there a more general version of auth_request which could be used for this?
For now we're using auth_request, but the downside is it turns 429 status codes into 500s.
Thanks!

Below config works for me and returns a 429 instead of 500
events {
worker_connections 1024;
}
http {
server {
listen 80;
location /api {
auth_request /rate_limit;
error_page 500 = #rate_limit_error;
echo "You were allowed to access the API";
}
location #rate_limit_error {
return 429 "Limit has been exceeded\n";
}
location = /rate_limit {
internal;
return 400 "Access is not allowed";
}
}
}
The test shows the correct response
$ curl -v localhost/api?count=2
* Trying ::1...
* Connected to localhost (::1) port 80 (#0)
> GET /api?count=2 HTTP/1.1
> Host: localhost
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 429
< Server: openresty/1.11.2.2
< Date: Sat, 30 Sep 2017 18:50:56 GMT
< Content-Type: text/plain
< Content-Length: 24
< Connection: close
<
Limit has been exceeded
* Closing connection 0
If you don't want to return a message or something else. You can also use error_page 500 = 429;

Related

Nginx: log the actual forwarded proxy_pass request URI to upstream

I've got the following nginx conf:
http {
log_format upstream_logging '[proxied request] '
'$server_name$request_uri -> $upstream_addr';
access_log /dev/stdout upstream_logging;
server {
listen 80;
server_name localhost;
location ~ /test/(.*)/foo {
proxy_pass http://127.0.0.1:3000/$1;
}
}
}
When I hit:
http://localhost/test/bar/foo
My actual output is:
[proxied request] localhost/test/bar/foo -> 127.0.0.1:3000
While my expected output is:
[proxied request] localhost/test/bar/foo -> 127.0.0.1:3000/bar
Is there a variable or a way to produce the actual proxied URI in the log?
If not production, you can test what is being sent by nginx after launching the simplest listening server on the desired local address and port (instead of a real one):
$ nc -l 127.0.0.1 3000
POST /some/uri HTTP/1.0
Host: 127.0.0.1
Connection: close
Content-Length: 14
some payload
Response can be simulated by manually entering HTTP/1.1 200 OK, followed with 2 new lines, while nc is running.

root context with preference over a regular expression

I have read the documentation and seems it is the same as explained here enter link description here, but during my tests the root context is having precedence over the regular expression one.
Does anyone know the reason?
See my nginx.conf file and a test curl I did to validate the configuration:
http {
server {
listen 80;
root /usr/share/nginx/html;
include /etc/nginx/mime.types;
location ~ /ui(/.*) {
try_files $1 $1/ #htmlext;
}
location #htmlext {
rewrite ^/ui(/\w*)(/.*)*$ $1.html last;
}
location / {
return 301 /ui$request_uri;
}
}
}
/ # curl -v http://localhost/ui/message?msg=error.forbidden
* Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /ui/message?msg=error.forbidden HTTP/1.1
> Host: localhost
> User-Agent: curl/7.67.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.19.0
< Date: Thu, 09 Jul 2020 22:34:49 GMT
< Content-Type: text/html
< Content-Length: 169
< Location: http://localhost/ui/ui/message?msg=error.forbidden
< Connection: keep-alive
<
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.19.0</center>
</body>
</html>
* Connection #0 to host localhost left intact
As you can see in the curl it is redirecting my /ui/message page instead of rendering it. If I remove the location / from the configuration it seems working, if I remove the #htmlext location and add that logic in the location ~ /ui(/.*) it seems working too. Then I think the reason of my error is the #htmlext redirect.
Does anyone know why?
Thanks

Change the Server Signature in NginX

I wan to Hide the Server Signature in HTTP 400 Error HTML Error page footer in Nginx. After implementing Headers-more Module. The Server Signature is changed when an HTTP Package is requested:
>> curl -I localhost
Output
HTTP/1.1 301 Moved Permanently
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://www.abshnb.com/
Server: Abshnb
But the HTTP 400 Error HTML Page is still returning the error page with "nginx" footer.
Here is a dead simple example of error_page directive where error response is generated by nginx itself:
server {
listen 8888;
server_tokens off;
...
error_page 400 502 #error;
location #error {
default_type text/html;
return 200 '<center><h1>$status</h1></center>';
}
location = /error400 {
return 400;
}
location = /error502 {
return 502;
}
Custom error handler:
$ http :8888/error400
HTTP/1.1 400 Bad Request
Connection: close
Content-Length: 29
Content-Type: text/html
Date: Sat, 08 Feb 2020 11:43:05 GMT
Server: nginx
<center><h1>400</h1></center>
Default error handler:
$ http :8888/nonexistent
HTTP/1.1 404 Not Found
Connection: keep-alive
Content-Length: 162
Content-Type: text/html
Date: Sat, 08 Feb 2020 11:47:19 GMT
Server: nginx
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

Nginx, Suppressing/Masking default Error

I have written lua code and integrated in nginx to upload files into s3 bucket. If the upload fails or signature is malformed, i get an 403 Forbidden which is send to the client along with very verbose Error XML.
I wanted to intercept/ return default error msg instead of the verbose error msg from s3 which reveals Access Key ID.
I tried adding snippet below but it has no effect what so ever:
error_page 403 /forbidden;
location = /forbidden {
internal;
content_by_lua_block {
local cjson = require "cjson"
ngx.header.content_type = "application/json; charset=utf-8"
ngx.status = 403
ngx.say(cjson.encode({ status = "Forbidden. Check the Request" }))
return ngx.exit(ngx.status)
}
}
When i see the reponse of curl cmd to upload, There are two status codes, 100 and then 403.
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 403 Forbidden
< Date: Thu, 28 Sep 2017 04:26:31 GMT
< Content-Type: application/xml
< Transfer-Encoding: chunked
< Connection: keep-alive >
How do i handle this to return default response for all 403 status code response.

nginx: variable expansion inside split_clients not working?

i have the following config:
map $host $variant_a {
default 'a';
}
map $host $variant_b {
default 'b';
}
map $host $variant_c {
default 'c';
}
map $host $variant_d {
default 'd';
}
map $host $variant_default {
default 'default';
}
# a/b testing
split_clients "abtest${remote_addr}${http_user_agent}${date_gmt}" $variant_chosen {
20% $variant_a;
20% $variant_b;
20% $variant_c;
20% $variant_d;
20% $variant_default;
}
server {
# defaults test independent
listen 80;
server_name _;
root /home/vagrant/www;
index index.html;
error_page 404 = 404.html;
error_page 403 = 404.html;
location / {
echo a=$variant_a,variant_chosen=$variant_chosen
}
}
when i execute a query against the server:
curl -i http://192.168.33.10/
i can see that the variables inside the 'split_clients' get never expanded.
sample output:
vagrant#precise32:~/www$ curl -i http://192.168.33.10/
HTTP/1.1 200 OK
Server: nginx/1.1.19
Date: Mon, 04 May 2015 15:46:17 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
a=a,variant_chosen=$variant_default
anybody any idea why the variables are not expanded?
any help highly appreciated
cheers
marcel

Resources