nginx proxy all request through authentication service - nginx

Consider a dockerized environment containing the following containers:
Backend API
Front-end REACT App served using pushstate-server
Authentication Service
Nginx Container
My nginx.conf contains the following:
server {
listen 8080;
location / {
auth_request /auth;
proxy_pass http://frontend:5000;
}
location = /auth {
proxy_pass http://auth:6000;
}
error_page 403 = #error403;
location #error403 {
rewrite ^ /login$1;
proxy_pass http://frontend:5000;
}
}
When the auth_request /auth; line is commented out, everything works just fine and all frontend pages can be accessed.
As soon as I introduce the auth_request I can see the authentication service return a 403 however, it does not look like Nginx proxies to the login page.
What am I doing wrong?

There are two issues here:
Firstly, the authorization header is not forwarded to the authentication service. This was fixed with
location = /auth {
proxy_pass http://auth:6000;
proxy_pass_header Authorization;
}
Secondly, when a request is made to the frontend, nginx tries to authenticate with the auth container. As I am not authenticated, this fails and returns a 403. The nginx server then proxies to the login page on the REACT container, however, there are further request behind the scenes to retrieve css and js resources from the same container, for which the nginx gateway tries to authenticate. Again, as I am not authenticated retrieving these resources fails, so the page does not render.
A dirty solution was to add:
location /static/js/main.1e2389bc.js {
proxy_pass http://web:5000;
}
location /static/css/main.aa587518.css {
proxy_pass http://web:5000;
}
This retrieves the necessary files to render the login page with trying to authenticate. This is a bad solution as there may be other resources (favicon, other media etc.) so more blocks would need to be added. I am sure there is a simple solution using regex to sort this out in a simple way.
However, I ended up with a cleaner solution. Authenticate on requests to the backend API. This ensured that no sensitive information was displayed on the frontend without being authenticated and removed the hassle of hacking a solution to render the login page.

Related

nginx proxy_pass to location returns 401 error

