I am trying to write an nginx configuration for authentication, but I am unsure of how to retain headers when using a 302 redirect.
Below is an example of how the server is setup.
location / {
#send to /auth/ for authentication
request_auth /auth/;
#upon failure, redirect to login
error_page 401 404 = #noaccess;
...
}
location #noaccess {
return 302 /login/;
}
location /auth/ {
internal;
proxy_pass http://127.0.0.1:8888/;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
#The header set here is what I would like to retain
proxy_set_header X-Original-URI $request_uri;
}
location /login/ {
proxy_pass http://127.0.0.1:9000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
#by the time 302 redirect, $request_uri is no longer the original...
proxy_set_header X-Target $request_uri;
}
Essentially all requests to the server must first be authenticated in the /auth/ location. This location will generally possess the $request_uri that I care about in its sub request. However, whenever the requeset_auth directive fails, and we are redirected with the #noaccess location, nginx makes a new request to /login/.
Is there a way for me to retain the original $request_uri such that it is sent as a header to the /login/ location? (this is needed in order to do a redirect later upon successful authentication).
I've seen some users talk about the auth_request_set directive, but I honestly do not understand how to use it effectively and how the headers of the sub request are renamed or made available.
Yes. With cookies.
Browser state is managed with cookies, so you need to be sending a Set-Cookie header with the 302 that has $request_uri and then convert the cookie to a header in the /login/ location.
Related
im trying to mock sites from my own FS. i want to intercept any http request and redirect it to my server that will return the needed file.
i have wrote a simple default sever:
server {
listen <<SERVICE_PORT>> default_server;
server_name _;
location / {
proxy_set_header X-ORIGINAL-URI $request_uri;
proxy_set_header X-ORIGINAL-REFERRER $http_referer;
proxy_set_header X-ORIGINAL-HOST $http_host;
proxy_pass <<BROWSE_RESOURCE_URL>>/browsing/resource/;
proxy_redirect off;
}
}
when a url as "http://exapmle.com" enters it works fine. but when any path is added as "http://exapmle.com/bar" it dose not pass the to <<BROWSE_RESOURCE_URL>>/browsing/resource/. currently i recive 404 but not from my server.
offcurce i dont need the orignal uri to be concated to my proxy_pass at all.
why dosent it work for me?
From Nginx's documentation:
A request URI is passed to the server as follows:
If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive
So, for your given configuration, when you request http://example.com/bar:
The normalized request URI will be /bar
The URI specified in the proxy_pass directive will be /browsing/resource/
The final URI that will be passed to the backend server is /browsing/resource/bar.
You have not configured the backend server to understand /browsing/resource/bar. So it only understands /browsing/resource/. That's why your backend server returned a 404 not found.
Because you don't want Nginx to combine the request URI with the URI specified in the proxy_pass directive, you can use another feature of the proxy_pass directive as mentioned in the Nginx's documentation:
When the URI is changed inside a proxied location using the rewrite directive, and this same configuration will be used to process a request (break):
...
In this case, the URI specified in the directive is ignored and the full changed request URI is passed to the server.
So you will instruct Nginx to rewrite all request URIs to the same URI /browsing/resource/ as follows:
location / {
proxy_set_header X-ORIGINAL-URI $request_uri;
proxy_set_header X-ORIGINAL-REFERRER $http_referer;
proxy_set_header X-ORIGINAL-HOST $http_host;
rewrite ^ /browsing/resource/ break;
proxy_pass <<BROWSE_RESOURCE_URL>>;
proxy_redirect off;
}
My nginx config file goes following:
server {
location /mysite {
auth_request /authVerify;
proxy_pass http://localhost:4200;
error_page 401 = /login;
}
location /authVerify {
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_pass http://localhost:3000;
}
location /login {
proxy_cookie_path / "/; HttpOnly";
proxy_pass http://localhost:3000;
}
location / {
root html;
index index.html index.htm;
}
}
log related configs use the default settings.
the auth_request configration works. But when I send request to /mysite, there is only logging of it in access log, there is no logging of /authVerify although it actually proxy through this locatoin. If I send request to /authVerify directly, there will be loggings as well.
So in the redirect cases how to produce logs for all the locations the request running through?
Update
Based on the comment, I set log_subrequest as on in http block level. After this change, the logs of internal rediect was produced, but the log of original mysite location disappear.
Currently after I send one request to /mysite, the log is as following:
I found the following explanation on nginx doc:
Requests are logged in the context of a location where processing ends. It may be different from the original location, if an internal redirect happens during request processing.
http://nginx.org/en/docs/http/ngx_http_log_module.html
Is that because of that? Any more methods to log the request's entire flow?
Have you tried enabling log_subrequest?
log_subrequest
Context: http, server, and location
Enables or disables logging of sub-requests triggered by internal redirects or SSI requests.
Syntax: on or off
Default value: off
I had a proxy server which redirects communications to some api on customer side via https. When I use configuration with set upstream variable (proxy_pass $upstream_endpoint$request_uri;), the DNS resolving for this domain (dynamic changing IP adress) is working well but I get response 403 unauthorized.
When I use configuration without upstream (proxy_pass https://api-test.example.com/api/), point directly to customer domain it works well, I am getting response 200 but DNS resolver is not working anymore..
Nginx config:
location /api-test.example.com/api/ {
resolver 10.100.10.1 valid=5s;
set $upstream_endpoint https://api-test.example.com;
proxy_pass $upstream_endpoint$request_uri;
#proxy_pass https://api-test.example.com/api/;
proxy_ssl_name api-test.example.com;
proxy_ssl_server_name on;
proxy_set_header Host api-test.example.com;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
By adding a URI to the proxy_pass statement, the requested URI is rewritten before passing it upstream. See this docuement for details.
So the URI /api-test.example.com/api/foo is rewritten to /api/foo.
You can achieve the same behaviour with a rewrite...break statement. See this document for details.
location /api-test.example.com/api/ {
rewrite ^/api-test.example.com(.*)$ $1 break;
set $upstream_endpoint https://api-test.example.com;
proxy_pass $upstream_endpoint;
...
}
I'm having a hard time configuring nginx to act as a proxy of a public S3 endpoint. My use case necessitates altering the status code of the S3 response, while preserving the response payload.
The possible status codes returned by S3 include 200 and 403. For my use case, I need to map those status codes to 503.
I have tried the following which does not work:
location ~* ^/.* {
[...]
proxy_intercept_errors on;
error_page 200 =503 $upstream_http_location
}
Nginx outputs the following error:
nginx: [emerg] value "200" must be between 300 and 599 in /etc/nginx/nginx.conf:xx
Here's a more complete snippet:
server {
listen 80;
location ~* ^/.* {
proxy_http_version 1.1;
proxy_method GET;
proxy_pass http://my-s3-bucket-endpoint;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Connection "";
proxy_set_header Host my-s3-bucket-endpoint;
proxy_set_header Authorization '';
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_hide_header Set-Cookie;
proxy_ignore_headers "Set-Cookie";
proxy_cache S3_CACHE;
proxy_cache_valid 200 403 503 1h;
proxy_cache_bypass $http_cache_purge;
add_header X-Cached $upstream_cache_status;
proxy_intercept_errors on;
error_page 200 =503 $upstream_http_location;
}
}
Is it possible to achieve what I need with nginx?
I found a more or less suitable solution. It's a bit hackish but it works.
The key was to set the index document of my S3 bucket to a non-existing filename. This causes requests to / on the S3 bucket endpoint to result in 403.
Since the nginx proxy maps all incoming requests to / on the S3 bucket endpoint, the result is always 403 which the nginx proxy can intercept. From there, the error_page directive tells it to respond by requesting a specific document (in this case error.json) in the S3 bucket endpoint and use 503 as the response status code.
location ~* ^/. {
proxy_intercept_errors on;
error_page 403 =503 /error.json;
}
This solution involves two requests being sent to the S3 bucket endpoint (/, /error.json) but at least caching seems to be enabled for both requests using the configuration in the more complete snippet above.
I am using nginx as a single point of entry to my entire solution.
my goal is to send some url to be authenticated before continuing.
I have found a module named: ngx_http_auth_request_module which suits right in place to solve my problem.
http://nginx.org/en/docs/http/ngx_http_auth_request_module.html
i am compiling my nginx like this: ./configure --with-http_auth_request_module
my code looks like this:
http {
server {
listen 8080 default_server;
server_name _;
location /api {
auth_request /auth;
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location = /auth {
proxy_pass http://localhost:8000/auth/user;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}
}
my problem is that if my server returns and error, like 401, then a default page gets shown. what i want is to be able to return the json that was returned from my authentication service. this is useless without this. what is the point of showing just a generic 401 page? i want to return my proxied json response.
please help.
auth_request is just for authentication. This little hack should work for you
error_page 401 /auth;
After auth error it'll go to /auth location again, this time as ordinary request.