mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
ipn/ipnlocal: add support for funnel in tsnet
Previously the part that handled Funnel connections was not aware of any listeners that tsnet.Servers might have had open so it would check against the ServeConfig and fail. Adding a ServeConfig for a TCP proxy was also not suitable in this scenario as that would mean creating two different listeners and have one forward to the other, which really meant that you could not have funnel and tailnet-only listeners on the same port. This also introduces the ipn.FunnelConn as a way for users to identify whether the call is coming over funnel or not. Currently it only holds the underlying conn and the target as presented in the "Tailscale-Ingress-Target" header. Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
@@ -150,6 +150,22 @@ type LocalBackend struct {
|
||||
shutdownCalled bool // if Shutdown has been called
|
||||
debugSink *capture.Sink
|
||||
|
||||
// getTCPHandlerForFunnelFlow returns a handler for an incoming TCP flow for
|
||||
// the provided srcAddr and dstPort if one exists.
|
||||
//
|
||||
// srcAddr is the source address of the flow, not the address of the Funnel
|
||||
// node relaying the flow.
|
||||
// dstPort is the destination port of the flow.
|
||||
//
|
||||
// It returns nil if there is no known handler for this flow.
|
||||
//
|
||||
// This is specifically used to handle TCP flows for Funnel connections to tsnet
|
||||
// servers.
|
||||
//
|
||||
// It is set once during initialization, and can be nil if SetTCPHandlerForFunnelFlow
|
||||
// is never called.
|
||||
getTCPHandlerForFunnelFlow func(srcAddr netip.AddrPort, dstPort uint16) (handler func(net.Conn))
|
||||
|
||||
// lastProfileID tracks the last profile we've seen from the ProfileManager.
|
||||
// It's used to detect when the user has changed their profile.
|
||||
lastProfileID ipn.ProfileID
|
||||
@@ -3117,6 +3133,12 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs ipn.PrefsView, logf logger.
|
||||
return dcfg
|
||||
}
|
||||
|
||||
// SetTCPHandlerForFunnelFlow sets the TCP handler for Funnel flows.
|
||||
// It should only be called before the LocalBackend is used.
|
||||
func (b *LocalBackend) SetTCPHandlerForFunnelFlow(h func(src netip.AddrPort, dstPort uint16) (handler func(net.Conn))) {
|
||||
b.getTCPHandlerForFunnelFlow = h
|
||||
}
|
||||
|
||||
// SetVarRoot sets the root directory of Tailscale's writable
|
||||
// storage area . (e.g. "/var/lib/tailscale")
|
||||
//
|
||||
|
@@ -761,12 +761,12 @@ func (h *peerAPIHandler) handleServeIngress(w http.ResponseWriter, r *http.Reque
|
||||
bad("Tailscale-Ingress-Src header invalid; want ip:port")
|
||||
return
|
||||
}
|
||||
target := r.Header.Get("Tailscale-Ingress-Target")
|
||||
target := ipn.HostPort(r.Header.Get("Tailscale-Ingress-Target"))
|
||||
if target == "" {
|
||||
bad("Tailscale-Ingress-Target header not set")
|
||||
return
|
||||
}
|
||||
if _, _, err := net.SplitHostPort(target); err != nil {
|
||||
if _, _, err := net.SplitHostPort(string(target)); err != nil {
|
||||
bad("Tailscale-Ingress-Target header invalid; want host:port")
|
||||
return
|
||||
}
|
||||
@@ -779,13 +779,17 @@ func (h *peerAPIHandler) handleServeIngress(w http.ResponseWriter, r *http.Reque
|
||||
return nil, false
|
||||
}
|
||||
io.WriteString(conn, "HTTP/1.1 101 Switching Protocols\r\n\r\n")
|
||||
return conn, true
|
||||
return &ipn.FunnelConn{
|
||||
Conn: conn,
|
||||
Src: srcAddr,
|
||||
Target: target,
|
||||
}, true
|
||||
}
|
||||
sendRST := func() {
|
||||
http.Error(w, "denied", http.StatusForbidden)
|
||||
}
|
||||
|
||||
h.ps.b.HandleIngressTCPConn(h.peerNode, ipn.HostPort(target), srcAddr, getConn, sendRST)
|
||||
h.ps.b.HandleIngressTCPConn(h.peerNode, target, srcAddr, getConn, sendRST)
|
||||
}
|
||||
|
||||
func (h *peerAPIHandler) handleServeInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
|
@@ -281,9 +281,22 @@ func (b *LocalBackend) HandleIngressTCPConn(ingressPeer *tailcfg.Node, target ip
|
||||
sendRST()
|
||||
return
|
||||
}
|
||||
dport := uint16(port16)
|
||||
if b.getTCPHandlerForFunnelFlow != nil {
|
||||
handler := b.getTCPHandlerForFunnelFlow(srcAddr, dport)
|
||||
if handler != nil {
|
||||
c, ok := getConn()
|
||||
if !ok {
|
||||
b.logf("localbackend: getConn didn't complete from %v to port %v", srcAddr, dport)
|
||||
return
|
||||
}
|
||||
handler(c)
|
||||
return
|
||||
}
|
||||
}
|
||||
// TODO(bradfitz): pass ingressPeer etc in context to HandleInterceptedTCPConn,
|
||||
// extend serveHTTPContext or similar.
|
||||
b.HandleInterceptedTCPConn(uint16(port16), srcAddr, getConn, sendRST)
|
||||
b.HandleInterceptedTCPConn(dport, srcAddr, getConn, sendRST)
|
||||
}
|
||||
|
||||
func (b *LocalBackend) HandleInterceptedTCPConn(dport uint16, srcAddr netip.AddrPort, getConn func() (net.Conn, bool), sendRST func()) {
|
||||
|
Reference in New Issue
Block a user