I have a WebDAV server set up, with its root folder /SomeVolume/webdav/contents and address 192.168.1.2:12345. User and password are set and the server can be accessed from a browser.
I am directing a domain name to the same machine using nginx, like this:
server {
server_name my.domain.me;
location / {
proxy_pass http://192.168.1.2:12345;
}
# plus the usual Certbot SSL stuff
This was working perfectly well, with HTTPS authentication and everything. I am using a third-party application that uses that server and it was working OK too.
I wanted to make this a bit more tidy and only changed couple of things:
WebDav server root to /SomeVolume/webdav (instead of /SomeVolume/webdav/contents), restarted the server.
proxy_pass http://192.168.1.2:12345 changed to proxy_pass http://192.168.1.2:12345/contents. Restarted ngninx.
Nothing else was modified.
I can still login through the browser, but the third-party application has stopped working because it gets authentication errors (401). Although if I try to login locally with http://192.168.1.2:12345/contents/ it works just fine.
What am I not understanding here? Is it some caching problem with the third-party application or have I misunderstood how location & proxy_pass work?
Thanks.

NGINX Proxy to another internal NGINX

I have a single entrypoint into a microservice application which is done via NGINX proxying in the main frontend application.
I have another frontend microservice (Let's call it FE2) that is not accessible from outside.
My NGINX config of the primary frontend looks the following:
set $fe2 "<kubernetes service address of my second frontend app>";
location /fe2 {
rewrite ^/fe2/(.*)$ /$1 break;
proxy_pass http://$fe2;
proxy_redirect / /fe2;
proxy_set_header Host $host;
}
So in theory, what should happen is that all requests that come to /fe2 should be proxies to my second frontend application via the rewrite and proxypass directives.
The proxy_redirect directive is there in place to handle all the redirects that are in place in my second frontend application.
The first thing I am concerned about are the elements of my second frontend that have href properties - will they be able to send requests to proper endpoints (xxx/fe2/somepage instead of just xxx/somepage)? The second frontend application is a webpack bundle, so all the page changes should not even reach NGINX.
Second thing is more of an issue - I already deployed this setup, but noticed that when I go to xxx/fe2/ the page is just blank, and the error message states Uncaught SyntaxError: Unexpected token '<' (at main.someid.js:1:1) . The file has a .js extension, but the actual code inside is HTML, so I don't know what's going on in there(
I know that the question is complicated, but have any of you folks tried the same setup, or are there any alternatives using NGINX?
I would appreciate any help on this matter!

How to set exceptions for NGINX load balancer

Is it possible to configure NGINX loadbalancer in least_conn mode to make exception for certain paths?
I want to configure loadbalancer in such way that all requests required for single login operation are sent to the same backend application instance.
I have frontend app accessing duplicated backend app via nginx load balancer. All apps are deployed on Tomcat 8.5 and backend instances have configured session replication between Tomcats.
My problem is that when user is authenticated using OAuth-2.0 authorization_code grant method, frontend app gets authorization code but due to conneting to backend through load balancer it tries to obtain token using this code from another machine resulting in InvalidGrantException.
Using ip_hash mode or it's variations isn't solution for this problem as it is unstable when application is accessed through VPN.
Yes you can achieve what you want by declaring two locations and treat them differently. See example below and check this question where it explains how the priority works.
http {
upstream myapp1 {
least_conn;
server srv1.example.com;
server srv2.example.com;
server srv3.example.com;
}
server {
listen 80;
location / {
proxy_pass http://myapp1;
}
location /my-special-path/ {
proxy_pass http://srv1.example.com;
}
}
}
Above is a solution mainly based in your first statement that you want routing based on certain paths. If your problem is more complicated i.e these paths are dynamically created etc you can share an example to be easier to understand your specific situation.
UPDATE
Based on comment. I would really suggest to go troubleshoot your backend in order to be synced. That being said if you really want a solution for the exact problem from your nginx I would do the following:
On every response I would add a specific header which specific backend answered this request. add_header X-Upstream $upstream_addr;
On this specific path I would serve the request based on the value of that header. proxy_pass http://$http_x_upstream;
So the config would look like this:
http {
...
server {
...
location / {
add_header X-Upstream $upstream_addr always;
proxy_pass http://myapp1;
}
location /authorize/ {
add_header X-Upstream $upstream_addr always;
proxy_pass http://$http_x_upstream;
}
}
}
NOTE: Security. If you go down this path be careful that you are routing your requests based on a value that your client can manipulate. So be sure that you are at least validating this value. Check this answer for validating headers with nginx.

Nginx redirect to url with /#/url

I'm new to using nginx and I'm trying to redirect one url to another.
https://server.com/#/page.html -> https://server.com/page.html
The /#/ comes from using Angular2's router functionality. I decided to change the page to static html because Angular2 loads the entire app at start up and it's unnecessary for this single page of information to be presented as such.
My attempts at solving the problem is as follows. I can redirect from /anotherpage -> /page.html but /#/anotherpage -> /page.html loads the Angular2 app and tries to route to the non existent route.
Using this config and location directives:
server {
listen 8001;
server_name server.com;
...
#SSL Things
...
root /to/root/folder/public;
#Redirects:
location = /anotherpage {
rewrite ^/.* https://$server_name/page.html;
}
#Does not redirect:
location = /#/anotherpage {
rewrite ^/.* https://$server_name/page.html;
}
}
Any suggestions on best practices are welcome as well.
The browser does not send any information after a # is encountered.
Thus when sending https://server.com/#/page.html only https://server.com/ is seen by nginx.
Thus redirecting is not possible and changing the client side application is the only option.

nginx auth_basic "Restricted" prompting login on every request

I've set up a simple nginx server, configured the location block to point to the respective directories I want served, and setup basic authentication using the auth_basic module.
However, my server requests username : password credentials on every single page request under the location block, even after providing them multiple times to different pages under the location block, including the root location directory.
How can I configure it to store the authentication? Is this an nginx issue or a browser / request headers issue?
Here is the nginx configuration:
server {
listen 80;
server_name 0.0.0.0;
location /path/to/dir {
alias /var/www/dir/;
index index.html index.htm;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files $uri $uri/ =404;
}
}
Running nginx 1.4.6 on Ubuntu.
The problem in my case was that one of the API calls that was called by JavaScript on page load was returning 401. This seemed to reset browser auth state for the page.
HTTP authentication information is stored on your browser cache, and should only be requested again if the authentication fails or it's from a different realm (in auth_basic "Restricted"; it's Restricted).
Your configuration is fine, considering your password is correct and Nginx user has read access to the password file (case in which it'll always fail — but send an error message at the log file indicating this error). This is the most probable reason, mainly if you have only one location with authentication.
Another possible reason is having multiple auth_basic directives and they use different realms or passwords. This is the same for application-generated WWW-Authenticate headers (say, if your backend application requests for HTTP authentication in addition to Nginx). When there's a different realm or a password fails, your browser will request it again. No browser that I know of stores authentication per URL, it's always a combination of realm+hostname.
If you do need different realms or passwords on different locations, make sure they don't overlap for a single page (for example, if you use a different password for your assets: images, styles or javascript). Or use different hosts — the password would be requested once for each host/realm combination, though.
Update
It's unusual to use 0.0.0.0 as a server_name — listen 80; already makes your server to listen to all interfaces/IP addresses.
Use server_name _; in case you mean to use any request host.

Resources