haproxy rewrite uri keep backend header host - http

By making a rewrite in Haproxy 1.8, I need to make a URI redirect to another domain (host), but keep header host in request.
Example:
www.mysite.com/api -> 104.4.4.4/api (rw) -> result www.mysite.com/api (response)
I made a lot of tests with some parameters of HA, and I managed to obtain some succes, but with one problem.
This is my actual scnenario
backend site1
acl path_to_rw url_beg /api
acl mysite hdr(host) -i www.mymainsite.com
http-request set-header Host www.mymainsite.com if mysite path_to_rw
reqirep ^Host Host:\ host_to_forward/api if mysite path_to_rw
cookie SERVERID insert indirect nocache maxlife 1h
server site1 myhost:80 check cookie site1
My backend is a IIS server, and my rewrite works. But, I get error bellow:
"HTTP Error 400. The request hostname is invalid"
It seems that my backend does not accept the headerhost tha i send. Have somebody already had this problem ?

I managed to fix this problem, with a simple combination between acl´s and "use backend" directive.
e.g:
Header host:
www.mysite.com
Path to aplication in another origin
/api
acl myhost hdr(host) -i www.myhost.com
acl path_api url_reg -i /API(.*)
use_backend be_origin_servers if myhost path_api
backend be_origin_servers
server myserver1 10.10.10.10 check cookie myserver1

Related

Kong LoadBalancer over Kubesphere

I installed Kong (Kong proxy+kong ingress controller) over Kubernetes/Kubesphere cluster with Istio mesh inside, and I added annotations and ingress types needed, so am able to access only the Kong Proxy at node exposed IP and port, but am unable neither add rules nor access Admin GUI or do any kind of configuration, every request I do to my Kong end-point like
curl -i -X GET http://10.233.124.79:8000/rules
or any kind of request to the proxy, I get the same response of:
Content-Type: application/json; charset=utf-8 Connection: keep-alive
Content-Length: 48 X-Kong-Response-Latency: 0 Server: kong/2.2.0
{"message":"no Route matched with those values"}
Am not able to invoke Admin API, its pod-container is only listening to 127.0.0.1, my environment var's for kong-proxy pod
KONG_PROXY_LISTEN
0.0.0.0:8000, 0.0.0.0:8443 ssl http2
KONG_PORT_MAPS
80:8000, 443:8443
KONG_ADMIN_LISTEN
127.0.0.1:8444 ssl
KONG_STATUS_LISTEN
0.0.0.0:8100
KONG_DATABASE
off
KONG_NGINX_WORKER_PROCESSES
2
KONG_ADMIN_ACCESS_LOG
/dev/stdout
KONG_ADMIN_ERROR_LOG
/dev/stderr
KONG_PROXY_ERROR_LOG
/dev/stderr
And env. var's for ingress-controller:
CONTROLLER_KONG_ADMIN_URL
https://127.0.0.1:8444
CONTROLLER_KONG_ADMIN_TLS_SKIP_VERIFY
true
CONTROLLER_PUBLISH_SERVICE
kong/kong-proxy
So how to be able to expose Admin GUI over the mesh over the nodeport and how to able to invoke Admin API, to add rules, etc?
Yes, first you should add rules.
You can directly add routers in KubeSphere. See the documentation for more info.

Nginx will not start with host not found in upstream

