Serve HTTP and HTTPS on same port, but don't force HTTPS?

I have a weird situation where I have to serve HTTP and HTTPS on the same port, and when HTTP connections come in, serve the response as HTTP, but when HTTPS connections come in, serve the response as HTTPS. When I attempt to do this with the configuration below:

*.<redacted>:32400 {
        proxy /library/metadata localhost:3213 {
                transparent
        }

        proxy / localhost:32401 {
                transparent
                websocket
        }

        log logs/plex.log

        tls plex.cert plex.key
}

:32400 {
        proxy /library/metadata localhost:3213 {
                transparent
        }

        proxy / localhost:32401 {
                transparent
                websocket
        }

        log logs/plex.log

        tls off
}

Caddy complains:

2020/04/02 12:02:43 cannot multiplex *.<redacted>.plex.direct (TLS) and  (not TLS) on same listen
er

Is there a way to get this setup working?

Thanks!

This is possible with a listener wrapper module, but I believe none have been written yet which do what you need.

How would I start doing this (i.e. what would it need to do)? I can’t find any docs specifically about listener wrappers, only the other types of plugins. (and I don’t see any examples of AddListenerMiddleware in the v1 branch)

Don’t write it for v1, which is soon to be discontinued. Write the extension for v2: Extending Caddy — Caddy Documentation

Your module would be in the caddy.listeners namespace, so for example caddy.listeners.multiplex or something like that.

Your module would implement this interface: caddy package - github.com/caddyserver/caddy/v2 - pkg.go.dev

And then you would set this field in your config to use it: caddy/server.go at 581f1defcb6de580d57f3c3e58b29950d2f42cb7 · caddyserver/caddy · GitHub

That should be all there is to it!

An efficient multiplexer is hard, though. It will require reading a few bytes to know if it is a TLS handshake (make sure to consider TLS 1.3 as well) and then handle accordingly.

1 Like

I’ll try to implement this, although I’m not even sure where to begin (after what you’ve told me to do, at least) :sweat_smile:

Thanks!

1 Like

There’s a no-op implementation here you can see as an example, it’s just a few lines of code: caddy/caddyhttp.go at 178ba024fea4db2b91fd159da629f0a8588f119a · caddyserver/caddy · GitHub

Of course, yours will probably have an Accept() method that reads a few bytes from the net.Conn, then decides whether to initiate a TLS handshake or not. You might actually have to implement both Listener.Accept() and Conn.Read() methods… I did something similar in v1 here: caddy/mitm.go at v1 · caddyserver/caddy · GitHub and caddy/mitm.go at v1 · caddyserver/caddy · GitHub – but maybe yours won’t have to be so complex.

1 Like

Thank you for your help!

If you need to study some prior art, there’s github.com/soheilhy/cmux. They already cite some limitations, so take that in consideration when you’re working on the implementation. For example, it doesn’t have the .Close() method, but there’s an open PR for that that you may look at. There’s a soft fork by CockroachDB at GitHub - cockroachdb/cmux: Connection multiplexer for GoLang: serve different services on the same port!, which they use in CockroachDB.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.