inetaf / tcpproxy

Proxy TCP connections based on static rules, HTTP Host headers, and SNI server names (Go package or binary)

Home Page:https://pkg.go.dev/github.com/inetaf/tcpproxy

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 of bufio.Reader
  • type Route interface { Match(Peeker) Target }. Gives you full power to examine input and decide on a Target.
  • type Matcher func(Peeker) bool and func StaticRoute(Matcher, Target) Route enables reuse/recomposition of matching code, and plumbs together the simpler cases.
  • Proxy.Add* replaced by a single Proxy.AddRoute(addr string, r Route). Maaybe make Proxy.AddStaticRoute(addr string, m Matcher, t Target) instead of a standalone StaticRoute 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.