How to extract some value from cookie in nginx - nginx

I am new to Nginx and hope to get some help.
I want to extract certain data (certain fields set by my PHP scripts) from browser cookie in nginx so that I can log it. If possible, I want to do this just by modifying nginx configuration.
Any pointer/help would be greatly appreciated.

You can access cookie values by using the $cookie_COOKIE_NAME_GOES_HERE variable.
See Nginx Documentation

If anyone is using the previous answer with several different cookies in the response the correct regex is:
map $http_cookie $auth_header {
default "";
"~*OAuth.AccessToken=(?<token>[^;]+)" "Bearer $token";
}
or more general usage:
map $http_cookie $auth_header {
default "";
"~*yourCookieName=(?<variable>[^;]+)" "the value you wanna set $variable";
}

Here's an example to extract an HttpOnly cookie and pass it on to a RESTful api as an OAuth Bearer token:
http {
map $http_cookie $auth_header {
default "";
"~*OAuth.AccessToken=(?<token>.+)" "Bearer $token";
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/nginx.crt;
ssl_certificate_key /etc/nginx/certs/nginx.key;
proxy_set_header Authorization $auth_header;
location / {
proxy_pass https://rest-api-host.domain.com/;
}
}
}

Related

Multiple variables in a single map directive

I have the following map based on the value of a cookie:
map $cookie_version $version_host {
default 172.17.0.1:8001;
2.65.1 172.17.0.1:8001;
2.65.2 172.17.0.1:8003;
}
However I need the same map for the value of a header:
map $http_app_version $version_api_host {
default 172.17.0.1:8001;
2.65.1 172.17.0.1:8001;
2.65.2 172.17.0.1:8003;
}
Is there a way to not duplicate the map?
My server block:
server {
listen 443 ssl;
ssl_certificate /etc/ssl/nexello.crt;
ssl_certificate_key /etc/ssl/nexello.key;
server_name _;
proxy_set_header Host $host;
proxy_set_header X-FORWARDED-FOR $remote_addr;
charset utf-8;
location / {
# Web based access
# Cookies are used instead of a header because the request passes through here before any javascript can even set a Header.
if ($cookie_token) {
proxy_pass https://$version_host;
}
# API based access
# Here we use headers because anyone that is using the API has full control of the headers.
if ($http_authorization) {
proxy_pass https://$version_api_host;
}
# Auth
# In case there is no header or cookie we assume that it has no access token and should be redirected to the auth machine.
proxy_pass https://172.17.0.1:7001;
}
}
You could use
map $cookie_version$http_app_version $version_host {
default 172.17.0.1:8001;
# your complicated regex here since two variables will be concatenated.
}
and use $version_host in proxy_pass.
Also offtopic, since you're using if be careful with it
https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/
I somewhat solved this as while there still is two maps, the options are stored in another file so there won't be any reptition:
map $cookie_version $version_cookie_host {
include /etc/nginx/versions.conf;
}
map $http_authorization $version_header_host {
include /etc/nginx/versions.conf;
}
Contents of /etc/nginx/versions.conf:
default 172.17.0.1:8001;
2.65.1 172.17.0.1:8001;
2.65.2 172.17.0.1:8003;

Nginx passing url arguments as header

In the following example:
http {
server { # simple reverse-proxy
listen 8080;
location / {
set $token $arg_token;
#return 200 $token;
add_header test "test $token";
proxy_pass http://localhost:5601;
}
} ...
}
if I leave return 200 $token I obtain the token as response + in header (which is a normal behavior) but when I delete return I obtain only "test" as test header value, what am I missing please ?
The proxy_set_header sets header that NGINX will use while communicating to the upstream/backend.
You won't see that added header in the response of NGINX back to the client.
If you want to see it, use add_header as well.

Nginx - Decode URL query parameter and forward it as request header

