Thanks.  Inline:

On 5/19/2021 8:23 PM, Willy Tarreau wrote:
Hello,

On Wed, May 19, 2021 at 01:52:10PM -0700, Peter Scott (US 172D) wrote:
We have observed some behavior we do not understand with
haproxy-1.5.18-6.e17.x86_64 and need help determining what is going on and
how to get the desired behavior.

The behavior: When an HTTP request is initiated from one of the cluster back
ends, if the load balancer picks that back end to route the request to, it
times out and then routes it to a different back end.  We want it to not
time out.  Requests can come from a back end when a web page runs a program
that does a curl on an API that happens to be within the cluster.

The setup: We have a load balancer with four back ends on transparent
proxying (so that Apache logs show the correct client_ip and not that of the
LB).  The back ends and LB are Centos 7.

The evidence: We have run tcpdump on the load balancer and observed the
following sequence when running a curl from one of the back ends (say, 'X')
to the LB address:

1. Back end X ephemeral port A -> load balancer port 80 connection and
    HTTP request
2. Load balancer (looking like back end X) ephemeral port B -> back end
    X connection
3. No response from back end X (no ACK, no packets at all)
4. Repeated attempts to establish connection from different ephemeral ports
5. Load balancer gives up
6. Load balancer (looking like back end X) -> back end Y port 80
    connection and HTTP request
7. Back end Y -> load balancer: HTTP response
8. Load balancer -> back end X: HTTP response

If the LB doesn't pick back end X in step 2, we go from step 1 to step 6.
The timeout we observed was 15 seconds.  haproxy.cfg contains:

    retries 3
    timeout connect         5s

When we changed the latter to 2s, the delay went to 6 seconds.

Here are the other parts of haproxy.cfg that might be relevant:

defaults
    mode tcp
    option tcplog
    log global
    option tcpka
    option dontlognull
    option http-server-close
    option redispatch
    retries 3
    default-server inter 15s
    timeout http-request    3m
    timeout client          3m
    timeout server          3m
    timeout http-keep-alive 3m
    timeout queue           2m
    timeout connect         5s
    timeout check           10s

frontend internal_http_80
    bind xxx.xxx.xxx.xxx:80 transparent
    default_backend internal_http_80_backend

backend internal_http_80_backend
    balance leastconn
    source 0.0.0.0 usesrc clientip
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You're running in transparent mode, which means that you're spoofing
the client's IP address when connecting to it. You cannot do that to
reconnect to the same node, because the machine definitely sees that
you're using its own IP address to connect, and even if it would
accept it, it would respond to itself!

You need to disable this.

And indeed that was how we had originally configured haproxy, but we switched to transparent mode because we need the correct client IP logged on the Apache back ends.  We couldn't use send-proxy or forwardfor because haproxy is not doing SSL termination.  We couldn't do SSL termination because the back ends are serving hundreds of virtual hosts each with their own SSL certs and the certs change without notice daily as CNAMEs come and go.

We tried send-proxy-v2-ssl and got SSL connection failure.  Is that a valid approach and we just didn't get it right?  Otherwise we're reduced to asking whether there is a way to ensure a connection is not routed to a particular back end if the connection came from there.

Note that in addition, you even have the
same problem if you'd connect to any machine present on the same
network. For example if A connects to LB which connects to B from
A's source address, and B routes the SYN-ACK back directly to A
without passing through the LB, it will fail.

Interestingly, 20 years ago when load balancers used to work at the
network level and were mostly modified routers, this problem was
common and some of them used to implement a source-NAT mechanism on
outgoing traffic called "proxy mode" which made the LB present its
own address when connecting to the servers to avoid this problem.

(...)
We know the version is quite old and have no problem with upgrading, but
it's a sufficiently critical environment that we'd like more than a guess
that the source of the problem is a bug in this version, particularly since
we've been unable to find any discussion of this behavior through googling.
Based on what you're doing it's no big deal if you keep this version,
you need to fix the configuration and/or the architecture so that under
no circumstance the LB connects to a server pretending to be itself or
pretending to be any address that the server will not route back through
the LB.

Hoping this helps,
Willy


Reply via email to