How to handle multiple HTTPS websites?

I’m actually proxying (rouiting, what’s the right term?) only 1 web server.

My actual config is that, and it’s my starting point.

frontend web

        mode http
        
        bind *:80
        
        # NOTE: This is a wildcard certicate, used on haproxy AND on ALL web servers
        bind *:443 ssl crt /etc/ssl/private/mydomain.tld.pem alpn h2,http/1.1

        reqadd X-Forwarded-Proto:\ https
        redirect scheme https if !{ ssl_fc }

        option forwardfor
        
        acl acl_pihole2 hdr(host) -i pihole2.mydomain.tld
        use_backend pihole2 if acl_pihole2

backend pihole2
        # Why port 80?
        # See https://discourse.haproxy.org/t/whats-the-canonical-way-to-handle-port-80-and-port-443/2903/10?u=realtebo
        server web 192.168.1.228:80 check 

Now I’d like to start adding more backends.

The next backend I must add is for the openvpn machine. In this machine (IP 192.168.225) there is a lighttpd with 2 services, one for users, one for the admin

   https://192.168.1.225 (port 443, automatically) 
   https://192.168.1.225:943/admin (port 943,manually) 

About HAProxy, I cannot simply add

    acl acl_openvpn hdr(host) -i openvpn.mydomain.tld
    use_backend openvpn if acl_openvpn

and

backend openvpn
        server web 192.168.1.225:80 check 

as done for the pihole2 (as you can see in the config file above), because in this case I need to pass-trough the SSL.

HAProxy is marvellious but I see 40 tutorials doing things in 50 different ways, and all of them has an asterisk about what this configuration will break… or it too old (<= v1.5 )

I ask you a suggestion about how to start planning the followin config

  • preserve the ability to route based on domain name
  • pass-throught the ssl to 2 openvpn services
  • but still be optionally able to terminate SSL on the proxy; not required, but usefull
  • Note I am using a wildacard certificate, so my life is easier. Only one .pem, no need for crt_list

In short

[ x ] http or https://pihole2.mydomain.tld -> http://192.168.1.228:80 [Done]

[   ] http:// or https://openvpnuser.mydomain.tld -> https://192.168.225:443 - [Todo]
[   ] http:// or https://openvpnadmin.mydomain.tld -> https://192.168.225:943 - [Todo]

I’m reading about change from http to tcp mode and inspecting ssl, but I cannot see 2 tutorials with the same commands, structure, idea or goal. I’m a bit lost.

I assume your intention is to to have everything on port 443 on your public IP, so the admin openvpn instance does not use a different port externally, is that correct?

I’m not sure openvpn in tcp mode is really regular TLS and I also, I’m not sure if it’s clients send SNI.

Understood, although it’s not complicated at all and there is no need for crt-list. You can specify the crt keyword multiple times or even point to a directory with thousands of certificates, and haproxy will sort it all out automatically. crt-list is only for a very advanced configuration where the SNI → SAN mapping needs to be specified manually.

[ x ] http or https://pihole2.mydomain.tld -> http://192.168.1.228:80 [Done]
[   ] http:// or https://openvpnuser.mydomain.tld -> https://192.168.225:443 - [Todo]
[   ] http:// or https://openvpnadmin.mydomain.tld -> https://192.168.225:943 - [Todo]

So if we use SNI for this, you need to know about a few limitations:

  • your certificates are overlapping: you have *.mydomain.tld and on the openvpnservers your probably have certificates matching openvpnuser.mydomain.tld and openvpnadmin.mydomain.tld. As a browser would never connect to the openvpn domains, this should not be a problem in your case
  • client needs to send SNI, but OpenVPN does not seem to implement that. Only openvpn3 based clients look like they send SNI

In absence of SNI, you can use a fallback approach (if SNI is not *.mydomain.tld or the VPN domains, route to openvpn backend), but then you cannot distinguish between user and admin vpn.

So you have the choice: would you like to use SNI, but that requires using openvpn3 based clients, or would you rather use just one vpn instance (not distinguishing between user and admin)?

No, sorry. I would like to keep exposed 80 and 443.

Actually pihole2 is already auto elevating to 443, haproxy switch from http to https automatically thanks to

redirect scheme https if !{ ssl_fc }

