Expose a way to do host/sni matches other than simple equality
danderson opened this issue · comments
For a full replacement for tlsrouter, I need more flexible SNI matching than just string equality (dns wildcard and regex).
Adding this stuff to AddSNIRoute
feels a bit icky, the functions are going to become unwieldy. How about making the route
interface public, with appropriate changes? I think I have sort of a plan for how to make that work. @bradfitz thoughts?
Some things that would need to change in the API:
- Use a
Peeker
interface in signatures instead ofbufio.Reader
type Route interface { Match(Peeker) Target }
. Gives you full power to examine input and decide on a Target.type Matcher func(Peeker) bool
andfunc StaticRoute(Matcher, Target) Route
enables reuse/recomposition of matching code, and plumbs together the simpler cases.Proxy.Add*
replaced by a singleProxy.AddRoute(addr string, r Route)
. Maaybe makeProxy.AddStaticRoute(addr string, m Matcher, t Target)
instead of a standaloneStaticRoute
constructor.
Tentative, poorly thought out usage example that I'm not 100% happy with but hopefully gives an idea:
var p tcpproxy.Proxy
// ACME is a complex implementation of the Route interface.
var a tcpproxy.ACME
// Handle *.acme.invalid
p.AddRoute(":443", &a)
// ACMETarget is passthrough for its arguments, so you can inline it elsewhere if you want.
// Registers the target as a candidate for acme challenges.
target := a.ACMETarget(tcpproxy.To("1.2.3.4:443"))
p.AddStaticRoute(":443", tcpproxy.MatchSNI("foo.com"), target)
// Inlined form
p.AddStaticRoute(":443", tcpproxy.MatchSNI("bar.com"), a.ACMETarget(tcpproxy.To("3.4.5.6:443")))
// Third-party matcher
p.AddStaticRoute(":70", retro.MatchGopher("/Plushie"), tcpproxy.To("10.20.30.40:70"))
// Dynamic router
p.AddRoute(":80", backpain.Yxorp()) // because it's a backwards proxy, see?
Rather than exporting all the Peeker
complexity, why don't we maintain parsing of hostnames out of protocols (HTTP Host and SNI) and give the user a Match func(ctx context.Context, hostname string) bool
instead where they can do the wildcards or database lookups, etc?
s/maintain/retain responsibility for/
I'm torn. What you describe clearly gives me less of a headache. OTOH I kinda like the notion of being able to implement complex things like backpain in a different package.
Let's go with the less exposed API until it becomes untenable.