I use openresty as nginx server with spnego-http-auth-nginx-module
This module replace request header Authorization: Negotiate YIIG... with Authorization: Basic ... and set REMOTE_USER header.
How to copy original Authorization header value to another custom header key to save original authorization header value?
This config snippet return needed data:
...
set_by_lua_block $xauth {
local inp = ngx.req.raw_header(true)
return string.match(inp, "Negotiate .*==")
}
uwsgi_param XAUTH $xauth;
...
Related
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.
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>;
}
}
To start with: I am NOT an nginx expert. Very much a newbie to it.
I am attempting to protect a 3rd party piece of software with nginx doing the authentication (really - just verifying that the request has a valid OAuth2 Bearer token)
The HTTP request will have an OAuth2 bearer token in the Authentication header.
e.g. Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZ....H5w
I have an OAuth2 server (UAA) that has an api where I can call http://myuaa/check_token?token=eyJhbGciOiJSUzI1NiIsImtpZ....H5w to get back a 2XX or a 4XX if the token is valid. A complication is that this server does require basic auth to call the /check_token endpoint.
I have tried using a map to parse the token from the authorization header, but with no luck.
Just kind of at a loss.
Perhaps this isn't a good fit for Nginx?
relevant pieces of the nginx.conf
# this map isnt working as I thought it might
http {
...
map $http_authorization $token {
~Bearer(?<token>abc) $token;
}
...
# test just to see if the authorization header is being parsed and passed - no luck
location /oauth {
proxy_set_header X-my-header $token;
proxy_set_header X-another-header value;
proxy_set_header Authorization "Basic basdasdfasdf";
proxy_pass http://localhost:8080;
}
Expected request to the 3rd party server that nginx is protecting:
<GET|POST|PUT|DELETE> /anyurl HTTP1/1.1
..
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZ....H5w
..
Expected request forwarded to the UAA server to validate token
GET /check_token?token=eyJhbGciOiJSUzI1NiIsImtpZ....H5w
..
Authorization Basic asfasdfdf
..
Your map directive isn't working, named group token somehow interfere with the $token variable, any of these definitions would work:
map $http_authorization $token {
~^Bearer\s+([\S]+)$ $1;
}
or
map $http_authorization $token {
~^Bearer\s+(?<bearer>[\S]+)$ $bearer;
}
Full working config will be looking like this:
map $http_authorization $token {
~^Bearer\s+(?<bearer>[\S]+)$ $bearer;
}
server {
...
location / {
auth_request /uaa;
...
}
location /uaa {
internal;
proxy_pass_request_body off;
proxy_set_header Authorization "Basic your_base64_auth_string";
proxy_set_header Content-Length "";
proxy_pass http://localhost:8080/check_token?token=$token;
}
}
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/;
}
}
}
I have a client program I cannot modify. It makes large POST (x-www-form-urlencoded) requests containing hundreds of variables across WAN links, but I only need 5 of them. I'm inserting nginx as a reverse proxy on the local client system. What's the easiest to get nginx to strip out the extra data?
Two ways I see so far:
1. Use Lua (If I did, should I do content_by_lua, rewrite the body, and then make a subrequest? Or is there a simpler way?)
2. Use form-input-nginx-module and proxy_set_body to parse and grab a few variables out.
I'm already using OpenResty, so Lua means no extra modules. But, it probably means writing more locations and so on to do subrequests.
In my opinion the easiest way will be using lua. The choice between content_by_lua, rewrite_by_lua, access_by_lua or any combination of them; will depend on how you use the response body of your subrequest. That decision will also determine if you would need additional locations or not.
Here are a couple of examples:
1. with content_by_lua targeting a local location.
(This approach requires the definition of the sub request location)
location /original/url {
lua_need_request_body on;
content_by_lua '
--Lots of params but I only need 5 for the subrequest
local limited_post_args, err = ngx.req.get_post_args(5)
if not limited_post_args then
ngx.say("failed to get post args: ", err)
return
end
local subreq_uri = "/test/local"
local subreq_response = ngx.location.capture(subreq_uri, {method=ngx.HTTP_POST,
body = ngx.encode_args(limited_post_args)})
ngx.print(subreq_response.body)
';
}
location ~/test/local {
lua_need_request_body on;
proxy_set_header Accept-Encoding "";
proxy_pass http://echo.200please.com;
}
2. with rewrite_by_lua to remote target
(No additional location is needed)
location /original/url/to/remote {
lua_need_request_body on;
rewrite_by_lua '
--Lost of params but I only need 5 for the subrequest
local limited_post_args, err = ngx.req.get_post_args(5)
if not limited_post_args then
ngx.say("failed to get post args: ", err)
return
end
--setting limited number of params
ngx.req.set_body_data(ngx.encode_args(limited_post_args))
--rewriting url
local subreq_path = "/test"
ngx.req.set_uri(subreq_path)
';
proxy_pass http://echo.200please.com;
}
Sample post request with 7 args limited to 5:
curl 'http://localhost/original/url/to/remote' --data 'param1=test¶m2=2¶m3=3¶m4=4¶m5=5¶m6=6¶m7=7' --compressed
response:
POST /test HTTP/1.0
Host: echo.200please.com
Connection: close
Content-Length: 47
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Accept: */*
Accept-Encoding: deflate, gzip
Content-Type: application/x-www-form-urlencoded
param3=3¶m4=4¶m1=test¶m2=2¶m5=5