mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-03 07:40:28 +08:00
* Remove logging from circuit breaker The logging is never output, and if it was would be very verbose. Instead of dropping the level down to trace the logger is removed entirely. * Add constants from trace library The logging functionality in trace is being removed in an upcoming release. To prepare the logging component keys are copied from trace into teleport. * Convert trace.ComponentFields to teleport.ComponentFields * Replace trace.Component with teleport.ComponentKey * Replace trace logging constants
156 lines
4.3 KiB
Go
156 lines
4.3 KiB
Go
/*
|
|
* Teleport
|
|
* Copyright (C) 2023 Gravitational, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package multiplexer
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
|
|
"github.com/gravitational/trace"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/gravitational/teleport"
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
)
|
|
|
|
// TestProxy is tcp passthrough proxy that sends a proxy-line when connecting
|
|
// to the target server.
|
|
type TestProxy struct {
|
|
listener net.Listener
|
|
target string
|
|
closeCh chan (struct{})
|
|
log logrus.FieldLogger
|
|
v2 bool
|
|
}
|
|
|
|
// NewTestProxy creates a new test proxy that sends a proxy-line when
|
|
// proxying connections to the provided target address.
|
|
func NewTestProxy(target string, v2 bool) (*TestProxy, error) {
|
|
listener, err := net.Listen("tcp", "localhost:0")
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return &TestProxy{
|
|
listener: listener,
|
|
target: target,
|
|
closeCh: make(chan struct{}),
|
|
log: logrus.WithField(teleport.ComponentKey, "test:proxy"),
|
|
v2: v2,
|
|
}, nil
|
|
}
|
|
|
|
// Address returns the proxy listen address.
|
|
func (p *TestProxy) Address() string {
|
|
return p.listener.Addr().String()
|
|
}
|
|
|
|
// Serve starts accepting client connections and proxying them to the target.
|
|
func (p *TestProxy) Serve() error {
|
|
for {
|
|
clientConn, err := p.listener.Accept()
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
p.log.Debugf("Accepted connection from %v.", clientConn.RemoteAddr().String())
|
|
go func() {
|
|
if err := p.handleConnection(clientConn); err != nil {
|
|
p.log.WithError(err).Error("Failed to handle connection.")
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
// handleConnection dials the target address, sends a proxy line to it and
|
|
// then starts proxying all traffic b/w client and target.
|
|
func (p *TestProxy) handleConnection(clientConn net.Conn) error {
|
|
serverConn, err := net.Dial("tcp", p.target)
|
|
if err != nil {
|
|
clientConn.Close()
|
|
return trace.Wrap(err)
|
|
}
|
|
defer serverConn.Close()
|
|
errCh := make(chan error, 2)
|
|
go func() { // Client -> server.
|
|
defer clientConn.Close()
|
|
defer serverConn.Close()
|
|
// Write proxy-line first and then start proxying from client.
|
|
err := p.sendProxyLine(clientConn, serverConn)
|
|
if err == nil {
|
|
_, err = io.Copy(serverConn, clientConn)
|
|
}
|
|
errCh <- trace.Wrap(err)
|
|
}()
|
|
go func() { // Server -> client.
|
|
defer clientConn.Close()
|
|
defer serverConn.Close()
|
|
_, err := io.Copy(clientConn, serverConn)
|
|
errCh <- trace.Wrap(err)
|
|
}()
|
|
var errs []error
|
|
for i := 0; i < 2; i++ {
|
|
select {
|
|
case err := <-errCh:
|
|
if err != nil && !utils.IsOKNetworkError(err) {
|
|
errs = append(errs, err)
|
|
}
|
|
case <-p.closeCh:
|
|
p.log.Debug("Closing.")
|
|
return trace.NewAggregate(errs...)
|
|
}
|
|
}
|
|
return trace.NewAggregate(errs...)
|
|
}
|
|
|
|
// sendProxyLine sends proxy-line to the server.
|
|
func (p *TestProxy) sendProxyLine(clientConn, serverConn net.Conn) error {
|
|
clientAddr, err := utils.ParseAddr(clientConn.RemoteAddr().String())
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
serverAddr, err := utils.ParseAddr(serverConn.RemoteAddr().String())
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
proxyLine := &ProxyLine{
|
|
Protocol: TCP4,
|
|
Source: net.TCPAddr{IP: net.ParseIP(clientAddr.Host()), Port: clientAddr.Port(0)},
|
|
Destination: net.TCPAddr{IP: net.ParseIP(serverAddr.Host()), Port: serverAddr.Port(0)},
|
|
}
|
|
p.log.Debugf("Sending %v to %v.", proxyLine.String(), serverConn.RemoteAddr().String())
|
|
if p.v2 {
|
|
b, bErr := proxyLine.Bytes()
|
|
if bErr != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
_, err = serverConn.Write(b)
|
|
} else {
|
|
_, err = serverConn.Write([]byte(proxyLine.String()))
|
|
}
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Close closes the proxy listener.
|
|
func (p *TestProxy) Close() error {
|
|
close(p.closeCh)
|
|
return p.listener.Close()
|
|
}
|