From 1410682fb68e650786940811784c835ddb786e5a Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 2 Mar 2023 14:02:37 -0800 Subject: [PATCH] cmd/sniproxy: add start of a tsnet-based SNI proxy $ curl https://canhazip.com/ 170.173.0.21 $ curl --resolve canhazip.com:443:100.85.165.81 https://canhazip.com/ 34.223.127.151 Updates #1748 Signed-off-by: Brad Fitzpatrick --- cmd/sniproxy/snipproxy.go | 90 +++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 3 ++ 3 files changed, 94 insertions(+) create mode 100644 cmd/sniproxy/snipproxy.go diff --git a/cmd/sniproxy/snipproxy.go b/cmd/sniproxy/snipproxy.go new file mode 100644 index 000000000..0ef212678 --- /dev/null +++ b/cmd/sniproxy/snipproxy.go @@ -0,0 +1,90 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// The sniproxy is an outbound SNI proxy. It receives TLS connections over +// Tailscale on one or more TCP ports and sends them out to the same SNI +// hostname & port on the internet. It only does TCP. +package main + +import ( + "context" + "flag" + "log" + "net" + "strings" + "time" + + "inet.af/tcpproxy" + "tailscale.com/client/tailscale" + "tailscale.com/net/netutil" + "tailscale.com/tsnet" +) + +var ports = flag.String("ports", "443", "comma-separated list of ports to proxy") + +func main() { + flag.Parse() + if *ports == "" { + log.Fatal("no ports") + } + + var s server + defer s.ts.Close() + + lc, err := s.ts.LocalClient() + if err != nil { + log.Fatal(err) + } + s.lc = lc + + for _, portStr := range strings.Split(*ports, ",") { + ln, err := s.ts.Listen("tcp", ":"+portStr) + if err != nil { + log.Fatal(err) + } + log.Printf("Serving on port %v ...", portStr) + go s.serve(ln) + } + select {} +} + +type server struct { + ts tsnet.Server + lc *tailscale.LocalClient +} + +func (s *server) serve(ln net.Listener) { + for { + c, err := ln.Accept() + if err != nil { + log.Fatal(err) + } + go s.serveConn(c) + } +} + +func (s *server) serveConn(c net.Conn) { + addrPortStr := c.LocalAddr().String() + _, port, err := net.SplitHostPort(addrPortStr) + if err != nil { + log.Printf("bogus addrPort %q", addrPortStr) + c.Close() + return + } + + var dialer net.Dialer + dialer.Timeout = 5 * time.Second + + var p tcpproxy.Proxy + p.ListenFunc = func(net, laddr string) (net.Listener, error) { + return netutil.NewOneConnListener(c, nil), nil + } + p.AddSNIRouteFunc(addrPortStr, func(ctx context.Context, sniName string) (t tcpproxy.Target, ok bool) { + log.Printf("got req for %q from %v", sniName, c.RemoteAddr()) + return &tcpproxy.DialProxy{ + Addr: net.JoinHostPort(sniName, port), + DialContext: dialer.DialContext, + }, true + }) + p.Start() +} diff --git a/go.mod b/go.mod index 7827c4f4d..21ecc5509 100644 --- a/go.mod +++ b/go.mod @@ -85,6 +85,7 @@ require ( gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 honnef.co/go/tools v0.4.0-0.dev.0.20230130122044-c30b15588105 inet.af/peercred v0.0.0-20210906144145-0893ea02156a + inet.af/tcpproxy v0.0.0-20221017015627-91f861402626 inet.af/wf v0.0.0-20220728202103-50d96caab2f6 k8s.io/api v0.25.0 k8s.io/apimachinery v0.25.0 diff --git a/go.sum b/go.sum index 495096920..8497e3505 100644 --- a/go.sum +++ b/go.sum @@ -126,6 +126,7 @@ github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -1947,6 +1948,8 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg= inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU= +inet.af/tcpproxy v0.0.0-20221017015627-91f861402626 h1:2dMP3Ox/Wh5BiItwOt4jxRsfzkgyBrHzx2nW28Yg6nc= +inet.af/tcpproxy v0.0.0-20221017015627-91f861402626/go.mod h1:Tojt5kmHpDIR2jMojxzZK2w2ZR7OILODmUo2gaSwjrk= inet.af/wf v0.0.0-20220728202103-50d96caab2f6 h1:BfgDtKnWJTeu+xI1aOEweXdPwqOhB3IbQUDj1XuftcY= inet.af/wf v0.0.0-20220728202103-50d96caab2f6/go.mod h1:bSAQ38BYbY68uwpasXOTZo22dKGy9SNvI6PZFeKomZE= k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0=