Nginx microservices configuration - nginx

I have many frontend microservices and I wanted to have a single entry in nginx that would solve them all.
Example of microservices I have:
budget-client
stock-client
To differentiate, each service is forwarded to a service following the following logic:
localhost/budget-client/foo/bar -> forward the request to the budge-client service
localhost/stock-client/foo/bar -> forward the request to the stock-client service
Currently my settings are like this:
location /stock-client {
resolver kube-dns.kube-system.svc.cluster.local valid=10s;
set $upstreamName stock-client;
rewrite ^/stock-client(.*) /stock-client/$1 break;
proxy_pass http://$upstreamName-frontend.cluster-dev.svc.cluster.local;
proxy_set_header Host $host;
}
location /budget-client {
resolver kube-dns.kube-system.svc.cluster.local valid=10s;
set $upstreamName budget-client;
rewrite ^/budget-client(.*) /budget-client/$1 break;
proxy_pass http://$upstreamName-frontend.cluster-dev.svc.cluster.local;
proxy_set_header Host $host;
}
But that way whenever a new service is born I have to adjust this nginx to deploy a new version of it. What I would like is to leave the generic entries to the point of understanding themselves.
I'll give an example below how I would like it to look:
location / {
resolver kube-dns.kube-system.svc.cluster.local valid=10s;
set $upstreamName $request_uri; # replace here to get the first parameter from path
rewrite ^/$upstreamName(.*) /$upstreamName/$1 break;
proxy_pass http://$upstreamName-frontend.cluster-dev.svc.cluster.local;
proxy_set_header Host $host;
}
I don't know if it's possible to do it that way, or in some other way that preserves the same idea, that with the same nginx rule it can redirect to any service by looking only at the first parameter of the path (which is the name of my service) , because I have a pattern of well-defined services and paths.

Related

Nginx domain resolving issue

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;
...
}

NGINX reverse proxy with Rewrites to replace TMG2010

