haproxy + nginx get client ip behind anonymous proxy - nginx

I'm creating an application, where frontend is Haproxy and nginx.
Do you know a way to get client IP address if navigates behind anonymous proxy with HAproxy ?
My actual configuration for haproxy use "option forwardfor", but I get anonymous proxy IP instead real client IP in nginx logs (using $http_x_forwarded_for var)
frontend general_frontend
bind 111.111.111.111:80
default_backend nginx_farm_backend
backend nginx_farm_backend
balance roundrobin
option abortonclose
option forwardfor
http-check disable-on-404
http-check expect string nginx
option httpchk GET /index.html HTTP/1.0
# - Nodes
server nginx-server-1 222.222.222.222:8080 check on-error mark-down observe layer7 error-limit 1
server nginx-server-1 333.333.333.333:8080 check on-error mark-down observe layer7 error-limit 1
Thank you

Do you Using $remote_addr var for nginx log format?

Related

Enabling proxy protocol in Nginx for just one vhost without breaking the others?

I just set up HAProxy on a server by itself to act as a reverse proxy. This will send the traffic to a main server that's running Nginx.
I got it almost working, aside from being able to pass the original IP through from the proxy server to the web server.
I'm using mode tcp in haproxy since a lot of the traffic coming in will already be on port 443 using SSL. I read that I can't use the option forwardfor in tcp mode, and I can't send SSL traffic using mode http. So I added send-proxy to the server lines, and then tried to enable the proxy protocol on Nginx.
That actually worked for the domain I'm running, but we have about 10 other virtualhost domains being hosted on that same machine, and as soon as I enabled proxy protocol on one vhost separately, it broke ALL of our other domains pointing to that server, as they all started timing out.
Is there a way around this? Can I enable proxy protocol for just one virtualhost on Nginx without breaking the rest of them? Or is there a way to just use http mode with the forwardfor option, even if it's sending SSL traffic?
Below is my haproxy config, with the IPs redacted:
global
maxconn 10000
user haproxy
group haproxy
defaults
retries 3
timeout client 30s
timeout server 30s
timeout connect 30s
mode tcp
frontend incoming
bind *:80
bind *:443
option tcplog
default_backend client_proxy
backend client_proxy
use-server ps_proxy_http if { dst_port 80 }
use-server ps_proxy_https if { dst_port 443 }
server ps_proxy_http XXX.XXX.XXX.XXX:80 send-proxy
server ps_proxy_https XXX.XXX.XXX.XXX:443 send-proxy
This is my first time using HAProxy as a reverse proxy, so any insight would be much appreciated.

Using HAProxy to Front Amazon S3

