3

I'm trying to configure HAProxy to be used for both HTTPS traffic and OpenVPN connections through port 443. The configuration is straightforward (first-level domain replaced by example.com for the sake of anonymousity):

frontend www_ssl
    mode tcp
    bind *:443

    acl host_vpn hdr(host) -i vpn.example.com

    use_backend vpn_backend if host_vpn
    default_backend nginx_pool_ssl

backend nginx_pool_ssl
    balance first
    mode tcp
    server web1 192.168.1.2:443 send-proxy check
    server web2 192.168.1.3:443 send-proxy check

backend vpn_backend
    mode tcp
    server vpn1 192.168.1.4:443

When establishing an OpenVPN connection, it fails with the following message:

WARNING: Bad encapsulated packet length from peer (18516), which must be > 0 and <= 1547 -- please ensure that --tun-mtu or --link-mtu is equal on both peers -- this condition could also indicate a possible active attack on the TCP link -- [Attempting restart...]

What happens is that instead of redirecting the traffic to 192.168.1.4, HAProxy goes to 192.168.1.2 which attempts to treat it as an HTTPS request. The same goes when I open vpn.example.com in the browser: I just get the website served by 192.168.1.2.

If I replace the above configuration by this:

frontend www_ssl
    mode tcp
    bind *:443
    default_backend host_vpn

backend vpn_backend
    mode tcp
    server vpn1 192.168.1.4:443

then OpenVPN connection is established correctly and the browser is unable to show anything, meaning that the issue comes from HAProxy configuration.

What is happening here? Is there a error in the original configuration?

  • you need to use mode http – c4f4t0r Dec 24 '17 at 17:02
  • @c4f4t0r: since this is HTTPS and OpenVPN traffic, the mode has to be tcp. As I explained in my answer below, there is no way for HAProxy to get any HTTP headers from any request, including HTTPS, which passes through HAProxy encrypted. – Arseni Mourzenko Dec 24 '17 at 18:01
  • I don't know if you can use this "acl host_vpn hdr(host) -i vpn.example.com" in mode tcp = hdr(name) The HTTP header <name> will be looked up in each HTTP request. Just as with the equivalent ACL 'hdr()' function, the header name in parenthesis is not case sensitive. If the header is absent or if it does not contain any value, the round-robin algorithm is applied instead. – c4f4t0r Dec 25 '17 at 18:26
  • @c4f4t0r: as explained in my answer below, hdr-based ACLs won't work, since HAProxy won't be able to access the headers neither in the encrypted HTTPS traffic, nor OpenVPN which has no headers. – Arseni Mourzenko Dec 25 '17 at 20:31
3

Found it.

The obvious problem is that the ACL directive is relying on HTTP headers: given that HAProxy won't be able to extract the headers from HTTPS traffic and OpenVPN doesn't have them, the ACL has no effect, and the use of default_backend is expected here.

My first thought was to use req.ssl_sni instead of hdr(host). This would make it possible—I thought—to detect whether it's HTTPS traffic or not:

acl host_non_vpn req.ssl_sni -m sub -i example.com

use_backend vpn_backend if !host_non_vpn
use_backend nginx_pool_ssl if host_non_vpn

While it worked well for browser requests, I experienced a problem where a part of the traffic from svn to WebDAV through HTTPS was redirected to OpenVPN server. I'm not sure what is happening here, and without tracing the network traffic, it was difficult to find the issue, nor which requests exactly were failing and why.

I ended up moving OpenVPN to port 80. By using the predefined HTTP ACL, the configuration is now looking like that:

frontend www_http
    mode tcp
    bind *:80
    use_backend nginx_pool_http if HTTP
    use_backend vpn_backend if !HTTP

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.