Accept-proxy: stops working

Hi Community.

When I adds accept-proxy to a bind configuration, it returns ssl handshake error. Without accept-proxy all is fine except I cannot get the original IP from the connecting client.

Let me introduce the arcitecture:

One frontend public server with HAProxy (Server A). This server is also a VPN server where the internal HAProxy server connects over VPN so Server A and Server B shares a dedicated network.

global
	log /dev/log    local0
	log /dev/log    local1 notice
	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
	stats timeout 30s
	user haproxy
	group haproxy
	daemon

	# Default SSL material locations
	#ca-base /etc/ssl/certs
	#crt-base /etc/ssl/private

	# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
	#ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA3>        #ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
	#ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
		
defaults
	log     global
	mode    http
	option  httplog
	option  dontlognull
	timeout connect 5000
	timeout client  50000
	timeout server  50000
	errorfile 400 /etc/haproxy/errors/400.http
	errorfile 403 /etc/haproxy/errors/403.http
	errorfile 408 /etc/haproxy/errors/408.http
	errorfile 500 /etc/haproxy/errors/500.http
	errorfile 502 /etc/haproxy/errors/502.http
	errorfile 503 /etc/haproxy/errors/503.http
	errorfile 504 /etc/haproxy/errors/504.http

frontend http-in
  bind *:80
  mode http
  option forwardfor
  # Define hosts
  use_backend nc-http-bk if { hdr(host) -i cloud.mydomain.tld }
  redirect scheme https code 301 if { hdr(Host) -i www.mydomain.tld } !{ ssl_fc }

backend nc-http-bk
  balance roundrobin
  mode http
  option forwardfor
  #source 0.0.0.0 usesrc clientip
  server node1 xxx.xxx.xxx.xxx:80 check send-proxy

frontend env_ssl_frontend
  bind *:443
  mode tcp
  http-request redirect scheme https if ! { ssl_fc }
  option tcplog
  option tcp-check
  tcp-request inspect-delay 10s
  tcp-request content accept if { req_ssl_hello_type 1 }
  use_backend bk-https-nc if { req_ssl_sni -i cloud.mydomain.tld }
  use_backend bk-https-www if { req_ssl_sni -i www.mydomain.tld }
  
backend bk-https-nc
  balance roundrobin
  mode tcp
  #source 0.0.0.0 usesrc clientip
  option ssl-hello-chk
  server main xxx.xxx.xxx.xxx:443 check send-proxy
  #check fall 3 rise 2 send-proxy

backend bk-https-www
  balance roundrobin
  mode tcp
  #source 0.0.0.0 usesrc clientip
  option ssl-hello-chk
  server main xxx.xxx.xxx.xxx:443 check send-proxy

Server B is internal and is used by all devices and clients on the internal network.

global
  log /dev/log    local0
  log /dev/log    local1 notice
  stats socket /haproxy-admin.sock mode 660 level admin
  stats timeout 30s
  daemon
  #ssl-default-bind-options ssl-min-ver TLSv1.2 prefer-server-ciphers
  #ssl-default-bind-ciphers ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES256:ECDH+AES128:!aNULL:!SHA1:!AESCCM
  #ssl-default-server-options ssl-min-ver TLSv1.3
  #ssl-default-server-ciphers ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES256:ECDH+AES128:!aNULL:!SHA1:!AESCCM

defaults
  maxconn 1000
  mode http
  log global
  option http-server-close
  option dontlognull # bind *:443 ssl crt .
  timeout http-request 5s
  timeout connect 30s
  timeout client 4h # ddos protection
  timeout server 4h # stick-table type ip size 100k expire 30s store conn_cur
  #option forwardfor

frontend http-in
  bind *:80
  mode http
  option forwardfor
  # Define hosts
  use_backend nc-http-bk if { hdr(host) -i cloud.mydomain.tld }
  redirect scheme https code 301 if { hdr(Host) -i www.mydomain.tld } !{ ssl_fc }
  
# Internal trafic  
frontend env_ssl_frontend
  bind xxx.xxx.xxx.xxx:443
  mode tcp
  http-request redirect scheme https if ! { ssl_fc }
  option tcplog
  option tcp-check
  tcp-request inspect-delay 10s
  tcp-request content accept if { req_ssl_hello_type 1 }
  use_backend bk-https-nc if { req_ssl_sni -i cloud.mydomain.tld }
  use_backend bk-https-www if { req_ssl_sni -i www.mydomain.tld }

# From VPN tunnel (public)
frontend env_vpn_ssl
  bind xxx.xxx.yyy.xxx:443
  mode tcp
  http-request redirect scheme https if ! { ssl_fc }
  option tcplog
  tcp-request inspect-delay 10s
  tcp-request content accept if { req_ssl_hello_type 1 }
  use_backend bk-https-nc if { req_ssl_sni -i cloud.mydomain.tld }
  use_backend bk-https-www if { req_ssl_sni -i www.mydomain.tld }
  
backend nc-http-bk
  balance roundrobin
  mode http
  option forwardfor
  #source 0.0.0.0 usesrc clientip
  server node1 xxx.xxx.xxx.xxx:8080 check send-proxy

backend bk-https-nc
  balance roundrobin
  mode tcp
  #source 0.0.0.0 usesrc clientip
  option ssl-hello-chk
  server main xxx.xxx.xxx.xxx:4443 send-proxy
  #check fall 3 rise 2 send-proxy

backend bk-https-www
  balance roundrobin
  mode tcp
  #source 0.0.0.0 usesrc clientip
  option ssl-hello-chk
  server main xxx.xxx.xxx.xxx:4443 send-proxy
  #check fall 3 rise 2 send-proxy

If I adds

frontend env_vpn_ssl
  bind xxx.xxx.yyy.xxx:443 accept-proxy

Then I get an SSL Handshake error on my downstream endpoint. Without that one, all trafic is proxied without any other error, but the connecting IP in the webserver behind Server B, is recognised to be the VPN tunnel IP of Server A.

I have tested using send-proxy-v2 but same result. Any ideas?

When you set accept-proxy, the client needs to send to actually send the PROXY protocol. If it doesn’t, it will not work.

So accept-proxy belongs on a bind line that recieves traffic from another haproxy instance configured on the backend with send-proxy.

Thats what I do.

Server A:
Public server with public IP, receives the trafic from outside.

Server B:
Internal “backend” HAProxy server.

Server A proxies port 80 and 443 from public IP to internal HAProxy (Server B) through a VPN network.

Remove option ssl-hello-chk everywhere, this will break when the proxy protocol is used, because it is a statically constructed SSLv3 client_hello. Instead you should use the SSL layer to health check, if that’s what you need (using check-ssl)…

Ah! Makes sense! Thanks.

I have removed all ssl-hello-chk but I have same error.

First of all disable health checks by removing the check keyword on the server line. At least we will know whether this is about failing health checks or other issues.

1 Like

Everywhere or just for the frontends and backends in scope?

All checks removed. Same error.

Can you try temporarily using a default_backend in frontend env_vpn_ssl, to see if SNI matches stop working in this case and if that is the only issue here?

Sure.

Nope. Using default_backend on Server A and Server B, same error.
Really appreciates the help!

In this case we need:

  • full logging in option tcplog mode from both haproxy instances
  • traffic capture between the two haproxy instances, between the client and instance a and between instance b and the server