ipn/ipnlocal: add ProxyProtocol support to VIP service TCP handler (#18175)

tcpHandlerForVIPService was missing ProxyProtocol support that
tcpHandlerForServe already had. Extract the shared logic into
forwardTCPWithProxyProtocol helper and use it in both handlers.

Fixes #18172

Signed-off-by: Raj Singh <raj@tailscale.com>
pull/18170/merge
Raj Singh 2 days ago committed by GitHub
parent 9613b4eecc
commit 65182f2119
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -591,16 +591,7 @@ func (b *LocalBackend) tcpHandlerForVIPService(dstAddr, srcAddr netip.AddrPort)
}) })
} }
errc := make(chan error, 1) return b.forwardTCPWithProxyProtocol(conn, backConn, tcph.ProxyProtocol(), srcAddr, dport, backDst)
go func() {
_, err := io.Copy(backConn, conn)
errc <- err
}()
go func() {
_, err := io.Copy(conn, backConn)
errc <- err
}()
return <-errc
} }
} }
@ -678,93 +669,93 @@ func (b *LocalBackend) tcpHandlerForServe(dport uint16, srcAddr netip.AddrPort,
}) })
} }
var proxyHeader []byte // TODO(bradfitz): do the RegisterIPPortIdentity and
if ver := tcph.ProxyProtocol(); ver > 0 { // UnregisterIPPortIdentity stuff that netstack does
// backAddr is the final "destination" of the connection, return b.forwardTCPWithProxyProtocol(conn, backConn, tcph.ProxyProtocol(), srcAddr, dport, backDst)
// which is the connection to the proxied-to backend. }
backAddr := backConn.RemoteAddr().(*net.TCPAddr) }
// We always want to format the PROXY protocol
// header based on the IPv4 or IPv6-ness of
// the client. The SourceAddr and
// DestinationAddr need to match in type, so we
// need to be careful to not e.g. set a
// SourceAddr of type IPv6 and DestinationAddr
// of type IPv4.
//
// If this is an IPv6-mapped IPv4 address,
// though, unmap it.
proxySrcAddr := srcAddr
if proxySrcAddr.Addr().Is4In6() {
proxySrcAddr = netip.AddrPortFrom(
proxySrcAddr.Addr().Unmap(),
proxySrcAddr.Port(),
)
}
is4 := proxySrcAddr.Addr().Is4()
var destAddr netip.Addr return nil
if self := b.currentNode().Self(); self.Valid() { }
if is4 {
destAddr = nodeIP(self, netip.Addr.Is4)
} else {
destAddr = nodeIP(self, netip.Addr.Is6)
}
}
if !destAddr.IsValid() {
// Pick a best-effort destination address of localhost.
if is4 {
destAddr = netip.AddrFrom4([4]byte{127, 0, 0, 1})
} else {
destAddr = netip.IPv6Loopback()
}
}
header := &proxyproto.Header{ // forwardTCPWithProxyProtocol forwards TCP traffic between conn and backConn,
Version: byte(ver), // optionally prepending a PROXY protocol header if proxyProtoVer > 0.
Command: proxyproto.PROXY, // The srcAddr is the original client address used to build the PROXY header.
SourceAddr: net.TCPAddrFromAddrPort(proxySrcAddr), func (b *LocalBackend) forwardTCPWithProxyProtocol(conn, backConn net.Conn, proxyProtoVer int, srcAddr netip.AddrPort, dport uint16, backDst string) error {
DestinationAddr: &net.TCPAddr{ var proxyHeader []byte
IP: destAddr.AsSlice(), if proxyProtoVer > 0 {
Port: backAddr.Port, backAddr := backConn.RemoteAddr().(*net.TCPAddr)
},
} // We always want to format the PROXY protocol header based on
if is4 { // the IPv4 or IPv6-ness of the client. The SourceAddr and
header.TransportProtocol = proxyproto.TCPv4 // DestinationAddr need to match in type.
} else { // If this is an IPv6-mapped IPv4 address, unmap it.
header.TransportProtocol = proxyproto.TCPv6 proxySrcAddr := srcAddr
} if proxySrcAddr.Addr().Is4In6() {
var err error proxySrcAddr = netip.AddrPortFrom(
proxyHeader, err = header.Format() proxySrcAddr.Addr().Unmap(),
if err != nil { proxySrcAddr.Port(),
b.logf("localbackend: failed to format proxy protocol header for port %v (from %v) to %s: %v", dport, srcAddr, backDst, err) )
} }
is4 := proxySrcAddr.Addr().Is4()
var destAddr netip.Addr
if self := b.currentNode().Self(); self.Valid() {
if is4 {
destAddr = nodeIP(self, netip.Addr.Is4)
} else {
destAddr = nodeIP(self, netip.Addr.Is6)
} }
}
if !destAddr.IsValid() {
// Unexpected: we couldn't determine the node's IP address.
// Pick a best-effort destination address of localhost.
if is4 {
destAddr = netip.AddrFrom4([4]byte{127, 0, 0, 1})
} else {
destAddr = netip.IPv6Loopback()
}
}
// TODO(bradfitz): do the RegisterIPPortIdentity and header := &proxyproto.Header{
// UnregisterIPPortIdentity stuff that netstack does Version: byte(proxyProtoVer),
errc := make(chan error, 1) Command: proxyproto.PROXY,
go func() { SourceAddr: net.TCPAddrFromAddrPort(proxySrcAddr),
if len(proxyHeader) > 0 { DestinationAddr: &net.TCPAddr{
if _, err := backConn.Write(proxyHeader); err != nil { IP: destAddr.AsSlice(),
errc <- err Port: backAddr.Port,
backConn.Close() // to ensure that the other side gets EOF },
return }
} if is4 {
} header.TransportProtocol = proxyproto.TCPv4
_, err := io.Copy(backConn, conn) } else {
errc <- err header.TransportProtocol = proxyproto.TCPv6
}() }
go func() { var err error
_, err := io.Copy(conn, backConn) proxyHeader, err = header.Format()
errc <- err if err != nil {
}() b.logf("localbackend: failed to format proxy protocol header for port %v (from %v) to %s: %v", dport, srcAddr, backDst, err)
return <-errc
} }
} }
return nil errc := make(chan error, 1)
go func() {
if len(proxyHeader) > 0 {
if _, err := backConn.Write(proxyHeader); err != nil {
errc <- err
backConn.Close()
return
}
}
_, err := io.Copy(backConn, conn)
errc <- err
}()
go func() {
_, err := io.Copy(conn, backConn)
errc <- err
}()
return <-errc
} }
func (b *LocalBackend) getServeHandler(r *http.Request) (_ ipn.HTTPHandlerView, at string, ok bool) { func (b *LocalBackend) getServeHandler(r *http.Request) (_ ipn.HTTPHandlerView, at string, ok bool) {

Loading…
Cancel
Save