Proxy Protocol v2 is messing up TLS handshake

Hello!

Our company provides Proxy-like solution with Load balancing for our customer, one of our new customer wants to have the packets send to him from us with the Proxy Protocol v2 for HTTPS traffic, normally we ask customer to upload a certificate to our Proxy device but this customer insists on not uploading it, so we don’t have a certificate on our device.

I red online that the Proxy Protocol should still work, but when I try and access the webpage I get this error:

When testing with CURL from a Linux machine:

The proxy protocol seems to be messing up the TLS handshake, but I am not sure why, have you encountered this before? Think you can help me out with the script?

This is the script I am using:

when CLIENT_ACCEPTED {

The binary header format starts with a constant 12 bytes block containing the protocol signature

set sig \x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a

The next byte (the 13th one) is the protocol version and command.

The highest four bits contains the version. As of this specification, it must always be sent as \x2 and the receiver must only accept this value.

The lowest four bits represents the command :

- \x1 : PROXY : the connection was established on behalf of another node, and reflects the original connection endpoints. The receiver must then use the information provided in the protocol block to get original the address.

set ver_cmd \x21

The 14th byte contains the transport protocol and address family. The highest 4 bits contain the address family, the lowest 4 bits contain the protocol.

if {[IP::version] == 4} { # IPv4
set address_family 1
} elseif {[IP::version] == 6} { # IPv6
set address_family 2
}
if {[IP::protocol] == 6} { # TCP
set transport_protocol 1
} elseif {[IP::protocol] == 17} { # UDP
set transport_protocol 2
}
set fam [binary format cu [expr [expr $address_family << 4] + $transport_protocol]]

Starting from the 17th byte, addresses are presented in network byte order.

The address order is always the same :

- source layer 3 address in network byte order

- destination layer 3 address in network byte order

- source layer 4 address if any, in network byte order (port)

- destination layer 4 address if any, in network byte order (port)

if {[IP::version] == 4} { # IPv4
set src_addr [binary format c4 [split [IP::client_addr] .]]
set dst_addr [binary format c4 [split [IP::local_addr] .]]
} elseif {[IP::version] == 6} { # IPv6
set addr [IP::client_addr]
set ip6_list [split $addr :]
while {[llength $ip6_list] < 8} {
set ip6_list [linsert $ip6_list [lsearch -exact $ip6_list “”] “”]
}
for {set i 0} {$i < [llength $ip6_list]} {incr i} {
set ip6_list [lreplace $ip6_list $i $i [format %04s [lindex $ip6_list $i]]]
}
set src_addr [binary format H32 [join $ip6_list “”]]

set addr [IP::local_addr]
set ip6_list [split $addr :]
while {[llength $ip6_list] < 8} {
set ip6_list [linsert $ip6_list [lsearch -exact $ip6_list “”] “”]
}
for {set i 0} {$i < [llength $ip6_list]} {incr i} {
set ip6_list [lreplace $ip6_list $i $i [format %04s [lindex $ip6_list $i]]]
}
set dst_addr [binary format H32 [join $ip6_list “”]]
}
if {[IP::protocol] == 6} { # TCP
set src_port [binary format Su [TCP::client_port]]
set dst_port [binary format Su [TCP::local_port]]
} elseif {[IP::protocol] == 17} { # UDP
set src_port [binary format Su [UDP::client_port]]
set dst_port [binary format Su [UDP::local_port]]
}
set addr $src_addr$dst_addr$src_port$dst_port

The 15th and 16th bytes is the address length in bytes in network endian order.

set len [binary format Su [string length $addr]]

Complete proxy protocol header

set proxy_protocol_v2 $sig$ver_cmd$fam$len$addr

TCP::collect
}
when CLIENT_DATA {
TCP::payload replace 0 0 $proxy_protocol_v2
TCP::release
}
-----END

Example of PCAP taken on our Load balancer (Which is the same device doing the proxy):
For security reasons I hid the IPs but see below:

  1. Red is my server which is trying to reach the customer’s server.
  2. Blue is our Load balancer.
  3. Violet is our customer’s Server.
    Some explanation - Our device is using Force proxy delayed binding, which means its completing TCP hand with my server, only then he negotiates TCP handshake with customer’s server, this is the first TCP handshake on the PCAP.

Thanks a lot if you even read this message, would really appreciate any help you can offer me!

Please share the haproxy configuration.

We’re using Appshape++ script which I wrote in the post, it’s my first time using this so if I’m doing anything wrong or you need any other information please let me know.

So you are not using haproxy. You have written your own proxy and are trying to use proxy protocol v2 towards your customer.

You don’t know whether you implemented it correctly, and you don’t know whether your customer has a correct implementation either.

You need to check every single bit of every single packet in your capture and compare it to the protocol specification.

As a generic tip I can only say: confirm that you are not expecting the proxy protocol on the client side (browser and curl does not send proxy protocol) and your customers server can actually correctly interpret it.

I’m sorry if this is not the place to ask but first of all thanks for your answers.

Second, I’ve never dealt with Proxy-protocol before, is that a way to use HAProxy the same way I use the proxy-protocol code I sent you, by that I mean to use AppShape++ script to run the proxy protocol v2 made by HAproxy?

I don’t know anything about appshape++.

I can help with haproxy. I cannot help with your custom code.