Also, about OpenVpn:

So you have the choice: would you like to use SNI, but that requires using openvpn3 based clients, or would you rather use just one vpn instance (not distinguishing between user and admin)?

Actually what I need is just to access user web page and admin web page. The VPN itself is on a different port (54567, yes set randomly) ), and it must remain here.

I’m actually tring to proxying only web services.

At end, I’m using wildcart certificates everywhere for now. haproxy, pihole2, openvpn servers (both public and admin)

                                       +------------------------+
                                       |      HAPROXY  223      |
                                +------+------------------------+-----+
                                |                                     |
                        +-------+                                     |                     +---------------------------------+
                        |       |                                     |                     |         openvpn1 225            |
+-------------+         | PORT  |                                     |              +------+---------------------------------+-----------+
|             |         |       |                                     |              |                                                    |
|   internet  +-------> | 80    |    openvpnuser.mydomain.tld:443  +------------------->  Lighttpd:443 for User Login/Profile download    |
|             |         |       |          wildcard SSL               |              |             WE NEED SSL PASSTROUGH HERE            |
|             |         |       |                                     |              |                    WILDCARD SSL                    |
+-------------+         | 443   |                                     |              +----------------------------------------------------+
                        |       |                                     |              |                                                    |
                        +-------+    openvpnadmin.mydomain.tld:443 +---------------------->   Lighttpd:943 for OpenVpn admin area         |
                                |          wildcard SSL               |              |             WE NEED SSL PASSTROUGH HERE            |
                                |                      |              |              |                    WILDCARD SSL                    |
                                |                                     |              +----------------------------------------------------+
                                |                                     |
                                |                                     |
                                |    pihole2.mydomain.tld:80          |                      +----------------------------------+
                                |              +                      |                      |          pihole2 228             |
                                |              |                      |             +--------+----------------------------------+--------+
                                |              |                      |             |                                                    |
                                |              v                      |             |                                                    |
                                |    pihole2.mydomain.tld:443   +-----------------------> Lighttpd: 80 (NO SSL, terminated on HAProxy)   |
                                |          wildcard SSL               |             |                                                    |
                                |                                     |             |                                                    |
                                |                                     |             +----------------------------------------------------+
                                |                                     |
                                |                                     |
                                |                                     |
                                +-------------------------------------+

Ok, that makes it both easier and more difficult. Since these are actually browsers accessing a HTTPS webpage, and the certificates overlap:

This becomes an actual problem for your setup, as SNI based content-switching takes the forwarding decision only once per TLS session, and a browser may request whatever the certificate permits within a TLS session, causing openvpn destinated requests to go to your regular TLS termination and vice versa.

You need to drop the wildcard certificates (everywhere) and replace them with exact matching certificates.

Then, you move your current TLS termination to somewhere else (another port, a unix socket or a abstract namespace socket) and also, enable the proxy protocol:

# NOTE: This is a wildcard certicate, used on haproxy AND on ALL web servers
bind abns@haproxy-tlsterm accept-proxy ssl crt /etc/ssl/private/mydomain.tld.pem alpn h2,http/1.1

Then you create a new listener on port 443, which does content-switching based on SNI and the respective openvpn and default backends:

frontend port443
    mode tcp
    bind *: 443
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }
    use_backend openvpnuser if { req_ssl_sni -i openvpnuser.mydomain.tld }
    use_backend openvpnadmin if { req_ssl_sni -i openvpnadmin.mydomain.tld }
    default_backend haptlsterm

backend openvpnuser
    mode tcp
    servers openvpnuser 192.168.225:443

backend openvpnadmin
    mode tcp
    servers openvpnadmin 192.168.225:943

backend haptlsterm
    mode tcp
    servers haproxytlstermination abns@haproxy-tlsterm send-proxy
1 Like

First of all, a lot of thanks for your time, patience, explanations and instructions.

Then… I return on the problem of wildcard certs.

You quoted yourself in the previous question, telling about a wildcard on proxy, but per-domain on the servers.

Really no. I am using a wildcard everywhere. Each and every single webserver is using the same wildcard certificate. I was thinking it could help me to simplify this problem. I do not understand the TLS problem, I need of course to study this argument before going on. It is of course a non-problem to obtain all single certificates, if needed