I have Direct Connect over a fast pipe to an AWS VPC and I'd like to use a cluster of HAProxy instances in my VPC to reverse-proxy one or more S3 buckets. This is so my users on premises can enjoy the increased bandwidth.
I guess the main question is whether this is doable, with the follow-on, "Is there a better solution for this than HAProxy?" I don't want to use an explicit proxy like squid because my only use-case for this is S3.
Assuming HAProxy is fine, I did a quick dummy setup for one bucket as a POC. When I connect directly to the bucket without credentials (simply to test connectivity), I see the "Access Denied" XML response I expect. But when I connect to the reverse-proxy, it seems to redirect me to https://aws.amazon.com/s3/. How am I screwing this up?
Here's my config (replace MY_BUCKET with any bucket name):
global
daemon
maxconn 256
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend servers
backend servers
server server1 MY_BUCKET.s3.amazonaws.com:80 maxconn 100
UPDATE:
Per Pedro's request, here is the configuration I found that makes this work. As you can see, it's extremely bare bones. (I'm using an EC2 instance with two CPUs.)
global
daemon
nbproc 2
nbthread 1
defaults
mode tcp
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend S3
backend S3
server server1 s3.amazonaws.com:80
Proxying from HAProxy to Amazon S3 is definitely possible! Below is a copy of our production config file. We are using HAProxy 2.2 but this should be backward compatible to older versions as well.
resolvers dns
parse-resolv-conf
hold valid 10s
frontend wwoof
mode http
bind *:80
default_backend s3
backend s3
mode http
http-request set-header Host your-bucket.s3-website.eu-west-3.amazonaws.com
http-request del-header Authorization
http-response del-header x-amz-id-2
http-response del-header x-amz-request-id
server s3 your-bucket.s3-website.eu-west-3.amazonaws.com:80 resolvers dns check inter 5000

haproxy: get the host name

I am trying to get the requester host/ip as it comes to haproxy node.
My haproxy config is as below:
frontend www-http
bind *:9000
http-request redirect location https://%fi:9143
frontend www-https
bind *:9143 ssl crt /root/keys.pem
reqadd X-Forwarded-Proto:\ https
default_backend www-backend
backend www-backend
balance roundrobin
cookie SERVERID insert indirect nocache
server server1 1.1.1.1:9080 cookie server1 weight 1 maxconn 1024 check
So here, if any http request comes, then I need to forward to https.
Now request may come either with ip address or hostname in fully qualified form, like
http://10.10.10.10:9000
this needs to be forwarded to https://10.10.10.10:9143
Again, the request may come hostname in fully qualified form, like
http://myhost.domain.com:9000
this needs to be forwarded to https://myhost.domain.com:9143
basically 10.10.10.10 and myhost.domain.com is same system.
Now with the above haproxy configuration, I am not able to get the below, as it is %fi (frontend_ip), so it is redirecting to https://10.10.10.10:9143
So my question is how I can get the haproxy node's ip/host as it comes to haproxy.
I tried below options, which did not work:
http-request redirect location https://%f:9143
http-request redirect location https://%[req.hdr(Host)]:9143
from https://www.haproxy.com/doc/aloha/7.0/haproxy/log_format_rules.html
See How do I set a dynamic variable in HAProxy? for additional details, but using that as a base, here is what should work for you:
frontend www-http
bind *:9000
# Redirect user from http port to https port
http-request set-var(req.hostname) req.hdr(Host),field(1,:),lower
http-request redirect code 301 location https://%[var(req.hostname)]:9143 if !{ ssl_fc }
frontend www-https
bind *:9143 ssl crt /root/keys.pem
reqadd X-Forwarded-Proto:\ https
default_backend www-backend
backend www-backend
balance roundrobin
cookie SERVERID insert indirect nocache
server server1 1.1.1.1:9080 cookie server1 weight 1 maxconn 1024 check
My situation was a little different as I was only looking to redirect a stats UI URL so I didn't have to go update each stats URL in our internal documentation. Here is what worked for my situation (in case it helps someone else):
userlist stats-auth
group admin users adminuser
group readonly users readonlyuser
# Passwords created via mkpasswd -m sha-512 PASSWORD_HERE
user adminuser password NOT_REAL_PASSWORD
user readonlyuser password NOT_REAL_PASSWORD
listen stats
# Used just for the initial connection before we redirect the user to https
bind *:4711
# Combined file containing server, intermediate and root CA certs along
# with the private key for the server cert.
bind *:4712 ssl crt /etc/ssl/private/my-site-name_combined_cert_bundle_with_key.pem
option dontlognull
mode http
option httplog
# Redirect user from http port to https port
http-request set-var(req.hostname) req.hdr(Host),field(1,:),lower
http-request redirect code 301 location https://%[var(req.hostname)]:4712/ if !{ ssl_fc }
acl AUTH http_auth(stats-auth)
acl AUTH_ADMIN http_auth_group(stats-auth) admin
stats enable
# The only "site" for using these ports is the admin UI, so use '/' as
# the base path instead of requiring something like '/haproxy_stats' or
# '/stats' in order to display the UI.
stats uri /
# Force a login if not already authenticated
stats http-request auth unless AUTH
# Allow administrator functionality if user logged in using admin creds
# (there are separate read-only username and password pairs)
stats admin if AUTH_ADMIN
I left out the frontend and backend config as those are much longer/detailed.
You can get the Source address through the src var.
Haproxy holds the requester IP under this , and can be used in acl's and other places.
For logging use it in the following manner : %[src]
Check out these links : src and fetching-samples(under layer 4)

Haproxy Appending Port to `HTTP_HOST` Header in Backend Request

I am using haproxy in front of my web-server for ssl termination.
I am forwarding request on port 81 if request is https and 80 if request is normal http-
backend b1_http
mode http
server bkend_server
backend b1_https
mode http
server bkend_server:81
Problem is, when haproxy sends request to back-end, it sends HTTP_HOST header as request.domain.com:81.
Is it possible in haproxy that I can send https request to back-end at specific port without appending the port in HTTP_HOST request header?
There are two issues, here.
First, there is no HTTP_HOST header. The header is Host:. It sounds like HTTP_HOST is something being generated internally by your web server or framework.
Second, HAProxy doesn't modify the Host: header just because your back-end is listening on a port other than 80. It doesn't actually modify the Host: header at all, unless explicit configured to, using a mechanism like reqirep ^Host: ... or http-request set-header host ....
You can confirm this with a packet capture. You should find that whatever HTTP_HOST is, the value is necessarily being generated internally on the back-end system itself, because it's not coming from HAProxy.

HAProxy connect to backend with source IP

I am running HAProxy on a machine with multiple interfaces and I want the connection to the backend to be made from the source IP of the interface on which the client request came in. Using the source directive from the documentation in the listen blocks didn't seem to do it as all connections seem to come from the first interface. My configuration is as follows:
listen f_192.168.1.10_http
bind 192.168.1.10:80
source 192.168.1.10
mode http
option httplog
capture request header Host len 30
use_backend b_domain1_http if { hdr(host) -i domain1.com }
listen f_192.168.1.20_http
bind 192.168.1.20:80
source 192.168.1.20
mode http
option httplog
capture request header Host len 30
use_backend b_domain1_http if { hdr(host) -i domain1.com }
backend b_domain1_http
mode http
option httplog
server srv1 domain1.com:80 check inter 30s
Ie. I am struggling to get connections coming in on interface 192.168.1.10 to have their source IP be 192.168.1.10 when connecting to the backend. Right now, regardless of if the connection comes in on 192.168.1.10 or 192.168.1.20, the outgoing connection to the backend is initiated from 192.168.1.10. I thought that using source in the listen would accomplish this but when I look at the output of netstat -at, all originating connections to the backend come from 1 interface.
Does anyone have any idea on how I can ensure the source ip of the connection to the backend is the same as the interface of the original client request?
I believe you can use source as a parameter for a server.
backend be1
...
server srv1 domain1.com:80 source ${frontend_ip} check inter 30s
I believe it is possible to substitute %fi for ${frontend_ip}, and you may also use %fp or ${frontend_port} to specifiy the port. This way you can remove the source statements in the frontends.

Resources