Re: Virtual hosting with s6-networking

From: Colin Booth <colin_at_heliocat.net>
Date: Fri, 23 Sep 2022 15:41:30 +0000

On Wed, Sep 21, 2022 at 01:27:10AM +0200, Songbo Wang wrote:
> - There are two domains, a.example.net and b.example.net, resolving to the
> same machine
> - For each HTTP(S) request to host a.example.net, port 80 (443), a
> superserver will fork and exec httpd-a, with stdin/out reading/writing to
> the network socket
> - Similarly, for each HTTP(S) request to host b.example.net, port 80 (443),
> a superserver will fork and exec httpd-b, with stdin/out reading/writing to
> the network socket
> - httpd-{a,b} may be completely different programs, so we cannot rely on
> them to react differently to HTTP(S) requests with different hosts
>
> I can think of adding an auxiliary program, between s6-tcpserver4d and the
> httpd programs, to distinguish requests to different hosts (for HTTPS, it
> may be able to be done by distinguishing the SNI in the Client Hello
> message, but I am totally not familiar with that). But is there a way to do
> it "natively", without introducing new programs?
>
Generally speaking the answer is no. There is a way to natively do it
with https since s6-tlsserver understands SNI but that is very much not
pretty. Similarly, if an http 1.1 aware utility existed in s6-networking
you could key off of the Host: header but that would be equally ugly.

The reason you can't do it natively with s6-networking out of the box
for http is because s6-tcpserver is ignorant of the application
protocol. It's really only available in TLS due to the desire to support
SNI.
>
> I thought about "multiplexing" the TCP connection, i.e. having two
> superservers listening to the same network socket. This can be done with
> s6-tcpserver4-socketbinder and fd holding mechanisms. And I've tried:
>
> 1. Launch a fdholder-daemon, with the right rules
> 2. Create a TCP socket, bind it to 127.0.0.1:8080, and store the fd in the
> daemon
> 3. Retrieve the fd and exec s6-tcpserver4d
> 4. Do step 3 again
>
> Then I test the setup by "nc 127.0.0.1 8080" and one of the two
> s6-tcpserver4ds will complain it is not able to accept(). Is this kind of
> setup even possible?
>
Alas it isn't. accept(2) dequeues the requst off the listening socket
(TCP:8080 in your case) and creates a connected socket. The rason why
one of the s6-tcpserver4d complains is that they both try to accept, one
of them succeeds, and the other is left calling accept on a listening
socket with nothing queued on it.

The most unix-y fix would be to give the system a second IP address and
run b.example.net on that. Depending on your expected setup that might
not be possible so the most reasonable answer given your requrement of
having multiple sites with different backing web servers is to use a
layer 7 proxy (haproxy, apache with mod_proxy, or so on) as the
"frontend" and then proxy back to the actual web servers based on the
Host header. It does require a third server but it's the least awkward
by far.

Other folks might have some other suggestions but for the most part
things outside of using a full proxy are going to be in the realm if
gnarly or fragile. That said, if someone does know of a super light
weight http proxy that is inetd aware that would be perfect for this
usecase, I just don't know of one.

Cheers!
-- 
Colin Booth
Received on Fri Sep 23 2022 - 17:41:30 CEST

This archive was generated by hypermail 2.4.0 : Fri Sep 23 2022 - 17:42:03 CEST