I curently have a Forefront TMG2010 doing reverse proxy to several internal web servers. Since it was really easy in TMG I have done lots of host and virtual directory mappings/rewrites.
Now I ened to acheive the same in NGINX.
e.g.,
apphost.domain.com/ needs to map internally to server1/apppath/
apphost.domain.com/test/ needs to map internally to server2/apppath/
apphost.domain.com/dev/ needs to map internally to server3/apppath/
in each case the internal /apppath/ needs to be invisible to the client. This is to achieve the same as the external to internal path mapping in TMG.
What I have done so far is;
server {
listen 80;
server_name host.server.com;
location /test/ {
proxy_set_header Host $host;
rewrite ^(.*)$ /app/$1 break;
proxy_pass http://10.0.0.2;
}
location / {
proxy_set_header Host $host;
rewrite ^(.*)$ /app/$1 break;
proxy_pass http://10.0.0.1;
}
But only the / seems to work, the /test/ doesnt.
Help!
So you want to map /test/foo to http://10.0.0.2/app/foo. The simplest solution is:
location /test/ {
proxy_set_header Host $host;
proxy_pass http://10.0.0.2/app/;
}
According to the documentation the /test/ element will be replaced by /app/ as it passes upstream.

I need to do a redirect on a proxied path in Nginx

I'm working with a strangely architected app and I have a need to proxy / and redirect it to a sub-directory (unless someone can comeup with a better solution). Here's the layout of the paths the app uses:
var securePaths = [
"/someaction",
"/anotheraction",
"/athirdaction",
"/client/interfaceishere/index.html" //path to client interface
];
As you can see the place where the client interfaces with the app is two levels in so I need a redirect to there. I can't proxy the app there because it needs access to top level paths like "/someaction". I have nginx configured to the proxy the app like this:
upstream socket_nodes {
ip_hash;
server 127.0.0.1:8080;
}
server {
server_name myapp.example.com;
listen 80;
location / {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;
}
}
What's the best way to get the user down to "/client/interfaceishere/index.html"?
The previous users of this app solved this by iframing which is not an option for me.
Then you need to split it up, the paths inside location blocks instead of passing everything with /. You really first need to think and write down where each path needs to go and then either use 'map' to redirect or/and location blocks to handle things. You can use location blocks to handle each uri separately.

Nginx proxy_pass with $remote_addr

I'm trying to include $remote_addr or $http_remote_addr on my proxy_pass without success.
The rewrite rule works
location ^~ /freegeoip/ {
rewrite ^ http://freegeoip.net/json/$remote_addr last;
}
The proxy_pass without the $remote_addr works, but freegeoip does not read the x-Real-IP
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
Then, I'm adding the ip to the end of the request, like this:
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/$remote_addr;
}
but nginx report this error: no resolver defined to resolve freegeoip.net
If the proxy_pass statement has no variables in it, then it will use the "gethostbyaddr" system call during start-up or reload and will cache that value permanently.
if there are any variables, such as using either of the following:
set $originaddr http://origin.example.com;
proxy_pass $originaddr;
# or even
proxy_pass http://origin.example.com$request_uri;
Then nginx will use a built-in resolver, and the "resolver" directive must be present. "resolver" is probably a misnomer; think of it as "what DNS server will the built-in resolver use". Since nginx 1.1.9 the built-in resolver will honour DNS TTL values. Before then it used a fixed value of 5 minutes.
It seems a bit strange that nginx is failing to resolve the domain name at runtime rather than at configuration time (since the domain name is hard coded). Adding a resolver declaration to the location block usually fixes dns issues experienced at runtime. So your location block might look like:
location ^~ /freegeoip/ {
#use google as dns
resolver 8.8.8.8;
proxy_pass http://freegeoip.net/json/$remote_addr;
}
This solution is based on an article I read a while back - Proxy pass and resolver. Would be worth a read.
If anyone is stll experiencing trouble, for me it helped to move the proxy_pass host to a seperate upstream, so I come up with something like this
upstream backend-server {
server backend.service.consul;
}
server {
listen 80;
server_name frontend.test.me;
location ~/api(.*)$ {
proxy_pass http://backend-server$1;
}
location / {
# this works mystically! backend doesn't...
proxy_pass http://frontend.service.consul/;
}
}
Another way is that you can provide your host ip address and port number instead of host URL in proxy_pass like below:
Your Code:
proxy_pass http://freegeoip.net/json/;
Updated Code
proxy_pass http://10.45.45.10:2290/json/;
So you want this to work:
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/$remote_addr;
}
but as pointed out by Chris Cogdon, it fails because at run-time, nginx does not have the domain name resolved, and so proxy_pass requires a resolver (see also, proxy_pass docs).
Something that seems to work (though may be a hack!) is to do something that triggers nginx to resolve the domain name at start-up and cache this.
So, change the config to include a location (for the same host) without any variables (as well as your location with variables):
location = /freegeoip/this-location-is-just-a-hack {
# A dummy location. Use any path, but keep the hostname.
proxy_pass http://freegeoip.net/this-path-need-not-exist;
}
location ^~ /freegeoip/ {
proxy_pass http://freegeoip.net/json/$remote_addr;
}
The host will now be resolved at start-up (and its cached value will be used in the second location at run-time). You can verify this start-up resolution behavior by changing hostname (freegeoip.net) to something that doesn't exist, and when you run nginx -t or nginx -s reload you'll have an emergency error ([emerg] host not found in upstream). And of course you can verify that the cached value is used at run-time by leaving out any resolver, hitting your location block, and observing no errors in the logs.
You could also mention your nginx server port in the proxy_pass uri. This solved the issue for me.
Try to use dig / nslookup to make sure this is still nginx issue.
In my case the issue was that one of dns server between my nginx and root dns was not respecting TTL values that are as small as 1 second.
Nginx does not re-resolve DNS names in Docker

Change Host header in nginx reverse proxy

I am running nginx as reverse proxy for the site example.com to loadbalance a ruby application running in backend server. I have the following proxy_set_header field in nginx which will pass host headers to backend ruby. This is required by ruby app to identify the subdomain names.
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $http_host;
}
Now I want to create an alias beta.example.com, but the host header passed to backend should still be www.example.com otherwise the ruby application will reject the requests. So I want something similar to below inside location directive.
if ($http_host = "beta.example.com") {
proxy_pass http://rubyapp.com;
proxy_set_header Host www.example.com;
}
What is the best way to do this?
You cannot use proxy_pass in if block, so I suggest to do something like this before setting proxy header:
set $my_host $http_host;
if ($http_host = "beta.example.com") {
set $my_host "www.example.com";
}
And now you can just use proxy_pass and proxy_set_header without if block:
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $my_host;
}
map is better than set + if.
map $http_host $served_host {
default $http_host;
beta.example.com www.example.com;
}
server {
[...]
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $served_host;
}
}
I was trying to solve the same situation, but with uwsgi_pass.
After some research, I figured out that, in this scenario, it's required to:
uwsgi_param HTTP_HOST $my_host;
Hope it helps someone else.
Just a small tip. Sometimes you may need to use X-Forwarded-Host instead of Host header. That was my case where Host header worked but only for standard HTTP port 80. If the app was exposed on non-standard port, then this port was lost when the app generated redirects. So finally what worked for me was:
proxy_set_header X-Forwarded-Host $http_host;

Resources