How to get request url in upstream server after rewrite in nginx - nginx

I have nginx as a proxy server: client -> nginx (server1:80) -> server (server2:81)
The client requests a json resource (HAL format) from the server in which the server generates the needed URIs inside the JSON+HAL resource.
I'm using proxy_pass in nginx config:
location /context {
proxy_cache one;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_redirect off;
rewrite /context(.*) $1 break;
proxy_pass http://server2:81;
}
The problem is that the generated URIs from server2 do not contain the context root /context.
Example:
Client request URI -> http://server1/context/foo/bar
URI used by server1 (nginx) to request server2 -> http://server2:81/foo/bar
The generated URIs in the response JSON is http://server1/foo/bar/baz as it should be http://server1/context/foo/bar/baz
Is there a way to tell server2 that the GET header is /context/foo/bar that server2 generates the correct URI in its response? (it's a .NET server and they are using the HttpHelper class to generate the URI).

Related

KeyCloak Client Behind reverse proxy

I have the following issue.
Both, my KeyCloak and the Client are behind a reverse Proxy.
My reverse Proxy Configuration for the Client:
server_name my.domain.here.com;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
proxy_pass http://127.0.0.1:9254;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
When the Client now tries to sign in with oAuth, KeyCloak has the following error:
Invalid parameter: redirect_uri
Because the valid redirect URI I specified is: my.domain.here.com/*
And the requestion redirect URI is: http://localhost/9254/*
Is there a way, to pass the server-name to the KeyCloak?

nginx reverse proxy remove subpath on upstream

I have an api server I am reverse proxying with nginx. It is functionally working but I want to change the current behavior.
api server url:
http://apiserver:5000/api/v1/ping
the above becomes accessible by this nginx url (see the double 'api' part?):
https://nginxserver/api/api/v1/ping
How can I write the config so that /api hits the api server but without adding an additional 'api' to the nginx url.
location ^~ /api {
proxy_pass http://apiserver:5000/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Script-Name /api;
}

Proxying NGINX Traffic To Secondary Proxy with Proxy_Protocol Enabled

I am trying to route requests such that those requiring websockets will route to a long-lived nginx process, and all others will go to the general reverse-proxy which handles all other traffic. These nginx processes exist in our AWS cloud behind an ELB that has been configured to use Proxy Protocol. Note that all of this works correctly with our current setup which uses only one nginx process that is configured to use proxy_protocol.
The change to this setup is as follows:
The first nginx server handling all ingress uses proxy_protocol and forwards requests to either the websocket or non-websocket nginx servers locally:
server {
listen 8080 proxy_protocol;
real_ip_header proxy_protocol;
charset utf-8;
client_max_body_size 20M;
#send to websocket process
location /client {
proxy_pass http://localhost:8084;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Proxy-Scheme $scheme;
proxy_set_header X-Proxy-Port $proxy_port;
proxy_set_header X-ELB-Proxy-Scheme "https";
proxy_set_header X-ELB-Proxy-Port "443";
# Always support web socket connection upgrades
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
#send to non-websocket process
location / {
proxy_pass http://localhost:8082;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Proxy-Scheme $scheme;
proxy_set_header X-Proxy-Port $proxy_port;
proxy_set_header X-ELB-Proxy-Scheme "https";
proxy_set_header X-ELB-Proxy-Port "443";
# Always support web socket connection upgrades
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
When any non-websocket request is sent to localhost:8082, I get an empty reply. If I remove proxy_protocol from the first server, I get a response as expected. Obviously, I need proxy_protocol to support the ingress from our ELB, so removing it is not an option. However, I would like to know what pieces I am missing to route traffic correctly -- and I would also like to know why proxying a request locally from a proxy_protocol enabled server to another nginx process (regardless of this second process using proxy_protocol or not) fails.
For reference, the basic configuration of this secondary nginx process is below:
upstream console {
server localhost:3000 max_fails=3 fail_timeout=60 weight=1;
}
server {
listen 8082;
client_max_body_size 20M;
location /console {
proxy_pass http://console
}
.
.
.
}
Turns out the non-websocket proxy block should not set the various proxy and upgrade headers:
location / {
proxy_pass http://localhost:8082;
proxy_set_header Host $host;
}

nginx reverse proxy to multiple backend servers depending on URL

I have 1 frontend Nginx server and 2 backend nginx servers.
Frontend server does proxy pass to the backend server. Previously there was only 1 backend server so all my queries were going to the single backend server, but as the traffic increased, I have added 1 more backend server just for search queries.
But I can not make only search queries to go to the 2nd backend server.
my current configuration is like this.
server {
listen 80;
server_name example.com;
location /search/ {
proxy_pass https://search.example.com/;
proxy_set_header Host search.example.com;
#set custom headers for upstream server
proxy_set_header Accept-Encoding "";
proxy_set_header CF-Connecting-IP "";
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
proxy_pass https://box1.example.com/;
proxy_set_header Host box1.example.com;
#set custom headers for upstream server
proxy_set_header Accept-Encoding "";
proxy_set_header CF-Connecting-IP "";
proxy_ssl_verify off;
proxy_ssl_server_name on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
What I want to do is.
If URL is
https://example.com/search/test/1/8/0
then reverse proxy to search.example.com
else reverse proxy all other requests to box1.example.com
my current configuration gives 404 not found errors for search queries.
How can I fix it?

How to route to content_by_lua nginx directive depending on both HTTP Action and URL prefix?

I'd like to route all requests sent to my nginx server to my backend application by default, but selectively send API requests with GET HTTP verbs to a OpenResty Lua based REST API backed by a content_by_lua nginx directive.
I'm successfully able to route all API requests to the Lua API based on their URL prefix using the following configuration (note that this does not take into account the HTTP Verb):
http {
upstream backend {
server localhost:8080;
}
server {
listen 80;
location / {
# Send all requests to the backend application
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_set_header HTTP_CLIENT_IP $remote_addr;
proxy_redirect off;
}
location /api {
# Send any URL with the /api prefix to the nginx Lua API application
content_by_lua '
require("lapis").serve("app")
';
}
}
}
But, as I stated above, I'd like to further restrict API requests such that any requests with HTTP Verbs other than GET (like POST, PUT, DELETE, etc) are still routed to the backend, while the GET requests alone are routed to the Lua API location.
Based on some other posts, blogs, and documentation (and hearing that the if directive is frowned upon), I tried using a limit_except directive, but then the nginx server crashed upon startup as it seems the content_by_lua directive was not designed for limit_except blocks. Here was my attempt:
http {
upstream backend {
server localhost:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_set_header HTTP_CLIENT_IP $remote_addr;
proxy_redirect off;
}
location /api {
# Default the non-get API requests back to the backend server
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_set_header HTTP_CLIENT_IP $remote_addr;
proxy_redirect off;
# Select requests that *aren't* a PUT, POST, or DELETE, and pass those to the Lapis REST API
limit_except PUT POST DELETE {
content_by_lua '
require("lapis").serve("app")
';
}
}
}
}
which promptly crashed with
nginx: [emerg] "content_by_lua" directive is not allowed here in nginx.conf:46
What is the best way to selectively route in nginx based on both URL prefix and HTTP verb when delegating to the content_by_lua directive?
I implemented conditional routing of particular URL GET actions by using the if directive, even though it's evil according to the nginx devs. It does seem like this might one of the few desirable use cases for the if directive, but if it's not or someone has a better approach, please let me know (which is why I haven't accepted my own answer).
Here is the nginx configuration file that currently implements the solution:
http {
upstream backend {
server localhost:3000;
}
server {
listen 80;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_set_header HTTP_CLIENT_IP $remote_addr;
proxy_redirect off;
# By default pass all the requests to the Rails app backend
location / {
proxy_pass http://backend;
}
# Delegate certain API calls to our special OpenResty Endpoints
location /api {
# Makes sure all POSTs, PUTs, and DELETE actions still get handed off to the backend
if ($request_method ~ POST|PUT|DELETE) {
proxy_pass http://backend;
}
# All that should remain are the GET and OPTIONS endpoints so send them to the OpenResty Lua backend!)
content_by_lua 'require("lapis").serve("app")';
}
}
}

Resources