Different files by useragent in NGINX - dictionary

How can I correctly write the configuration of the nginx server?
...
if the client has useragent (A) and it refers to http://somehost.domain/somefile.someextension
nginx responding a file from the root /file.zip
if the client has useragent (B) and it refers to http://somehost.domain/somefile.someextension
nginx responding a file from the root /file2.zip
if the client has useragent (C) and it refers to http://somehost.domain/somefile.someextension
nginx responding 403 error
...
I did this code:
map $http_user_agent $browser {
"~*Firefox" "/var/www/test1";
"~*Wget" "/var/www/test2";
"~*SomeUserAgent" "/var/www/test3";
}
server {
...
root $browser
But how do I get the condition to pass to any address http://somehost.domain/somefile.someextension?

You can use map, location and alias directives to map a specific URI to multiple files based on the value of a header.
For example (where all of the files are in the same directory):
map $http_user_agent $browser {
default "nonexistent";
"~*Firefox" "file.zip";
"~*Wget" "file1.zip";
}
server {
...
location = /somefile.someextension {
alias /path/to/directory/$browser;
if (!-f $request_filename) {
return 403;
}
}
}
The if block is only required to change the 404 response to a 403 response.
See this document for more.

Related

How to drop path in rewrite and proxy pass the args in nginx

Example request - http://localhost/iframe?ip=192.168.0.237
I want to proxy pass the request to the value of IP and remove the path and args after localhost/ .
Ideally the proxy_pass should point to 192.168.0.237 and the URL should be http://localhost/.
localhost /iframe {
rewrite ^/(iframe/.*)$ http://localhost/ permanent;
proxy_pass $arg_ip;
}
I'm not sure whether rewrite is the proper way to address this problem.
I would use the argument ip and a rewrite to remove the iframe location
server {
listen 8085;
location /iframe {
rewrite ^/iframe(.*)$ /$1 break;
proxy_pass http://$arg_ip;
}
}
server {
listen 8080;
location / { return 200 "$host$uri"; }
}
Security Notice
I just have a feeling you should whilelist the upstream servers accepted as arguments. If not this will be a wildcard proxy to every single http-server reachable in the network. This is a easy to use SSRF attack vector. So please add some extra layer of security.
SSRF Explained:
Let's say we use this configuration without any further security. Given the folowing NGINX config:
server {
listen 8085;
location /iframe {
rewrite ^/iframe(.*)$ /$1 break;
proxy_pass http://$arg_ip;
}
}
# Server for iframe service
server {
listen 8080;
root /usr/share/nginx/;
location / { return 200 "$host$uri\n"; }
}
# Private Server Section here!
server {
listen 8086;
allow 127.0.0.1;
deny all;
.....
location / {
index welcome.html;
}
}
Trying to reach the secret server directly
curl -v EXTERNALIP:8086
will fail with HTTP 403.
The NGINX will just allow connections form localhost/127.0.0.1 as defined in the allow/deny directives.
But lets try the iframe with the ip argument.
$# curl localhost:8085/iframe?ip=127.0.0.1:8086
Welcome to our very secure server! Internals only!
It prints the content of the secret server. Whitlisting a proxy-pass like this is never a good idea regardless its working or not.

NGINX read body from proxy_pass response

I have two servers:
NGINX (it exchanges file id to file path)
Golang (it accepts file id and return it's path)
Ex: When browser client makes request to https://example.com/file?id=123, NGINX should proxy this request to Golang server https://go.example.com/getpath?file_id=123, which will return the response to NGINX:
{
data: {
filePath: "/static/..."
},
status: "ok"
}
Then NGINX should get value from filePath and return file from the location.
So the question is how to read response (get filePath) in NGINX?
I assume you are software developer and your have full control over your application so there is no need to force square peg in a round hole here.
Different kinds of reverse proxies support ESI(Edge Side Includes) technology which allow developer to replace different parts of responce body with content of static files or with response bodies from upstream servers.
Nginx has such technology as well. It is called SSI (Server Side Includes).
location /file {
ssi on;
proxy_pass http://go.example.com;
}
Your upstream server can produce body with content <!--# include file="/path-to-static-files/some-static-file.ext" --> and nginx will replace this in-body directive with content of the file.
But you mentioned streaming...
It means that files will be of arbitrary sizes and building response with SSI would certainly eat precious RAM resources so we need a Plan #B.
There is "good enough" method to feed big files to the clients without showing static location of the file to the client.
You can use nginx's error handler to server static files based on information supplied by upstream server.
Upstream server for example can send back redirect 302 with Location header field containing real file path to the file.
This response does not reach the client and is feed into error handler.
Here is an example of config:
location /file {
error_page 302 = #service_static_file;
proxy_intercept_errors on;
proxy_set_header Host $host;
proxy_pass http://go.example.com;
}
location #service_static_file {
root /hidden-files;
try_files $upstream_http_location 404.html;
}
With this method you will be able to serve files without over-loading your system while having control over whom do you give the file.
For this to work your upstream server should respond with status 302 and with typical "Location:" field and nginx will use location content to find the file in the "new" root for static files.
The reason for this method to be of "good enough" type (instead of perfect) because it does not support partial requests (i.e. Range: bytes ...)
Looks like you are wanting to make an api call for data to run decision and logic against. That's not quite what proxying is about.
The core proxy ability of nginx is not designed for what you are looking to do.
Possible workaround: extending nginx...
Nginx + PHP
Your php code would do the leg work.
Serve as a client to connect to the Golang server and apply additional logic to the response.
<?php
$response = file_get_contents('https://go.example.com/getpath?file_id='.$_GET["id"]);
preg_match_all("/filePath: \"(.*?)\"/", $response, $filePath);
readfile($filePath[1][0]);
?>
location /getpath {
try_files /getpath.php;
}
This is just the pseudo-code example to get it rolling.
Some miscellaneous observations / comments:
The Golang response doesn't look like valid json, replace preg_match_all with json_decode if so.
readfile is not super efficient. Consider being creative with a 302 response.
Nginx + Lua
sites-enabled:
lua_package_path "/etc/nginx/conf.d/lib/?.lua;;";
server {
listen 80 default_server;
listen [::]:80 default_server;
location /getfile {
root /var/www/html;
resolver 8.8.8.8;
set $filepath "/index.html";
access_by_lua_file /etc/nginx/conf.d/getfile.lua;
try_files $filepath =404;
}
}
Test if lua is behaving as expected:
getfile.lua (v1)
ngx.var.filepath = "/static/...";
Simplify the Golang response body to just return a bland path then use it to set filepath:
getfile.lua (v2)
local http = require "resty.http"
local httpc = http.new()
local query_string = ngx.req.get_uri_args()
local res, err = httpc:request_uri('https://go.example.com/getpath?file_id=' .. query_string["id"], {
method = "GET",
keepalive_timeout = 60,
keepalive_pool = 10
})
if res and res.status == ngx.HTTP_OK then
body = string.gsub(res.body, '[\r\n%z]', '')
ngx.var.filepath = body;
ngx.log(ngx.ERR, "[" .. body .. "]");
else
ngx.log(ngx.ERR, "missing response");
ngx.exit(504);
end
resty.http
mkdir -p /etc/nginx/conf.d/lib/resty
wget "https://raw.githubusercontent.com/ledgetech/lua-resty-http/master/lib/resty/http_headers.lua" -P /etc/nginx/conf.d/lib/resty
wget "https://raw.githubusercontent.com/ledgetech/lua-resty-http/master/lib/resty/http.lua" -P /etc/nginx/conf.d/lib/resty

Nginx - proxy pass subpaths only

I would like to proxy the subpaths of my website to another service:
http://some-web-site.com/friends/ - renders /friends/index.html
http://some-web-site.com/friends/ [not empty request path] - proxy to another service.
Currently I have the following Nginx configuration:
location /programming/ {
(...)
proxy_pass http://tomcat:8080/friends;
}
But unfortunately this proxies /programming/ to http://tomcat:8080/friends.
Use an exact match location block to extract specific URIs for special handling:
location = /programming/ {
...
}
location /programming/ {
...
proxy_pass http://tomcat:8080/friends;
}
See this document for details.

Nginx: proxy_pass cannot have URI part in location inside "if" statement

Suppose there's a nginx configuration:
server {
...
location /nakayoshi {
if ($georedirect) {
proxy_pass http://foo.bar/fa/fb/fc;
}
proxy_pass http://foo.bar/fa/fb/fd;
}
}
When I sudo nginx -t, it prints out:
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular express, or inside named location, or inside "if" statement, or inside "limit_except" block in /etc/nginx/conf.d/nakayoshi.conf
Also I've found rewrite help me here, but the redirected uri will change to urls like "http://foo.bar/fa/fb/fc".
Can I keep the redirected uris unchanged with proxy_pass?
Try something like this
http {
...
map $georedirect $proxyuri {
"" fa/fb/fd;
default fa/fb/fc;
}
server {
...
location /nakayoshi {
proxy_pass http://foo.bar/$proxyuri;
}
}
}

nginx - response based on the requested header

I have nginx 1.0.8 installed.
here is my problem:
I have 2 files : file1.js and file2.js. the requested path is something like this:
www.mysite.com/files_dir/%user%/file.js
If the requested header : "X-Header" exists and has the value "OK" then the responded content should be file1.js else file2.js.
The files are situated in "html/files_dir" and %user% is a set of directories that represents the usernames registered through my service.
How do I configure this in nginx? I'm not interested in php, asp or similar technologies only if it's possible with nginx.
Thanks
map lets you define a variable's value based on another variable. map should be declared at http level (i.e. outside of server):
map $http_x_header $file_suffix {
default "2";
OK "1";
};
Then the following location should do the trick using your new variable $file_suffix
location ~ ^(/files_dir/.+)\.js$ {
root html;
try_files $1$file_suffix.js =404;
}
You could do this with nginx very easily. This is example:
location /files_dir/ {
set $file = file2.js;
if ( $http_x_header = OK ) {
set $file = file1.js;
}
rewrite ^(/files_dir/.*)/file.js$ $1/$file last;
}
You could read about HTTP variables in NGINX here , and about nginx rewrite module here

Resources