This page looks best with JavaScript enabled

Proxy protocol

 ·  🎃 kr0m

This protocol is especially useful when balancing SSL traffic where it is impossible to modify headers. The best solution is to configure haproxy in passthrough mode, terminating the SSL tunnel on the web server but passing the origin IP information through the proxy protocol. This way, the web server receives the client’s IP.

The information provided by the proxy protocol is the same as that obtained using getsockname() and getpeername():

  • Address type (AF_INET - IPv4, AF_INET6 - IPv6, AF_UNIX)
  • Socket protocol (SOCK_STREAM - TCP, SOCK_DGRAM - UDP)
  • Source and destination address
  • Source and destination port

Using the proxy protocol allows us to chain multiple balancers/proxies without losing the source IP address at any time. The protocol simply consists of inserting an easily parseable header at the beginning of the connection.


The protocol supports two header formats:

  • send-proxy: V1 easily readable by humans
  • send-proxy-v2: V2 binary format

Both protocols have been designed to work with the minimum TCP segment size (576 - 40 = 536 bytes), which ensures that the entire header will always be delivered in a single transaction.

The protocol can be applied to a multitude of services:

  • HTTP
  • SSL
  • FTP
  • SMTP
  • POP
  • IMAP
  • LDAP
  • SSH
  • RDP

Does this mean that if you jump from server to server via SSH, you can know the source IP?
No, it means that if we balance SSH traffic through Haproxy, the final SSH server will receive the client’s source IP and not that of the balancer, since the openssh server is capable of interpreting the proxy-protocol information sent by Haproxy. However, the SSH server does not natively support such functionality.
http://jonnyzzz.com/blog/2017/05/24/ssh-haproxy/

Haproxy allows both receiving and emitting information.

Server-side: “accept-proxy” must be configured in the “bind” line, useful if multiple balancers are chained.

frontend web-ssl
        bind *:443 accept-proxy
        mode tcp
        default_backend nodes-ssl

Client-side: “send-proxy” must be configured in the “server” line.

backend nodes-ssl
        balance roundrobin
        mode tcp
        server SRV1 X.X.X.X:443 check send-proxy
        server SRV2 Y.Y.Y.Y:443 check send-proxy

NOTE: For debugging, we leave send-proxy, for production send-proxy-v2.

To make the destination web server read the proxy protocol information, we must follow certain steps depending on the web server used.

To make Apache read the proxy protocol info:

git clone https://github.com/roadrunner2/mod-proxy-protocol
cd mod-proxy-protocol/
make
cp .libs/mod_proxy_protocol.so /usr/lib/apache2/modules/

vi /etc/apache2/httpd.conf

LoadModule proxy_protocol_module modules/mod_proxy_protocol.so
/etc/init.d/apache2 restart

In the config of the vhost in question:

CustomLog ${APACHE_LOG_DIR}/access.log "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\""

For nginx it would be:
https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/

The proxy protocol allows us to make very advanced configurations, such as balancing mysql traffic and configuring user permissions based on the client’s source IP:
https://www.haproxy.com/blog/using-haproxy-with-the-proxy-protocol-to-better-secure-your-database/

If you liked the article, you can treat me to a RedBull here