diff --git a/lib/reversetunnel/agentpool.go b/lib/reversetunnel/agentpool.go index 645f984e312bc..34abcfbfadc8c 100644 --- a/lib/reversetunnel/agentpool.go +++ b/lib/reversetunnel/agentpool.go @@ -556,20 +556,21 @@ func (p *AgentPool) getVersion(ctx context.Context) (string, error) { // transport creates a new transport instance. func (p *AgentPool) transport(ctx context.Context, channel ssh.Channel, requests <-chan *ssh.Request, conn sshutils.Conn) *transport { return &transport{ - closeContext: ctx, - component: p.Component, - localClusterName: p.LocalCluster, - kubeDialAddr: p.KubeDialAddr, - authClient: p.Client, - reverseTunnelServer: p.ReverseTunnelServer, - server: p.Server, - emitter: p.Client, - sconn: conn, - channel: channel, - requestCh: requests, - log: p.log, - authServers: p.LocalAuthAddresses, - proxySigner: p.PROXYSigner, + closeContext: ctx, + component: p.Component, + localClusterName: p.LocalCluster, + kubeDialAddr: p.KubeDialAddr, + authClient: p.Client, + reverseTunnelServer: p.ReverseTunnelServer, + server: p.Server, + emitter: p.Client, + sconn: conn, + channel: channel, + requestCh: requests, + log: p.log, + authServers: p.LocalAuthAddresses, + proxySigner: p.PROXYSigner, + forwardClientAddress: true, } } diff --git a/lib/reversetunnel/srv.go b/lib/reversetunnel/srv.go index 792c814fad911..5068696bec113 100644 --- a/lib/reversetunnel/srv.go +++ b/lib/reversetunnel/srv.go @@ -688,6 +688,7 @@ func (s *server) handleTransport(sconn *ssh.ServerConn, nch ssh.NewChannel) { localClusterName: s.ClusterName, emitter: s.Emitter, proxySigner: s.proxySigner, + sconn: sconn, } go t.start() } diff --git a/lib/reversetunnel/transport.go b/lib/reversetunnel/transport.go index d48de1da47ba8..273a275beb840 100644 --- a/lib/reversetunnel/transport.go +++ b/lib/reversetunnel/transport.go @@ -164,6 +164,12 @@ type transport struct { // proxySigner is used to sign PROXY headers and securely propagate client IP information proxySigner multiplexer.PROXYHeaderSigner + + // forwardClientAddress indicates whether we should take into account ClientSrcAddr/ClientDstAddr on incoming + // dial request. If false, we ignore those fields and take address from the parent ssh connection. It allows + // preventing users connecting to the proxy tunnel listener spoofing their address; but we are still able to + // correctly propagate client address in reverse tunnel agents of nodes/services. + forwardClientAddress bool } // start will start the transporting data over the tunnel. This function will @@ -201,6 +207,24 @@ func (p *transport) start() { p.reply(req, false, []byte(err.Error())) return } + + if !p.forwardClientAddress { + // This shouldn't happen in normal operation. Either malicious user or misconfigured client. + if dreq.ClientSrcAddr != "" || dreq.ClientDstAddr != "" { + p.log.Warnf("Received unexpected dial request with client source address %q, "+ + "client destination address %q, when they should be empty.", dreq.ClientSrcAddr, dreq.ClientDstAddr) + } + + // Make sure address fields are overwritten. + if p.sconn != nil { + dreq.ClientSrcAddr = p.sconn.RemoteAddr().String() + dreq.ClientDstAddr = p.sconn.LocalAddr().String() + } else { + dreq.ClientSrcAddr = "" + dreq.ClientDstAddr = "" + } + } + p.log.Debugf("Received out-of-band proxy transport request for %v [%v], from %v.", dreq.Address, dreq.ServerID, dreq.ClientSrcAddr) // directAddress will hold the address of the node to dial to, if we don't