I use nginx to proxy and hold persistent connections to far away servers for me.
I have configured about 15 blocks similar to this example:
upstream rinu-test {
server test.rinu.test:443;
keepalive 20;
}
server {
listen 80;
server_name test.rinu.test;
location / {
proxy_pass https://rinu-test;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
}
}
The problem is if the hostname can not be resolved in one or more of the upstream blocks, nginx will not (re)start. I can't use static IPs either, some of these hosts explicitly said not to do that because IPs will change. Every other solution I've seen to this error message says to get rid of upstream and do everything in the location block. That it not possible here because keepalive is only available under upstream.
I can temporarily afford to lose one server but not all 15.
Edit:
Turns out nginx is not suitable for this use case. An alternative backend (upstream) keepalive proxy should be used. A custom Node.js alternative is in my answer. So far I haven't found any other alternatives that actually work.
Earlier versions of nginx (before 1.1.4), which already powered a huge number of the most visited websites worldwide (and some still do even nowdays, if the server headers are to be believed), didn't even support keepalive on the upstream side, because there is very little benefit for doing so in the datacentre setting, unless you have a non-trivial latency between your various hosts; see https://serverfault.com/a/883019/110020 for some explanation.
Basically, unless you know you specifically need keepalive between your upstream and front-end, chances are it's only making your architecture less resilient and worse-off.
(Note that your current solution is also wrong because a change in the IP address will likewise go undetected, because you're doing hostname resolution at config reload only; so, even if nginx does start, it'll basically stop working once IP addresses of the upstream servers do change.)
Potential solutions, pick one:
The best solution would seem to just get rid of upstream keepalive as likely unnecessary in a datacentre environment, and use variables with proxy_pass for up-to-date DNS resolution for each request (nginx is still smart-enough to still do the caching of such resolutions)
Another option would be to get a paid version of nginx through a commercial subscription, which has a resolve parameter for the server directive within the upstream context.
Finally, another thing to try might be to use a set variable and/or a map to specify the servers within upstream; this is neither confirmed nor denied to have been implemented; e.g., it may or may not work.
Your scenario is very similar to the one when using aws ELB as uptreams in where is critical to resolve the proper IP of the defined domain.
The first thing you need to do and ensure is that the DNS servers you are using can resolve to your domains, then you could create your config like this:
resolver 10.0.0.2 valid=300s;
resolver_timeout 10s;
location /foo {
set $foo_backend_servers foo_backends.example.com;
proxy_pass http://$foo_backend_servers;
}
location /bar {
set $bar_backend_servers bar_backends.example.com;
proxy_pass http://$bar_backend_servers;
}
Notice the resolver 10.0.0.2 it should be IP of the DNS server that works and answer your queries, depending on your setup this could be a local cache service like unbound. and then just use resolve 127.0.0.1
Now, is very important to use a variable to specify the domain name, from the docs:
When you use a variable to specify the domain name in the proxy_pass directive, NGINX re‑resolves the domain name when its TTL expires.
You could check your resolver by using tools like dig for example:
$ dig +short stackoverflow.com
In case is a must to use keepalive in the upstreams, and if is not an option to use Nginx +, then you could give a try to openresty balancer, you will need to use/implement lua-resty-dns
A one possible solution is to involve a local DNS cache. It can be a local DNS server like Bind or Dnsmasq (with some crafty configuration, note that nginx can also use specified dns server in place of the system default), or just maintaining the cache in hosts file.
It seems that using hosts file with some scripting is quite straightforward way. The hosts file should be spitted into the static and dynamic parts (i.e. cat hosts.static hosts.dynamic > hosts), and the dynamic part should be generated (and updated) automatically by a script.
Perhaps it make sense to check from time to time the hostnames for changing IPs, and update hosts file and reload configuration in nginx on changes. In case of some hostname cannot be resolved the old IP or some default IP (like 127.0.1.9) should be used.
If you don't need the hostnames in the nginx config file (i.e., IPs are enough), the upstream section with IPs (resolved hostnames) can be generated by a script and included into nginx config — and no need to touch the hosts file in such case.
I put the resolve parameter on server and you need to set the Nginx Resolver in nginx.conf as below:
/etc/nginx/nginx.conf:
http {
resolver 192.168.0.2 ipv6=off valid=40s; # The DNS IP server
}
Site.conf:
upstream rinu-test {
server test.rinu.test:443;
keepalive 20;
}
My problem was container related. I'm using docker compose to create the nginx container, plus the app container. When setting network_mode: host in the app container config in docker-compose.yml, nginx was unable to find the upstream app container. Removing this fixed the problem.
we can resolve it temporarily
cd /etc
sudo vim resolv.conf
i
nameserver 8.8.8.8
:wq
then do sudo nginx -t
restart nginx it will work for the momment
An alternative is to write a new service that only does what I want. The following replaces nginx for proxying https connections using Node.js
const http = require('http');
const https = require('https');
const httpsKeepAliveAgent = new https.Agent({ keepAlive: true });
http.createServer(onRequest).listen(3000);
function onRequest(client_req, client_res) {
https.pipe(
protocol.request({
host: client_req.headers.host,
port: 443,
path: client_req.url,
method: client_req.method,
headers: client_req.headers,
agent: httpsKeepAliveAgent
}, (res) => {
res.pipe(client_res);
}).on('error', (e) => {
client_res.end();
})
);
}
Example usage:
curl http://localhost:3000/request_uri -H "Host: test.rinu.test"
which is equivalent to:
curl https://test.rinu.test/request_uri

