mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-24 17:47:30 +00:00
ipn/ipnlocal: add PROXY protocol support to Funnel/Serve
This adds the --proxy-protocol flag to 'tailscale serve' and 'tailscale funnel', which tells the Tailscale client to prepend a PROXY protocol[1] header when making connections to the proxied-to backend. I've verified that this works with our existing funnel servers without additional work, since they pass along source address information via PeerAPI already. Updates #7747 [1]: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt Change-Id: I647c24d319375c1b33e995555a541b7615d2d203 Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
This commit is contained in:
@@ -85,6 +85,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
||||
💣 github.com/modern-go/reflect2 from github.com/json-iterator/go
|
||||
github.com/munnerz/goautoneg from k8s.io/kube-openapi/pkg/handler3+
|
||||
github.com/opencontainers/go-digest from github.com/distribution/reference
|
||||
github.com/pires/go-proxyproto from tailscale.com/ipn/ipnlocal
|
||||
github.com/pkg/errors from github.com/evanphx/json-patch/v5+
|
||||
D github.com/prometheus-community/pro-bing from tailscale.com/wgengine/netstack
|
||||
github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil from github.com/prometheus/client_golang/prometheus/promhttp
|
||||
|
||||
@@ -168,6 +168,7 @@ type serveEnv struct {
|
||||
http uint // HTTP port
|
||||
tcp uint // TCP port
|
||||
tlsTerminatedTCP uint // a TLS terminated TCP port
|
||||
proxyProtocol uint // PROXY protocol version (1 or 2)
|
||||
subcmd serveMode // subcommand
|
||||
yes bool // update without prompt
|
||||
service tailcfg.ServiceName // service name
|
||||
@@ -570,7 +571,7 @@ func (e *serveEnv) handleTCPServe(ctx context.Context, srcType string, srcPort u
|
||||
return fmt.Errorf("cannot serve TCP; already serving web on %d", srcPort)
|
||||
}
|
||||
|
||||
sc.SetTCPForwarding(srcPort, fwdAddr, terminateTLS, dnsName)
|
||||
sc.SetTCPForwarding(srcPort, fwdAddr, terminateTLS, 0 /* proxy proto */, dnsName)
|
||||
|
||||
if !reflect.DeepEqual(cursc, sc) {
|
||||
if err := e.lc.SetServeConfig(ctx, sc); err != nil {
|
||||
|
||||
@@ -240,6 +240,7 @@ func newServeV2Command(e *serveEnv, subcmd serveMode) *ffcli.Command {
|
||||
}
|
||||
fs.UintVar(&e.tcp, "tcp", 0, "Expose a TCP forwarder to forward raw TCP packets at the specified port")
|
||||
fs.UintVar(&e.tlsTerminatedTCP, "tls-terminated-tcp", 0, "Expose a TCP forwarder to forward TLS-terminated TCP packets at the specified port")
|
||||
fs.UintVar(&e.proxyProtocol, "proxy-protocol", 0, "PROXY protocol version (1 or 2) for TCP forwarding")
|
||||
fs.BoolVar(&e.yes, "yes", false, "Update without interactive prompts (default false)")
|
||||
fs.BoolVar(&e.tun, "tun", false, "Forward all traffic to the local machine (default false), only supported for services. Refer to docs for more information.")
|
||||
}),
|
||||
@@ -413,6 +414,14 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc {
|
||||
return errHelpFunc(subcmd)
|
||||
}
|
||||
|
||||
if (srvType == serveTypeHTTP || srvType == serveTypeHTTPS) && e.proxyProtocol != 0 {
|
||||
return fmt.Errorf("PROXY protocol is only supported for TCP forwarding, not HTTP/HTTPS")
|
||||
}
|
||||
// Validate PROXY protocol version
|
||||
if e.proxyProtocol != 0 && e.proxyProtocol != 1 && e.proxyProtocol != 2 {
|
||||
return fmt.Errorf("invalid PROXY protocol version %d; must be 1 or 2", e.proxyProtocol)
|
||||
}
|
||||
|
||||
sc, err := e.lc.GetServeConfig(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting serve config: %w", err)
|
||||
@@ -507,7 +516,7 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc {
|
||||
if len(args) > 0 {
|
||||
target = args[0]
|
||||
}
|
||||
err = e.setServe(sc, dnsName, srvType, srvPort, mount, target, funnel, magicDNSSuffix, e.acceptAppCaps)
|
||||
err = e.setServe(sc, dnsName, srvType, srvPort, mount, target, funnel, magicDNSSuffix, e.acceptAppCaps, int(e.proxyProtocol))
|
||||
msg = e.messageForPort(sc, st, dnsName, srvType, srvPort)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -828,7 +837,7 @@ func (e *serveEnv) runServeSetConfig(ctx context.Context, args []string) (err er
|
||||
for name, details := range scf.Services {
|
||||
for ppr, ep := range details.Endpoints {
|
||||
if ep.Protocol == conffile.ProtoTUN {
|
||||
err := e.setServe(sc, name.String(), serveTypeTUN, 0, "", "", false, magicDNSSuffix, nil)
|
||||
err := e.setServe(sc, name.String(), serveTypeTUN, 0, "", "", false, magicDNSSuffix, nil, 0 /* proxy protocol */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -850,7 +859,7 @@ func (e *serveEnv) runServeSetConfig(ctx context.Context, args []string) (err er
|
||||
portStr := fmt.Sprint(destPort)
|
||||
target = fmt.Sprintf("%s://%s", ep.Protocol, net.JoinHostPort(ep.Destination, portStr))
|
||||
}
|
||||
err := e.setServe(sc, name.String(), serveType, port, "/", target, false, magicDNSSuffix, nil)
|
||||
err := e.setServe(sc, name.String(), serveType, port, "/", target, false, magicDNSSuffix, nil, 0 /* proxy protocol */)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service %q: %w", name, err)
|
||||
}
|
||||
@@ -953,7 +962,7 @@ func serveFromPortHandler(tcp *ipn.TCPPortHandler) serveType {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *serveEnv) setServe(sc *ipn.ServeConfig, dnsName string, srvType serveType, srvPort uint16, mount string, target string, allowFunnel bool, mds string, caps []tailcfg.PeerCapability) error {
|
||||
func (e *serveEnv) setServe(sc *ipn.ServeConfig, dnsName string, srvType serveType, srvPort uint16, mount string, target string, allowFunnel bool, mds string, caps []tailcfg.PeerCapability, proxyProtocol int) error {
|
||||
// update serve config based on the type
|
||||
switch srvType {
|
||||
case serveTypeHTTPS, serveTypeHTTP:
|
||||
@@ -966,7 +975,7 @@ func (e *serveEnv) setServe(sc *ipn.ServeConfig, dnsName string, srvType serveTy
|
||||
if e.setPath != "" {
|
||||
return fmt.Errorf("cannot mount a path for TCP serve")
|
||||
}
|
||||
err := e.applyTCPServe(sc, dnsName, srvType, srvPort, target)
|
||||
err := e.applyTCPServe(sc, dnsName, srvType, srvPort, target, proxyProtocol)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to apply TCP serve: %w", err)
|
||||
}
|
||||
@@ -1092,6 +1101,9 @@ func (e *serveEnv) messageForPort(sc *ipn.ServeConfig, st *ipnstate.Status, dnsN
|
||||
if tcpHandler.TerminateTLS != "" {
|
||||
tlsStatus = "TLS terminated"
|
||||
}
|
||||
if ver := tcpHandler.ProxyProtocol; ver != 0 {
|
||||
tlsStatus = fmt.Sprintf("%s, PROXY protocol v%d", tlsStatus, ver)
|
||||
}
|
||||
|
||||
output.WriteString(fmt.Sprintf("|-- tcp://%s:%d (%s)\n", host, srvPort, tlsStatus))
|
||||
for _, a := range ips {
|
||||
@@ -1170,7 +1182,7 @@ func (e *serveEnv) applyWebServe(sc *ipn.ServeConfig, dnsName string, srvPort ui
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *serveEnv) applyTCPServe(sc *ipn.ServeConfig, dnsName string, srcType serveType, srcPort uint16, target string) error {
|
||||
func (e *serveEnv) applyTCPServe(sc *ipn.ServeConfig, dnsName string, srcType serveType, srcPort uint16, target string, proxyProtocol int) error {
|
||||
var terminateTLS bool
|
||||
switch srcType {
|
||||
case serveTypeTCP:
|
||||
@@ -1197,8 +1209,7 @@ func (e *serveEnv) applyTCPServe(sc *ipn.ServeConfig, dnsName string, srcType se
|
||||
return fmt.Errorf("cannot serve TCP; already serving web on %d for %s", srcPort, dnsName)
|
||||
}
|
||||
|
||||
sc.SetTCPForwarding(srcPort, dstURL.Host, terminateTLS, dnsName)
|
||||
|
||||
sc.SetTCPForwarding(srcPort, dstURL.Host, terminateTLS, proxyProtocol, dnsName)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -919,6 +919,73 @@ func TestServeDevConfigMutations(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tcp_with_proxy_protocol_v1",
|
||||
steps: []step{{
|
||||
command: cmd("serve --tcp=8000 --proxy-protocol=1 --bg tcp://localhost:5432"),
|
||||
want: &ipn.ServeConfig{
|
||||
TCP: map[uint16]*ipn.TCPPortHandler{
|
||||
8000: {
|
||||
TCPForward: "localhost:5432",
|
||||
ProxyProtocol: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "tls_terminated_tcp_with_proxy_protocol_v2",
|
||||
steps: []step{{
|
||||
command: cmd("serve --tls-terminated-tcp=443 --proxy-protocol=2 --bg tcp://localhost:5432"),
|
||||
want: &ipn.ServeConfig{
|
||||
TCP: map[uint16]*ipn.TCPPortHandler{
|
||||
443: {
|
||||
TCPForward: "localhost:5432",
|
||||
TerminateTLS: "foo.test.ts.net",
|
||||
ProxyProtocol: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "tcp_update_to_add_proxy_protocol",
|
||||
steps: []step{
|
||||
{
|
||||
command: cmd("serve --tcp=8000 --bg tcp://localhost:5432"),
|
||||
want: &ipn.ServeConfig{
|
||||
TCP: map[uint16]*ipn.TCPPortHandler{
|
||||
8000: {TCPForward: "localhost:5432"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
command: cmd("serve --tcp=8000 --proxy-protocol=1 --bg tcp://localhost:5432"),
|
||||
want: &ipn.ServeConfig{
|
||||
TCP: map[uint16]*ipn.TCPPortHandler{
|
||||
8000: {
|
||||
TCPForward: "localhost:5432",
|
||||
ProxyProtocol: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tcp_proxy_protocol_invalid_version",
|
||||
steps: []step{{
|
||||
command: cmd("serve --tcp=8000 --proxy-protocol=3 --bg tcp://localhost:5432"),
|
||||
wantErr: anyErr(),
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "proxy_protocol_without_tcp",
|
||||
steps: []step{{
|
||||
command: cmd("serve --https=443 --proxy-protocol=1 --bg http://localhost:3000"),
|
||||
wantErr: anyErr(),
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, group := range groups {
|
||||
@@ -1889,18 +1956,19 @@ func TestSetServe(t *testing.T) {
|
||||
e := &serveEnv{}
|
||||
magicDNSSuffix := "test.ts.net"
|
||||
tests := []struct {
|
||||
name string
|
||||
desc string
|
||||
cfg *ipn.ServeConfig
|
||||
st *ipnstate.Status
|
||||
dnsName string
|
||||
srvType serveType
|
||||
srvPort uint16
|
||||
mountPath string
|
||||
target string
|
||||
allowFunnel bool
|
||||
expected *ipn.ServeConfig
|
||||
expectErr bool
|
||||
name string
|
||||
desc string
|
||||
cfg *ipn.ServeConfig
|
||||
st *ipnstate.Status
|
||||
dnsName string
|
||||
srvType serveType
|
||||
srvPort uint16
|
||||
mountPath string
|
||||
target string
|
||||
allowFunnel bool
|
||||
proxyProtocol int
|
||||
expected *ipn.ServeConfig
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "add new handler",
|
||||
@@ -2183,7 +2251,7 @@ func TestSetServe(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := e.setServe(tt.cfg, tt.dnsName, tt.srvType, tt.srvPort, tt.mountPath, tt.target, tt.allowFunnel, magicDNSSuffix, nil)
|
||||
err := e.setServe(tt.cfg, tt.dnsName, tt.srvType, tt.srvPort, tt.mountPath, tt.target, tt.allowFunnel, magicDNSSuffix, nil, tt.proxyProtocol)
|
||||
if err != nil && !tt.expectErr {
|
||||
t.Fatalf("got error: %v; did not expect error.", err)
|
||||
}
|
||||
|
||||
@@ -156,6 +156,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
L github.com/pierrec/lz4/v4/internal/lz4errors from github.com/pierrec/lz4/v4+
|
||||
L github.com/pierrec/lz4/v4/internal/lz4stream from github.com/pierrec/lz4/v4
|
||||
L github.com/pierrec/lz4/v4/internal/xxh32 from github.com/pierrec/lz4/v4/internal/lz4stream
|
||||
github.com/pires/go-proxyproto from tailscale.com/ipn/ipnlocal
|
||||
LD github.com/pkg/sftp from tailscale.com/ssh/tailssh
|
||||
LD github.com/pkg/sftp/internal/encoding/ssh/filexfer from github.com/pkg/sftp
|
||||
D github.com/prometheus-community/pro-bing from tailscale.com/wgengine/netstack
|
||||
|
||||
@@ -43,6 +43,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
||||
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
|
||||
L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink+
|
||||
💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
|
||||
github.com/pires/go-proxyproto from tailscale.com/ipn/ipnlocal
|
||||
D github.com/prometheus-community/pro-bing from tailscale.com/wgengine/netstack
|
||||
L 💣 github.com/safchain/ethtool from tailscale.com/net/netkernelconf
|
||||
W 💣 github.com/tailscale/certstore from tailscale.com/control/controlclient
|
||||
|
||||
@@ -151,5 +151,5 @@
|
||||
});
|
||||
};
|
||||
}
|
||||
# nix-direnv cache busting line: sha256-p8dVVZm2bLL4J/d4TtnUOp3bfMqUkBGE+53RUhamF+A=
|
||||
# nix-direnv cache busting line: sha256-ADuIW4GAiskY0nRFWFk50rzihRIXIdm1Wk7IxfYKMPg=
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@@ -70,6 +70,7 @@ require (
|
||||
github.com/miekg/dns v1.1.58
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/peterbourgon/ff/v3 v3.4.0
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/prometheus-community/pro-bing v0.4.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
sha256-p8dVVZm2bLL4J/d4TtnUOp3bfMqUkBGE+53RUhamF+A=
|
||||
sha256-ADuIW4GAiskY0nRFWFk50rzihRIXIdm1Wk7IxfYKMPg=
|
||||
|
||||
2
go.sum
2
go.sum
@@ -809,6 +809,8 @@ github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkM
|
||||
github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
||||
|
||||
@@ -218,10 +218,11 @@ func (src *TCPPortHandler) Clone() *TCPPortHandler {
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _TCPPortHandlerCloneNeedsRegeneration = TCPPortHandler(struct {
|
||||
HTTPS bool
|
||||
HTTP bool
|
||||
TCPForward string
|
||||
TerminateTLS string
|
||||
HTTPS bool
|
||||
HTTP bool
|
||||
TCPForward string
|
||||
TerminateTLS string
|
||||
ProxyProtocol int
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of HTTPHandler.
|
||||
|
||||
@@ -807,12 +807,19 @@ func (v TCPPortHandlerView) TCPForward() string { return v.ж.TCPForward }
|
||||
// (the HTTPS mode uses ServeConfig.Web)
|
||||
func (v TCPPortHandlerView) TerminateTLS() string { return v.ж.TerminateTLS }
|
||||
|
||||
// ProxyProtocol indicates whether to send a PROXY protocol header
|
||||
// before forwarding the connection to TCPForward.
|
||||
//
|
||||
// This is only valid if TCPForward is non-empty.
|
||||
func (v TCPPortHandlerView) ProxyProtocol() int { return v.ж.ProxyProtocol }
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _TCPPortHandlerViewNeedsRegeneration = TCPPortHandler(struct {
|
||||
HTTPS bool
|
||||
HTTP bool
|
||||
TCPForward string
|
||||
TerminateTLS string
|
||||
HTTPS bool
|
||||
HTTP bool
|
||||
TCPForward string
|
||||
TerminateTLS string
|
||||
ProxyProtocol int
|
||||
}{})
|
||||
|
||||
// View returns a read-only view of HTTPHandler.
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/pires/go-proxyproto"
|
||||
"go4.org/mem"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/net/netutil"
|
||||
@@ -671,10 +672,81 @@ func (b *LocalBackend) tcpHandlerForServe(dport uint16, srcAddr netip.AddrPort,
|
||||
})
|
||||
}
|
||||
|
||||
var proxyHeader []byte
|
||||
if ver := tcph.ProxyProtocol(); ver > 0 {
|
||||
// backAddr is the final "destination" of the connection,
|
||||
// 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
|
||||
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{
|
||||
Version: byte(ver),
|
||||
Command: proxyproto.PROXY,
|
||||
SourceAddr: net.TCPAddrFromAddrPort(proxySrcAddr),
|
||||
DestinationAddr: &net.TCPAddr{
|
||||
IP: destAddr.AsSlice(),
|
||||
Port: backAddr.Port,
|
||||
},
|
||||
}
|
||||
if is4 {
|
||||
header.TransportProtocol = proxyproto.TCPv4
|
||||
} else {
|
||||
header.TransportProtocol = proxyproto.TCPv6
|
||||
}
|
||||
var err error
|
||||
proxyHeader, err = header.Format()
|
||||
if err != nil {
|
||||
b.logf("localbackend: failed to format proxy protocol header for port %v (from %v) to %s: %v", dport, srcAddr, backDst, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bradfitz): do the RegisterIPPortIdentity and
|
||||
// UnregisterIPPortIdentity stuff that netstack does
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
if len(proxyHeader) > 0 {
|
||||
if _, err := backConn.Write(proxyHeader); err != nil {
|
||||
errc <- err
|
||||
backConn.Close() // to ensure that the other side gets EOF
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err := io.Copy(backConn, conn)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
21
ipn/serve.go
21
ipn/serve.go
@@ -149,6 +149,12 @@ type TCPPortHandler struct {
|
||||
// SNI name with this value. It is only used if TCPForward is non-empty.
|
||||
// (the HTTPS mode uses ServeConfig.Web)
|
||||
TerminateTLS string `json:",omitempty"`
|
||||
|
||||
// ProxyProtocol indicates whether to send a PROXY protocol header
|
||||
// before forwarding the connection to TCPForward.
|
||||
//
|
||||
// This is only valid if TCPForward is non-empty.
|
||||
ProxyProtocol int `json:",omitzero"`
|
||||
}
|
||||
|
||||
// HTTPHandler is either a path or a proxy to serve.
|
||||
@@ -404,7 +410,10 @@ func (sc *ServeConfig) SetWebHandler(handler *HTTPHandler, host string, port uin
|
||||
// connections from the given port. If terminateTLS is true, TLS connections
|
||||
// are terminated with only the given host name permitted before passing them
|
||||
// to the fwdAddr.
|
||||
func (sc *ServeConfig) SetTCPForwarding(port uint16, fwdAddr string, terminateTLS bool, host string) {
|
||||
//
|
||||
// If proxyProtocol is non-zero, the corresponding PROXY protocol version
|
||||
// header is sent before forwarding the connection.
|
||||
func (sc *ServeConfig) SetTCPForwarding(port uint16, fwdAddr string, terminateTLS bool, proxyProtocol int, host string) {
|
||||
if sc == nil {
|
||||
sc = new(ServeConfig)
|
||||
}
|
||||
@@ -417,11 +426,15 @@ func (sc *ServeConfig) SetTCPForwarding(port uint16, fwdAddr string, terminateTL
|
||||
}
|
||||
tcpPortHandler = &svcConfig.TCP
|
||||
}
|
||||
mak.Set(tcpPortHandler, port, &TCPPortHandler{TCPForward: fwdAddr})
|
||||
|
||||
if terminateTLS {
|
||||
(*tcpPortHandler)[port].TerminateTLS = host
|
||||
handler := &TCPPortHandler{
|
||||
TCPForward: fwdAddr,
|
||||
ProxyProtocol: proxyProtocol, // can be 0
|
||||
}
|
||||
if terminateTLS {
|
||||
handler.TerminateTLS = host
|
||||
}
|
||||
mak.Set(tcpPortHandler, port, handler)
|
||||
}
|
||||
|
||||
// SetFunnel sets the sc.AllowFunnel value for the given host and port.
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
) {
|
||||
src = ./.;
|
||||
}).shellNix
|
||||
# nix-direnv cache busting line: sha256-p8dVVZm2bLL4J/d4TtnUOp3bfMqUkBGE+53RUhamF+A=
|
||||
# nix-direnv cache busting line: sha256-ADuIW4GAiskY0nRFWFk50rzihRIXIdm1Wk7IxfYKMPg=
|
||||
|
||||
@@ -43,6 +43,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
||||
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
|
||||
LA 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink+
|
||||
LDW 💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
|
||||
github.com/pires/go-proxyproto from tailscale.com/ipn/ipnlocal
|
||||
DI github.com/prometheus-community/pro-bing from tailscale.com/wgengine/netstack
|
||||
L 💣 github.com/safchain/ethtool from tailscale.com/net/netkernelconf
|
||||
W 💣 github.com/tailscale/certstore from tailscale.com/control/controlclient
|
||||
|
||||
Reference in New Issue
Block a user