Bind 443 to multiple backends based on dummy paths


#1

Hi! I am new to the forum and after learning and searching a lot in Google is I come up here because I wasn’t able to achive the challenge I am going to explain. Before I start, I started to use Haproxy a few months ago and even though I read a lot about proxy pass, forward and redirect, I think I still don’t understand them enough so I will explain with a diagram I made for the case and my words:

So the thing is I want to connect to my home several services(cameras, nextcloud and others) through only port 443 by diferentiating them through dummy paths (I mean by dummy paths, non existing paths).

The reason for this is that lot of outside internet conections have strong firewalls and port 443 is the only port I can use to connect to my home network. I have Openvpn but I can’t ask everyone to use my vpn to connect to a service.

So after some work, I come up with something that seems is on the way but still lacks something:

frontend rules_443_ssl2
bind *:443 ssl crt my_cert.pem
mode tcp
tcp-request inspect-delay 3s
tcp-request content accept if { req.ssl_hello_type 1 }

    use_backend cam1 if { url_beg /cam1 }
    use_backend cam2 if { url_beg /cam2 }
    use_backend nextcloud  if { url_beg /nextcloud }
default_backend tcp_ovpn

backend cam1
mode http
option forwardfor
option http-server-close
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
http-request set-path %[path,regsub(^/cam1/?,/)] if { path /cam3 } or { path_beg /cam1/ }
server ipcam 192.168.0.147:8081 check fall 3 rise 2 maxconn 50

backend cam2
(similar to 1)

backend nextcloud
mode http
http-request set-path %[path,regsub(^/nextcloud/?,/)] if { url_beg /nextcloud } or { path_beg /nextcloud }
#reqrep ^([^\ ]\ /)nextcloud[/]?(.) \1\ \2
server nextcloud 127.0.0.1:8084 ssl verify none

backend tcp_ovpn
mode tcp
option ssl-hello-chk
server ovpn 127.0.0.1:1194 maxconn 50

So when I test it this is what happens:

  1. From my computer’s web browser I type: https://home_IP/cam1
  2. I get: https://home_IP/main.htm

When I see haproxy log I can see that 1) hits the correct backend(camera 1) but when the backend camera redirects me to its login pagem It doesn’t add the “cam1/login.htm” dummy path and instead sends my directly to “login.htm” and thus I end up in the Openvpn backend which is the default.

So If I type: https://home_IP/cam1/main.htm I reach the camera login but if I login I still get this error.

Thanks in advance!


#2

From a quick glance at this configuration there are a number of things wrong with it:

  • you cannot terminate ssl and then forward it to OpenVPN. OpenVPN needs to handle crypto on it’s own, you are interferening with it, and it will never work that way
  • you cannot access HTTP field in a frontend that is in TCP mode

I would also strongly suggest that you don’t fiddle with those paths (/nextcloud, /cam3, /cam2, etc). It is extremely error prone, and your backends application may refer to absolute paths, which then fail, etc.

You need a layered approach, where you don’t terminate SSL on your primary frontend, you use SNI to differentiate between different backend applications (with different hostnames) and default_backend to openvpn.

Check this post for an example of how to use a 2 layered approach:


#3

Thank you Lukas! I really wanted the path solution but since duckdns.org allows me to have multiple hostnames your solution worked out.

For anyone looking for the final solution, this is a working substract:

##########################

Host redirection with SNI

##########################
frontend rules_443_ssl2
bind-process 2-4
bind *:443 tfo process 2
bind *:443 tfo process 3
bind *:443 tfo process 4
mode tcp
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
use_backend recir_client1 if { req_ssl_sni -i my_host1.duckdns.org }
use_backend recir_client2 if { req_ssl_sni -i my_host2.duckdns.org }
use_backend recir_client3 if { req_ssl_sni -i my_host3.duckdns.org }
use_backend recir_client4 if { req_ssl_sni -i my_host4.duckdns.org }
use_backend recir_client5 if { req_ssl_sni -i my_other_host }
use_backend recir_client6 if { req_ssl_sni -i my_other_host2 }
default_backend openvpn_dest_8070

#Intermediate backend to frontend(1:apexis, 2:foscamhttp, 3:tplink8080, 4:motion, 5:tplink8092, 6:Openvpn)
backend recir_client1
server loopback-for-tls abns@haproxy-clt1 send-proxy-v2
backend recir_client2
server loopback-for-tls abns@haproxy-clt2 send-proxy-v2
backend recir_client3
server loopback-for-tls abns@haproxy-clt3 send-proxy-v2
backend recir_client4
server loopback-for-tls abns@haproxy-clt4 send-proxy-v2
backend recir_client5
server loopback-for-tls abns@haproxy-clt5 send-proxy-v2
backend recir_client6
server loopback-for-tls abns@haproxy-clt6 send-proxy-v2
###########

OPENVPN

###########
frontend openvpn_in_443_8070
bind *:8071 bind abns@haproxy-clt6 accept-proxy tfo
option tcplog
mode tcp
option tcp-smart-accept
default_backend openvpn_dest_8070

backend openvpn_dest_8070
mode tcp
#option ssl-hello-chk
option tcp-smart-connect
server ovpn 127.0.0.1:8070 maxconn 50

############

CAMERA1

############
frontend foscam_in_8072
bind-process 2-4
bind *:8097 tfo ssl crt /etc/ssl/certs_self/ec_concatenated_prime256v1.pem crt /etc/ssl/certs_self/ec_concatenated_secp348r1.pem crt /etc/ssl/certs_self/ec_concatenated_secp521r1.pem process 2
bind *:8097 tfo ssl crt /etc/ssl/certs_self/ec_concatenated_prime256v1.pem crt /etc/ssl/certs_self/ec_concatenated_secp348r1.pem crt /etc/ssl/certs_self/ec_concatenated_secp521r1.pem process 3
bind abns@haproxy-clt2 accept-proxy tfo ssl crt /etc/ssl/certs_self/ec_concatenated_prime256v1.pem crt /etc/ssl/certs_self/ec_concatenated_secp348r1.pem crt /etc/ssl/certs_self/ec_concatenated_secp521r1.pem
process 4
mode tcp
option tcplog
option tcp-smart-accept
default_backend foscam_dest_90

backend foscam_dest_90
mode tcp
option tcp-smart-connect
server ipcam 192.168.0.2:90 check fall 3 rise 2 maxconn 50