Correct proxy configuration for nginx server to access rest api - nginx

I have a nginx-server configured as follows:
server {
listen 3000;
listen [::]:3000;
server_name .+;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
...
index index.html;
root /usr/share/nginx/html;
location /cam/ {
proxy_pass http://cam:8000/;
}
location /api {
proxy_pass https://some_ip:some_port;
proxy_pass_request_headers on;
proxy_set_header X-API-KEY xxxxx;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
proxy_ssl_server_name on;
}
location /share/ {
alias /usr/src/share;
}
location / {
try_files $uri $uri/ /index.html;
}
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
client_max_body_size 1M;
keepalive_timeout 10;
}
For displaying my frontend, this configuration works fine. The proxy for the cam is also working correctly.
I am getting problems in the configuration of the proxy for the api. The api has a sanity-check under "https://some_ip:some_port", which just returns "200: ok". Otherwise I should be able to make request using a fetch request such as
const res = await fetch(`api/subaddress/${system_id.toString()}/base`, {
method: 'POST',
headers: {
'Content-type': 'application/json',
'X-API-KEY': xxxxx,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE',
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Auth-Token'
},
body: JSON.stringify(data_body),
});
Which, if successfull, returns "202: ok".
If I make such a request using curl, it works just fine (both of the requests), so the api should not be the problem.
At the current configuration, I get a "200: ok", which tells me that I have connected to the api, but the proxy does not take in account my subaddresses.
Besides the given configuration, I tried the following:
location /api {
proxy_pass https://some_ip:some_port$request_uri;
...
}
which always gives me "502: bad gateway"
location /api/ {
proxy_pass https://some_ip:some_port/;
...
}
which always gives me "403: forbidden"
location /api {
rewrite ^/api/(.*) /$1 break;
proxy_pass https://some_ip:some_port/$1;
...
}
which causes nginx to crash, as it tells me that it cannot resolve it. I would like to avoid using a resolver, unless definitly necessary (I have tried it using a resolver before too and it did not work either).
What is the correct way to accomplish this?

I solved this. Apparently the 403: forbidden was caused my some of the configurations of the proxy. After removing those, I did not get that error anymore.
In the end I used
location /api {
rewrite ^/api/(.*) /$1 break;
proxy_pass https://some_ip:some_port/$1;
...
}
which I made work by putting
resolver 1.1.1.1 valid=30s;
at the top of the configuration file.

Related

Nginx how to set proxy pass using map based on the host and location