But … if the fact i’m using a wildcard everywhere solves / bypasses the problem, I can go on and try to track a diagram of what you suggested me. I never header of the ‘proxy protocol’ until now …

So, TL;DR,: is 100% sure that using wildcard everywhere do not help me avoiding problem with TLS ?

And … I am free to redesign everything from scratch.
I am sure there MUST BE a more intelligent solution; I’m sure to have misprojected all. What I am starting to do is something I am able to realize on an hardware router (CISCO) entering just protocol, port, layer, target and destination. So I stupidly was thinking it could be done via software. I do these kind of setups on my workplace, I had not problem. But at home … I’ve not a phisical firewall / router / proxy / nat

If this solution is so worst now, and I’am just at 25% of what I’m realizing, I absolutely need to stop, study more and re-plan all.

I am a little discouraged. There is too little theory in the proxy doc to understand what to config, how, and why.

No, it makes it worse. When you are TCP content-switching based on SNI, you can’t have overlapping certificates, because when a browser uses service 1 first, and then tries to access service 2, while the TLS session is still open, the browser will re-use the existing session, which will cause the request to go to the wrong backend.

Yes, if you use SNI based content-switching, overlapping certificates are a no-go, and wildcard certificates are causing overlaps in this case. If everything is wildcard, you are causing even more overlaps.

That’s just not true, based on what you are asking. Perhaps you did not properly understand what you where asking here.

You can go down the dedicated certificate route and use the proposal above.

If you want something simpler, which is definitely possible, you need to challenge your requirements. For example:

  • Why do you think you need to SSL passthrough to your openvpn webinterface?
  • Why can’t you use a non-SSL port (just like with pihole2)?
  • If it’s impossible to use a non-SSL port on the openvpn instances, why not re-encrypt on the backend?
1 Like

Thanks for clarifying the SSL connection problem. Now I understand why do not use wildcards.

About hw routing it’s really possible, but of course we do not use wildcards. So it is the trick, I think. But it is off topic, sorry.

Your questions are useful, and need answers.

Pihole2 was my first experiment with haproxy. I seen that terminating SSL on proxy was simpler, so I configured pihole2’s lighttpd to do not upgrade http to https. Simple, and it works for both incoming http and https.

In the case of OpenVpn actually I am not able to force it to accept http. Something is going wrong even in a so simple task, so while I am still trying to resolve this I also tried to pass through the SSL. It was due to a my (again) mistake doing presumption that SSL could be simply forwarded to and from the internal server through haproxy.

It is a stupid thing, but I also was thinking was more secure to preserve SSL until the last door. But of course there is no risk in downgraded http if it’s fully inside our lan. Just paranoid. Overkill.

Anyway, I’d like to learn how to do a SSL passthrough. It could be useful for the future. To try it, I will get asap three certificates, one for each domain. What is not clear is how and where tell to haproxy that for one domain I want to terminate SSL and for other two I want to pass through. What tells to terminate and what tells to passthrough? Do I need 3 frontend? Do I simply use one but listing three pem certificates?

You now suggested a third option: reencrypt. I would like to ask you as simple example, on a single port, single frontend, single back end, for what to do to reencrypt on proxy. Just for curiosity but I would to take the approach to terminate all ssl on proxy, so I can continue to use http mode and do ACL using requested host name.

If you want to reencrypt, you use a setup similar to your pihole2 backend, but use destination port 443 and specify ssl verify none to enable ssl (re-)encryption and disable certificate validation (since this is local anyway, you probably don’t need to go through the hassle of setting up proper certificate validation here).

    acl acl_openvpnuser hdr(host) -i openvpnuser.mydomain.tld
    use_backend openvpnuser if acl_openvpnuser
    acl acl_openvpnadmin hdr(host) -i openvpnadmin.mydomain.tld
    use_backend openvpnadmin if acl_openvpnadmin

backend openvpnuser
    server openvpnuser 192.168.1.225:443 ssl verify none

backend openvpnadmin
    server openvpnadmin 192.168.1.225:943 ssl verify none

1 Like

These option in the frontend must be kept or removed?

    #reqadd X-Forwarded-Proto:\ https
    #redirect scheme https if !{ ssl_fc }

    #option forwardfor

I’m retrying. I take new certificates and update you.

You should leave it as is.