Nginx reverse proxy.. dynamic hostname with header key and value or url path

I have got some nginx problem.I hope you will help me to solve this problem.
There are sevral servers
User PC internet networked;
Nginx proxy, hostnamed "nginxproxy", located in internal network, and it has only server which has Public IP "1.1.1.1" but jumphost, 8090 listen.
server1 hostnamed "tomcat1" located in internal network (only has private IP "70.1.1.1")
server2 hostnamed "tomcat2" located in internal network (only has private IP "70.1.1.2")
and 5, 6, ... There are more servers hostnamed apache1, apache2, redis1 etc...
Now my client wants to send http request call to server located in internal network directly. but it is not possible (because there don't have Public ips..) so the call has to passed in to nginx proxy first.
I just wander that when i call request from user pc, destination server hostname put on the request's header or url, the nginx can parse it and combine to there destination in internal network?
for example i call like this,
http://nginxproxy:1888/[destination hostname]/[path, files like index.html, some keys and values.&k1=v1. etc....]
i hope nginx pass and convert it and call there destination host like this
http://[destination hostname]:8888/[path, files like index.html, some keys and values.&k1=v1. etc....]
i tried to do this. there were some errors..
error log printed
"localhost could not be resolved (10060: Operation timed out), client: 127.0.0.1, server: localhost, request: "GET /localhost/8080/index"
server {
listen 1888;
server_name localhost;
location ~^\/([a-zA-Z0-9]+)\/([0-9]+)\/([a-zA-Z0-9]+) {
proxy_pass http://$1:$2/$3;
}
}
and one more..
in the java code,
i set like this
import org.apache.http.HttpMessage;
HttpMessage request;
request.addHeader("destinationHost", "tomcat2");
request.addHeader("destinationPort", "8888");
and call to this url
http://nginxproxy:1888/[path, files like index.html, some keys and values.&k1=v1. etc....]
can nginx convert url to
http://tomcat2:8888/[path, files like index.html, some keys and values.&k1=v1. etc....]
and pass to there??
if so, how can i set nginx.conf
thank you so much and have a nice day..

Nginx memcached with fallback to remote service

I can't get Nginx working with memcached module, the requirement is to query remote service, cache data in memcached and never fetch remote endpoint until backend invalidates the cache. I have 2 containers with memcached v1.4.35 and one with Nginx v1.11.10.
The configuration is the following:
upstream http_memcached {
server 172.17.0.6:11211;
server 172.17.0.7:11211;
}
upstream remote {
server api.example.com:443;
keepalive 16;
}
server {
listen 80;
location / {
set $memcached_key "$uri?$args";
memcached_pass http_memcached;
error_page 404 502 504 = #remote;
}
location #remote {
internal;
proxy_pass https://remote;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
I tried to set memcached upstream incorrectly but I get HTTP 499 instead and warnings:
*3 upstream server temporarily disabled while connecting to upstream
It seems with described configuration Nginx can reach memcached successfully but can't write or read from it. I can write and read to memcached with telnet successfully.
Can you help me please?
My guesses on what's going on with your configuration
1. 499 codes
HTTP 499 is nginx' custom code meaning the client terminated connection before receiving the response (http://lxr.nginx.org/source/src/http/ngx_http_request.h#0120)
We can easily reproduce it, just
nc -k -l 172.17.0.6 172.17.0.6:11211
and curl your resource - curl will hang for a while and then press Ctrl+C — you'll have this message in your access logs
2. upstream server temporarily disabled while connecting to upstream
It means nginx didn't manage to reach your memcached and just removed it from the pool of upstreams. Suffice is to shutdown both memcached servers and you'd constantly see it in your error logs (I see it every time with error_log ... info).
As you see these messages your assumption that nginx can freely communicate with memcached servers doesn't seem to be true.
Consider explicitly setting http://nginx.org/en/docs/http/ngx_http_memcached_module.html#memcached_bind
and use the -b option with telnet to make sure you're correctly testing memcached servers for availability via your telnet client
3. nginx can reach memcached successfully but can't write or read from it
Nginx can only read from memcached via its built-in module
(http://nginx.org/en/docs/http/ngx_http_memcached_module.html):
The ngx_http_memcached_module module is used to obtain responses from
a memcached server. The key is set in the $memcached_key variable. A
response should be put in memcached in advance by means external to
nginx.
4. overall architecture
It's not fully clear from your question how the overall schema is supposed to work.
nginx's upstream uses weighted round-robin by default.
That means your memcached servers will be queried once at random.
You can change it by setting memcached_next_upstream not_found so a missing key will be considered an error and all of your servers will be polled. It's probably ok for a farm of 2 servers, but unlikely is it what your want for 20 servers
the same is ordinarily the case for memcached client libraries — they'd pick a server out of a pool according to some hashing scheme => so your key would end up on only 1 server out of the pool
5. what to do
I've managed to set up a similar configuration in 10 minutes on my local box - it works as expected. To mitigate debugging I'd get rid of docker containers to avoid networking overcomplication, run 2 memcached servers on different ports in single-threaded mode with -vv option to see when requests are reaching them (memcached -p 11211 -U o -vv) and then play with tail -f and curl to see what's really happening in your case.
6. working solution
nginx config:
https and http/1.1 is not used here but it doesn't matter
upstream http_memcached {
server 127.0.0.1:11211;
server 127.0.0.1:11212;
}
upstream remote {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name server.lan;
access_log /var/log/nginx/server.access.log;
error_log /var/log/nginx/server.error.log info;
location / {
set $memcached_key "$uri?$args";
memcached_next_upstream not_found;
memcached_pass http_memcached;
error_page 404 = #remote;
}
location #remote {
internal;
access_log /var/log/nginx/server.fallback.access.log;
proxy_pass http://remote;
proxy_set_header Connection "";
}
}
server.py:
this is my dummy server (python):
from random import randint
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello: {}\n'.format(randint(1, 100000))
This is how to run it (just need to install flask first)
FLASK_APP=server.py [flask][2] run -p 8080
filling in my first memcached server:
$ telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
set /? 0 900 5
cache
STORED
quit
Connection closed by foreign host.
checking:
note that we get a result every time although we stored data
only in the first server
$ curl http://server.lan && echo
cache
$ curl http://server.lan && echo
cache
$ curl http://server.lan && echo
cache
this one is not in the cache so we'll get a response from server.py
$ curl http://server.lan/?q=1 && echo
Hello: 32337
whole picture:
the 2 windows on the right are
memcached -p 11211 -U o -vv
and
memcached -p 11212 -U o -vv

How to manage many URLs / domains

I would like to get following situation:
I have domains: xxx.com zzz.com and yyy.com
I have one server: xxx.yyy.zz.qq
I would like to configure glassfish to start listening on port 80, and basing on the URL choose proper base catalog for my sites i.e.:
Scenario 1: Visitor is entering url xxx.com or www.xxx.com -> Glassfish receive request on port 80 and pick up catalog: ./glassfish4/myXXXcom/ where index.html for xxx.com is placed.
Scenario 2: Visitor is entering url zzz.com or www.zzz.com -> Glassfish receive request on port 80 and pick up catalog: ./glassfish4/anotherSite/ where index.html for zzz.com is placed.
What have I done:
Installed glassfish 4.1 on my server.
Changed A field of my domains to my server address.
Created virtual server:
glassfish4/bin/asadmin/create-virtual-server --hosts xxx.com xxx
Created http listener:
glassfish4/bin/asadmin create-http-listener --listeneraddress xxx.com --listenerport 80 --default-virtual-server xxx xxx
I think that I am doing something completely wrong here. How do I fix this problem?
If I understand correctly, what you need to do is, create two domains in glassfish or create a cluster and assign two instances of local glassfish instances. One running in port 28080 and another domain in 28081 and use nginx as the load balancer to forward the request to appropriate ports when requests comes from different domains. To make it clear, I am writing step by step
Create a new cluster in glassfish admin console
Create and assign a new local glassfish instance to cluster. This instance will be running in port 28080 and handles requests coming from example1.com
Create another glassfish domain 28081 as the port no for handling example2.com
Install nginx, this acts as proxy and forward request to appropriate
domains. Nginx will be running in port 80.
Start the cluster
Configure nginx as below. This is the crucial part
server {
listen 80;
server_name example1.com;
location / {
proxy_pass http://127.0.0.1:28080;
}
}
server {
listen 80;
server_name example2.com;
location / {
proxy_pass http://127.0.0.1:28081;
}
}
Start nginx
I hope you are familiar with creating clusters and domains in glassfish. If you are unfamiliar with creating clusters in commandline. Glassfish admin console is there, where you can achieve everything. If you need more info, please feel free to write in comments.

Resources