Is it possible in nginx while setting up reverse proxy to have server parameter as variable based on browser cookie in upstream module? - nginx

I am setting up nginx reverse proxy for routing/load distribution using upstream and server modules where same URL should proxy to 5 different servers running Apache, without changing the URL in the browser, depending on the value of the cookie. How do I accomplish server parameter in upstream to pass a variable based on cookie value?
I have tried mapping cookie value to server that I need to proxy to and then have used the server ip as a variable in "server_name" parameter. I am trying to use the same in "server" parameter in upstream, however nginx does not support that. I do not want to put all 5 servers in upstream as then nginx is trying to load balance across them, that is not the goal here. The goal is to distribute load to specific servers based on cookie value.
nginx.conf file
http {
include /etc/nginx/conf.d/*.conf;
upstream su.la.com {
server 100.0.8.2:443;
}
}
#The above works, making this $suhost, does not work.Any alternative
for this will be helpful.
suhosts.conf file under /etc/nginx/conf.d
map $cookie_su_ID $suhost {
default 100.0.8.2;
1 100.0.8.2;
3 100.0.8.6;
}
server {
set $bypass 0;
if ($remote_addr ~ "^(127.0.0.1)$") {
set $bypass $http_secret_header;
}
listen 80;
server_name su.la.com;
return 301 https://$host$request_uri;
}
server {
set $bypass 0;
if ($remote_addr ~ "^(127.0.0.1)$") {
set $bypass $http_secret_header;
}
listen 443 ssl;
server_name $suhost;
ssl_certificate /etc/nginx/certs/client.crt;
ssl_certificate_key /etc/nginx/certs/client.private.key;
location / {
proxy_pass https://su.la.com;
proxy_cache_bypass $bypass;
}
}
Expected results:
Based on map function described above, if su_id=1, the server parameter in upstream $suhost, should be picked up as 100.0.8.2 and https://su.la.com should route to 100.0.8.2, with no change in browser.
Actual Results:
nginx does not allow variable for server parameter, so need other alternatives or approach to make this happen. If server parameter is given a value for a single server, this works. We need to make it work for routing to multiple servers, single server at a time, based on cookie value.

Related

Use nginx upstreams module with a mix of SSL and non-SSL, with and without path

I want to create a reverse proxy which has 2 upstreams: a primary and a fallback one.
However the primary upstream talks http while the fallback upstream talks https.
Also, the fallback one has an api key as path component.
In theory I want something like this:
upstream backends {
server primary.mydomain.dev;
server fallback.notmydomain.com/v1/12345 ssl fallback;
}
server {
listen 80;
server_name proxy.example.com;
location / {
proxy_pass http://backends;
...
}
}
}
However my understanding is that I can't have URL paths in the upstreams module.
I could add the path to the proxy_pass directive, but then it would be applied regardless of the chosen upstream server.
Is what I want even possible with nginx?
If not, would it be possible with haproxy? How?

How to redirect all https domain from www to non www using nginx config file nginx.conf?

I want to redirect all domain from www to non-www using Nginx config file nginx.conf.
I have tried using the below configuration but it only work for URL start with HTTP but does not work for HTTPS
I have added below server block
server {
server_name "~^(?!www\.).*" ;
return 301 $scheme://$1$request_uri ;
}
Since you didn't specify listening port in the server block you've shown in your question, it will listen on a plain HTTP TCP port 80 by default. You need to specify
listen 443 ssl;
to listen on an HTTPS TCP port 443. However to make the server block workable via the HTTPS protocol, you'd need to specify an SSL certificate/key (at least), and to made a user browser following a redirect returned by nginx, that certificate should be a valid one, issued for the domain name you want to be redirected, or the browser will complain about invalid certificate and won't follow the redirect location.
So if you want to use some kind of universal server block for redirecting every HTTPS request from www to non-www domain, it will be impossible unless you have a certificate that include every domain name you want do redirect (which seems to be impossible to have for a custom non-predefined list of domain names).
Update
Although this isn't a thing I'd do for myself in a production environment, actually there is a way to achieve workable solution using the lua-resty-auto-ssl (see the documentation examples), OpenResty/lua-nginx-module and the following sever block (remember that server names specified by domain prefix have the lowest priority comparing to exact matched server names, e.g. www.example.com, or server names specified by domain suffix, e.g. *.example.com):
init_by_lua_block {
auto_ssl = (require "resty.auto-ssl").new()
auto_ssl:set("allow_domain", function(domain)
return true
end)
auto_ssl:init()
}
map $host $basename {
~^www\.(.+) $1;
default $host;
}
server {
listen 443 ssl;
server_name www.*;
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate()
}
ssl_certificate /path/to/dummy.crt;
ssl_certificate_key /path/to/dummy.key;
return 301 https://$basename$request_uri;
}
In order for this to work you'd also need the corresponding plain HTTP block to allow ACME challenge(s) to be successfully completed:
server {
listen 80;
server_name www.*;
location / {
return 301 https://$basename$request_uri;
}
location /.well-known/acme-challenge/ {
content_by_lua_block {
auto_ssl:challenge_server()
}
}
}

Nginx proxy_next_upstream with different URI modification

We have a need to set up multiple up-stream server, and use proxy_next_upstream to a backup, if the main server returns 404. However, the URI for up-stream backup server is different than the one towards main server, so I don't know whether this can be possible.
In detail, below config snippet works fine (if URIs are the same to all up-stream servers):
upstream upstream-proj-a {
server server1.test.com;
server server2.test.com backup;
}
server {
listen 80;
listen [::]:80;
server_name www.test.com;
location /proj/proj-a {
proxy_next_upstream error timeout http_404;
proxy_pass http://upstream-proj-a/lib/proj/proj-a;
}
For a request of http://test.com/proj/proj-a/file, it will first try to request http://server1.test.com/lib/proj/proj-a/file, if return 404 or timeout, then try http://server2.test.com/lib/proj/proj-a/file. This is good.
However, now for server2, it can only accept URL like http://server2.test.com/lib/proj/proj-a-internal/file, which is different than the URI towards the main server. If only considering the backup server, I can write like below:
proxy_pass http://server2.test.com/lib/proj/proj-a-internal
However looks like I can not have different proxy_pass for different upstream server combining proxy_next_upstream.
How can I achieve this?
I found a work-around using simple proxy_pass, and set local host as the backup upstream server, then do rewrite on behalf of the real backup upstream server.
The config is like below:
upstream upstream-proj-a {
server server1.test.com:9991;
# Use localhost as backup
server localhost backup;
}
server {
listen 80;
listen [::]:80;
resolver 127.0.1.1;
server_name www.test.com;
location /lib/proj/proj-a {
# Do rewrite then proxy_pass to real upstream server
rewrite /lib/proj/proj-a/(.*) /lib/proj/proj-a-internal/$1 break;
proxy_pass http://server2.test.com:9992;
}
location /proj/proj-a {
proxy_next_upstream error timeout http_404;
proxy_pass http://upstream-proj-a/lib/proj/proj-a;
}
}
It works fine, but the only side-effect is that, when a request needs to go to the backup server, it creates another new HTTP request from localhost to localhost which seems to double the load to nginx. The goal is to transfer quite big files, and I am not sure if this impacts performance or not, especially if all the protocols are https instead of http.

Configure Nginx to reverse proxy requests to backend server based on port number

I have have four web applications running in one ec2-instance with hostname "ip-10-176-225-83.us-west-2.compute.internal" on the ports 8888, 8088, 8042 and 8890. All those web application are on HTTP.
Our security team doesn't allow to open http port from onpremise to AWS. The suggest to setup a reverse proxy in same VPC subent which takes HTTPS requests and forward the same to back end webservers using HTTP.
I have created a new instance in same subnet with hostname "ip-10-176-225-84.us-west-2.compute.internal" and installed Niginx Server.
How can i configure Nginx so that it does as below
https://ip-10-176-225-84.us-west-2.compute.internal:8080 call http://ip-10-176-225-83.us-west-2.compute.internal:8080 and responds back
same for other ports
TL;DR: There are several methods of accomplishing what you're looking for here.
Adhering to the principle of reducing attack surface where possible, it's usually best to not expose ports to the public internet unless absolutely necessary. A reverse proxy is an excellent way to accomplish this. Generally, you'd want all of the backend web apps to be reverse proxied over HTTPS on port 443, and you would access each service either:
with different host names, such as app1.example.com and app2.example.com, or
with different subdirectories, such as example.com/app1 and example.com/app2
Depending on the method you choose, the configuration may vary significantly, either with multiple server blocks for the former scenario, or location blocks for the latter. In either case, we'll be making use of the upstream and proxy_pass directives. I'll give an example of both scenarios.
Multiple Hosts
If you're using multiple hostnames (or multiple ports) for the frontend, you'll need to create multiple server blocks for them:
# Nginx reverse-proxy configuration
upstream app1 {
server 10.176.225.83:8888;
}
upstream app2 {
server 10.176.225.83:8088;
}
upstream app3 {
server 10.176.225.83:8042;
}
upstream app4 {
server 10.176.225.83:8890;
}
server {
listen 443 ssl;
server_name app1.example.com;
ssl_certificate_key /path/to/your/ssl-key.pem;
ssl_certificate /path/to/your/ssl-cert.pem;
location / {
proxy_pass http://app1;
}
}
server {
listen 443 ssl;
server_name app2.example.com;
ssl_certificate_key /path/to/your/ssl-key.pem;
ssl_certificate /path/to/your/ssl-cert.pem;
location / {
proxy_pass http://app2;
}
}
server {
listen 443 ssl;
server_name app3.example.com;
ssl_certificate_key /path/to/your/ssl-key.pem;
ssl_certificate /path/to/your/ssl-cert.pem;
location / {
proxy_pass http://app3;
}
}
server {
listen 443 ssl;
server_name app4.example.com;
ssl_certificate_key /path/to/your/ssl-key.pem;
ssl_certificate /path/to/your/ssl-cert.pem;
location / {
proxy_pass http://app4;
}
}
Several things to note here:
All of the backends have been defined at the top, using the upstream directive, and can now be referenced by name.
If you don't want to do it this way, you can omit the upstream blocks, and the proxy_pass lines would look like this: proxy_pass http://10.176.225.83:8888;
Because each app is being served on a different hostname, each gets its own server block. This would also be the case if they were each being served on different ports.
The ssl_certificate and ssl_certificate_key in each server block can point to different certificates, or even the same certificate, if you defined multiple SANs in the certificate.
Each app will be available from its own hostname, which all point to the frontend server:
App 1: https://app1.example.com
App 2: https://app2.example.com
App 3: https://app3.example.com
App 4: https://app4.example.com
Multiple Directories
For a configuration using a single host and port, serving the backend apps from subdirectories, you'll be using a single server block with multiple location blocks:
# Nginx reverse-proxy configuration
upstream app1 {
server 10.176.225.83:8888;
}
upstream app2 {
server 10.176.225.83:8088;
}
upstream app3 {
server 10.176.225.83:8042;
}
upstream app4 {
server 10.176.225.83:8890;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate_key /path/to/your/ssl-key.pem;
ssl_certificate /path/to/your/ssl-cert.pem;
location /app1 {
proxy_pass http://app1;
}
location /app2 {
proxy_pass http://app2;
}
location /app3 {
proxy_pass http://app3;
}
location /app4 {
proxy_pass http://app4;
}
}
A few more things to note with this one:
We still define all four upstream services, as before. And you can still omit them if you prefer the direct route. This directive is more useful in complex configurations.
Because all the apps are being served from the same hostname, they share a server block, but have separate location blocks for their respective subdirectories.
Since they all share the same hostname, multiple SANs aren't necessary for the server certificate in this case.
Each backend app will be available from a subdirectory on the frontend server:
App 1: https://example.com/app1
App 2: https://example.com/app2
App 3: https://example.com/app3
App 4: https://example.com/app4
TLS Configuration
In either scenario, you'll need to configure SSL certificates, either signed by a public CA, or your internal PKI, if applicable. If your apps are to be public facing, you will need to use public certificates. Then you'd add their location to the above Nginx config.
Further Reading
For a real world example, you can check out the Nginx configuration I'm using for a Genieacs TR-069 server, which implements SSL and reverse proxies several other services, albeit on their original ports, not to 443. This may be useful if you wanted to keep them on their original ports.
The Nginx site also has a decent primer on reverse proxy basic configuration.

nginx reverse proxy, single domain, multiple subdirectories

Having trouble figuring this out.
I have nginx running on home.domain.com.
Within my home network, I have multiple web services that I'd like to access externally through a reverse proxy. No SSL for now, but I'll add that later. I need to set up a reverse proxy on subdirectories of home.domain.com/app{1-3} as I only have a valid cert for home.domain.com.
My current configuration:
server {
listen 80 default;
keepalive_timeout 120;
server_name home.domain.com;
location /app1/ {
proxy_pass http://internalIP1:8081/;
}
location /app2/ {
proxy_pass http://internalIP2:5000/;
}
}
When I try to access home.domain.com/app1, it should redirect to home.domain.com/app1/login/?next=%2F, but instead goes to home.domain.com/login/?next=%2F. This obviously throws a 404, as it's not available on the nginx server.
Thoughts? Suggestions?

Resources