I need to send some basic auth credentials (es. user:pass) to nginx in the form of query parameter (es. http://example.com?BASIC_AUTH=dXNlcjpwYXNz) and being able to forward them in the more usual Authorization: Basic dXNlcjpwYXNz header form to a target server behind the proxy.
I'm already able to retrieve the value of the encoded auth string with a regular expression. The problem is that very often that value may contain some character that need to be percent-encoded in the URL. Es. user:pass! -> ?BASIC_AUTH=dXNlcjpwYXNzIQ== becomes ?BASIC_AUTH=dXNlcjpwYXNzIQ%3D%3D
Therefore, when I forward the request to the target server, I end up specifing Authorization: Basic dXNlcjpwYXNzIQ%3D%3D which the target server will reject, giving a 401 Unauthorized.
How can I force nginx to decode the auth string before setting the Authorization header? Thanks in advance for your help.
Note: I can't send the auth string in the Authorization header in the first place due to some application-specific constraints.
"Pure" nginx solution
Unfortunately nginx does not provide a rich string operations set. I think there isn't a way to do global search-and-replace through some string (which can be a solution if we could replace all %2B with +, %2F with / and %3D with =). However there are circumstances under which nginx performs an urldecoding of some string - when this string becomes a part of an URI which will be forwarded to an upstream proxy server.
So we can add a value of a BASIC_AUTH request argument to the URI and make a proxy request to ourself:
# Main server block
server {
listen 80 default_server;
...
location / {
if ($arg_basic_auth) {
# "basic_auth" request argument is present,
# append "/decode_basic_auth/<BASE64_token>" to the URI
# and go to the next location block
rewrite ^(.*)$ /decode_basic_auth/$arg_basic_auth$1 last;
}
# No "basic_auth" request argument present,
# can do a proxy call from here without setting authorization headers
...
}
location /decode_basic_auth/ {
# This will be an internal location only
internal;
# Remove "basic_auth" request argument from the list of arguments
if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
set $args $1$3$4;
}
# Some hostname for processing proxy subrequests
proxy_set_header Host internal.basic.auth.localhost;
# Do a subrequest to ourselfs, preserving other request arguments
proxy_pass http://127.0.0.1$uri$is_args$args;
}
}
# Additional server block for proxy subrequests processing
server {
listen 80;
server_name internal.basic.auth.localhost;
# Got URI in form "/decode_basic_auth/<BASE64_token>/<Original_URI>"
location ~ ^/decode_basic_auth/([^/]+)(/.*)$ {
proxy_set_header Authorization "Basic $1";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>$2$is_args$args;
}
# Do not serve other requests
location / {
return 444;
}
}
Maybe this is not a very elegant solution, but it is tested and works.
OpenResty / ngx_http_lua_module
This can be easily solved with openresty or ngx_http_lua_module using ngx.escape_uri function:
server {
listen 80 default_server;
...
location / {
set $auth $arg_basic_auth;
if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
set $args $1$3$4;
}
rewrite_by_lua_block {
ngx.var.auth = ngx.unescape_uri(ngx.var.auth)
}
proxy_set_header Authorization "Basic $auth";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>;
}
}

How to process response from www-authenticate in nginx?

I'm using openresty nginx v1.11.2.4. I wish to be able to authenticate users before they are given access to a resource or before they try to PUT something on the server. I am using the http_auth_request_module and the following is an except from my nginx.conf file:
location /video/ {
auth_request /auth;
root /usr/local/openresty/nginx/html;
}
location = /auth {
more_set_headers "WWW-Authenticate: Basic";
return 401;
}
This results in the browser asking for user credentials alright but now how do I get/process the user credentials from the client?
The ngx_http_auth_request_module implements client authorization based on the result of a subrequest.
If you want to use basic authentication you don't need to use ngx_http_auth_request_module. Use http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html
following the answer to the question here: Nginx authentication with auth_request module
I was able to process the username and password by accessing the $http_authorization variable in my nginx.conf file. The following is an excerpt from my nginx.conf:
location /video {
satisfy any;
auth_basic "Private Property";
auth_basic_user_file /usr/local/openresty/nginx/conf/htpasswd;
auth_request /auth;
root /usr/local/openresty/nginx/html;
client_max_body_size 1000M;
if ($request_method != "GET"){
content_by_lua_file /root/Documents/contentbylua.lua;
}
}
location = /auth {
set $authHeader $http_authorization;
set $authUName $remote_user;
content_by_lua_file /root/Documents/restrict.lua;
}
The following conf allows me to authenticate a user whose credentials are stored in a redisDB in the restrict.lua file which returns a 200 or 401 code depending on the credentials of the user back to the /location block.
The response (username & password) is accessed in the restrict.lua file by ngx.var.authHeader. Some string processing is done to remove the 'Basic' then the remnant is base64 decoded and then some string processing is done on it to obtain the password. That's all

How should I configure Nginx to proxy to a URL passed by parameter?

I'm trying to get access to media files (images, videos) sitting behind an OAuth2 authentication.
In order to access the resource I need to add a custom Authorization Bearer token to the request, so I can't use a simple rewrite (well, as far as I know at least).
It cannot be done via plain HTML (say img or video tag) so I'm considering to have Nginx proxying the queries to the final server.
Each of the media resources would be loaded via a /proxy path, with a token parameter (for authentication) and url for the actual resource to load.
Sample URL:
http://myserver.com/proxy/?token=12345&url=http://protectedserver.com/custompath/asset
This is what I came up with but I am not quite sure how to configure the proxy_pass directive since I need it to proxy to the $url variable specifically. I do not need to proxy the path (which would be empty anyway).
location /proxy/ {
if ($arg_token ~ "^$") { return 404; }
if ($arg_url ~ "^$") { return 404; }
set $url $arg_url;
proxy_set_header Authorization "Bearer $arg_token";
set $args "";
#proxy_pass $url;
}
Note: this will be run in a closed environment and only specific machines (kiosks with limited interaction) will be able to access the page so I'm not concerned about a potential leak of the auth token.
I noticed a similar question on ServerFault, but no one had an answer to that:
https://serverfault.com/questions/671991/nginx-proxy-pass-url-from-get-argument
I'm looking for a config setting to make it work or a viable alternative solution.
Here is a correct configuration for my problem:
location /proxy/ {
if ($arg_token ~ "^$") { return 404; }
if ($arg_url ~ "^$") { return 404; }
set $url $arg_url;
set $token $arg_token;
set $args "";
# IMPORTANT, this is required when using dynamic proxy pass
# You can alternatively use any DNS resolver under your control
resolver 8.8.8.8;
proxy_pass $url;
proxy_set_header Authorization "Bearer $token";
proxy_redirect off;
}

Resources