Currently I setting proxy pass dynamicaly using map directive. Now I would like for a specifc location /dcs/ I would like to set a different proxy pass rather than one mentioned through map.conf and proxy_pass.map. Is there a way to say use different map for a specific location /dcs/.
Should just use If stantement like if host is dev-api.mysite.com then set proxypass to http://abcdef.com. Is this the only option.
MySite.conf
server {
listen 80;
server_name ~^(api|dev-api|staging-api)\.mysite\.com$;
location / {
proxy_set_header Cookie "";
add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;
proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '*' always;
include /etc/nginx/conf.d/headers.inc;
}
location /dcs/ {
set $proxy_host $host;
set $proxy_pass Set-different-host based of dev,staging,prod;
proxy_set_header Cookie "";
add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;
proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '*' always;
include /etc/nginx/conf.d/headers.inc;
}
}
maps.conf
map $host $proxy_pass {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
proxy_pass.map
# mysite.com
api.mysite.com myst.plat:8080;
staging-api.mysite.com staging.myst.plat:8081;
dev-api.mysite.com dev.plat:8080;
headers.inc
proxy_pass $upstream_proto://$proxy_pass;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-Proto $real_scheme;
proxy_set_header X-Forwarded-Scheme $real_scheme;
proxy_set_header Host $proxy_host;
proxy_ssl_session_reuse on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-GeoIP-Country-Code $geoip_country_code;
proxy_set_header X-Site-Id $site_id;
add_header X-Cache-Time $date_gmt;
add_header X-Cache-Date $upstream_http_date;
add_header X-Proxy-Cache $upstream_cache_status;
# add_header X-Site-Id $site_id;
You can use several chained map blocks:
map $host $proxy_by_host {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
map $uri $proxy_pass {
~^/dcs/ abcdef.com;
default $proxy_by_host;
}
Update
Looks like I slightly misunderstood your question, to use two different map blocks depending on request URI, use something like
map $host $proxy_by_host {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
map $host $dcs_proxy_by_host {
hostnames;
include /etc/nginx/conf.d/dcs_proxy_pass.map;
}
map $uri $proxy_pass {
~^/dcs/ $dcs_proxy_by_host;
default $proxy_by_host;
}
This way if all the other parameters for your locations are equal, you can use only one location / { ... } for all requests.

Nginx Config for Camunda Webapp

I am configuring camunda webapp with nginx reverse proxy. Applications is loading perfectly fine using location / (Root Path). But I need to access it using a readable location path e.g. /process/. I tried a lot with several rewrites and redirections but to no success.
At root path all the static content loads but with other location path it fails. I'm a newbie in Nginx So it would make complete sense if i'm missing something very trivial.
Here is my config with location as Root Path:
server {
listen 8080;
server_name abc.xyz.net;
rewrite_log on;
error_log /var/log/nginx/localhost.error_log notice;
location / {
# Simple requests
if ($request_method ~* "(GET|POST)") {
add_header "Access-Control-Allow-Origin" *;
}
# Preflighted requests
if ($request_method = OPTIONS ) {
add_header "Access-Control-Allow-Origin" *;
add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD";
add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
return 200;
}
proxy_pass http://camunda-webapp.xyz.net;
proxy_set_header X-Forwarded-Host $host/;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass_request_headers on;
proxy_read_timeout 180s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
On another context, If I try to give full URL in proxy_pass(Refer below config) then it works but the browser URL gets completely changed to proxy_pass URL.(I had even tried with proxy_redirect to retain the original URL but it doesn't work)
server {
listen 8080;
server_name abc.xyz.net;
rewrite_log on;
error_log /var/log/nginx/localhost.error_log notice;
location /process/ {
rewrite ^\/(?>[process]+)(\/.*) $1 break;
# Simple requests
if ($request_method ~* "(GET|POST)") {
add_header "Access-Control-Allow-Origin" *;
}
# Preflighted requests
if ($request_method = OPTIONS ) {
add_header "Access-Control-Allow-Origin" *;
add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD";
add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
return 200;
}
proxy_pass http://camunda-webapp.xyz.net/app/welcome/default;
proxy_redirect http://camunda-webapp.xyz.net/app/welcome/default https://abc.xyz.net/process
proxy_set_header X-Forwarded-Host $host/process;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass_request_headers on;
proxy_read_timeout 180s;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Any kind of info or help will be appreciated. Thanks in Advance.
Bella Ciao!
I think the trick is to specify the same location context of the nginx as the context path of the your application.
Also, please remove url rewrite as its not needed here.

How to check substring using regular expression in Nginx config?

I try to check some parameters in request. Here is my url:
http://localhost:8080/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=004C0000064F&
STYLES=&WIDTH=256&HEIGHT=256&FORMAT=image%2fjpeg&CRS=EPSG%3a100000&DPI=96&
MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi%3a96&
BBOX=1530569.52624839870259166%2c524135.21126760687911883%2c1531064.27656850102357566%2c524629.96158770937472582
I trying to get REQUEST parameter. Here is my nginx 1.12.1 config:
server {
listen 8080;
server_name 127.0.0.1 localhost;
set $site_backend localhost:56297;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location /favicon.ico {
error_page 403 404 = #tomcat_static_mapping;
}
location ~* /wms {
internal;
add_header URI $request_uri;
add_header X-debug-message1 "$request_uri" always;
if ($request_uri ~* REQUEST=([^&]*)) {
add_header X-debug-message2 "hi" always;
set $requesttype $1;
}
}
}
And in browser i got header:
X-debug-message1: /wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=004C0000064F&STYLES=&WIDTH=256&HEIGHT=256&FORMAT=image%2fjpeg&CRS=EPSG%3a100000&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi%3a96&BBOX=1530569.52624839870259166%2c524135.21126760687911883%2c1531064.27656850102357566%2c524629.96158770937472582
But not get X-debug-message2 header. I check regular expression here https://rubular.com/ and it's found match and return GetMap as like i want.
What can be wrong here?
Something is not complete / matching in your post. I got X-debug-message2: hi only which does match to how nginx has to behave:
These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level
For more intuitive outcome, use Headers-More module.
more_set_headers "URI: $request_uri";
more_set_headers 'X-debug-message1: "$request_uri"';
location ~* /wms {
if ($request_uri ~* REQUEST=([^&]*)) {
more_set_headers 'X-debug-message2: hi';
set $requesttype $1;
}
}

Nginx API proxy form-data and custom header not passed to remote API

I want to setup an Nginx with this purpose.
Context
Serve Angular 2 dist static files from root url: http://example.com -> display the index.html at /home/www/index.html
In my Angular2 project, I make some calls locally to /api/some-endpoints both in GET, POST.
I want the calls to /api/{ANY-PATH} be redirected as is to a remote private url. I need the call be made with my custom headers (for example form-data or apikey header) AND add another apikey to the request to distant server.
My problem
When making a POST call (from machine where nginx is installed) to /api/document, with form-data values (containing an uploaded file) and my custom header: apikey , 253018b8425f4eb08291a1b68c4bc328
The distant server doesn't seem to receive nor the form-data nor the apikey.
My config
upstream api-server {
server myprivate-api-server.com:8000;
}
map $http_apikey $api_route {
default "error";
253018b8425f4eb08291a1b68c4bc328 'api-server';
}
server {
listen 80;
index index.html;
server_name example.com;
location = /501_apikey.html {
root /home/www/error_pages;
internal;
}
location ~ /api/(?<path>.*) {
if ($request_method = OPTIONS ) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'apikey';
return 200;
}
if ($api_route = "error"){return 501;}
set $clientapikey "hW4g5V60UT2O3iQP1PS2g29hKjQ403E5";
error_page 501 /501_apikey.html;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT';
add_header 'Access-Control-Allow-Headers' 'apikey';
add_header 'clientapikey' $clientapikey;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://$api_route/$path$is_args$args;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $remote_addr;
proxy_ssl_session_reuse off;
proxy_set_header Host $http_host;
add_header 'Access-Control-Allow-Headers' 'apikey';
proxy_redirect off;
}
}
Bonus question
Is my way of adding the client second key valid?
add_header 'clientapikey' $clientapikey;
I will need in the future to create a location for each of my client, each with different clientapikey to identify them on the remote API.

Nginx + HapiJS CORS request (not GET)

I have a node server with a hapiJS API, and am attempting to send it requests from a separate Node server.
Everything works fine in development, but not on the servers, and the issue is basic auth is enabled on the servers.
Specifically, the requests work fine if they are GET requests, but all other requests fail. I suspect this is due to the OPTIONS pre-request flight check failing, which isn't sent on GET requests from my limited understanding.
The exact error message I get is:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://site.example.com' is therefore not allowed access. The response had HTTP status code 401.
My nginx config:
server {
listen 80;
server_name https://api.example.com;
return 301 https://$server_name$request_uri; ## Permanently redirect all http traffic to https
}
server {
#SSL INIT
listen 443;
ssl on;
ssl_certificate /var/vcap/jobs/nginx/config/site.bundle.crt;
ssl_certificate_key /var/vcap/jobs/nginx/config/site.key;
server_name https://api.example.com;
location / {
auth_basic "Restricted Content";
auth_basic_user_file .htpasswd;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass_request_headers on;
proxy_pass https://api.example.com;
}
}
And my CORS settings for HapiJS (I excluded the irrelevant stuff).
connections:
routes:
cors:
origin: ["https://api.example.com"]
credentials: true
I have tried the https://www.npmjs.com/package/hapi-cors-headers hapi-cors-headers plugin, to no avail (instead of the above settings.) I tried enabling every single CORS thing on nginx I could think of (most of them gotten from http://enable-cors.org/server_nginx.html)
One of two things happens, no matter how I've adjusted the configuration - and I've tried a LOT of things:
1) It continues to give the above message no matter what
2) It complains about the heading being there TWICE (if I put it in both nginx and hapijs at the same time).
In no situation does it work (except GET requests).
An example of a POST ajax call to the API that I'm using (used with Kendo):
$.ajax({
url: api_address + '/vendors',
method: 'POST',
contentType: 'application/json',
processData: false,
data: JSON.stringify(this.vendor),
xhrFields: {
withCredentials: true
}
success: function(data) {
vm.set('vendor', data);
notification.show('Vendor created successfully', 'success');
vm.set('has_changes', false);
},
error: function() {
notification.show('Error creating vendor', 'error');
}
});
the api_address mentioned above is:
https://username:password#api.example.com
Why is this working for GET requests but not POST/PUT/etc?
Change your nginx to handle the CORS and don't have hapi worry about it. Taken and modified from http://enable-cors.org/server_nginx.html
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
if ($request_method = 'OPTIONS') {
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
auth_basic "Restricted Content";
auth_basic_user_file .htpasswd;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass_request_headers on;
proxy_pass https://api.example.com;
}
Of course you would need more work if you are supporting PUT or DELETE.

Resources