diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9b226e..e7ee111 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: name: Test strategy: matrix: - go-version: [1.13.x, 1.14.x] + go-version: [1.20.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/cmd/tcp-proxy/main.go b/cmd/tcp-proxy/main.go index 4053855..a351849 100644 --- a/cmd/tcp-proxy/main.go +++ b/cmd/tcp-proxy/main.go @@ -17,16 +17,18 @@ var ( connid = uint64(0) logger proxy.ColorLogger - localAddr = flag.String("l", ":9999", "local address") - remoteAddr = flag.String("r", "localhost:80", "remote address") - verbose = flag.Bool("v", false, "display server actions") - veryverbose = flag.Bool("vv", false, "display server actions and all tcp data") - nagles = flag.Bool("n", false, "disable nagles algorithm") - hex = flag.Bool("h", false, "output hex") - colors = flag.Bool("c", false, "output ansi colors") - unwrapTLS = flag.Bool("unwrap-tls", false, "remote connection with TLS exposed unencrypted locally") - match = flag.String("match", "", "match regex (in the form 'regex')") - replace = flag.String("replace", "", "replace regex (in the form 'regex~replacer')") + localAddr = flag.String("l", ":9999", "local address") + remoteAddr = flag.String("r", "localhost:80", "remote address") + verbose = flag.Bool("v", false, "display server actions") + veryverbose = flag.Bool("vv", false, "display server actions and all tcp data") + nagles = flag.Bool("n", false, "disable nagles algorithm") + hex = flag.Bool("h", false, "output hex") + colors = flag.Bool("c", false, "output ansi colors") + unwrapTLS = flag.Bool("unwrap-tls", false, "remote connection with TLS exposed unencrypted locally") + match = flag.String("match", "", "match regex (in the form 'regex')") + replace = flag.String("replace", "", "replace regex (in the form 'regex~replacer')") + proxyProtocol = flag.Bool("p", false, "Enable proxy protocol for passthrought the real IP") + proxyProtocolVersion = flag.Int("pv", 2, "Proxy protocol version (1, 2) default: 2") ) func main() { @@ -90,6 +92,11 @@ func main() { Color: *colors, } + p.ProxyProtocol = proxy.ProxyProtocol{ + Enabled: *proxyProtocol, + Version: byte(*proxyProtocolVersion), + } + go p.Start() } } diff --git a/go.mod b/go.mod index 7d05ade..d626022 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,5 @@ go 1.13 require ( github.com/mattn/go-colorable v0.1.4 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b + github.com/pires/go-proxyproto v0.7.0 // indirect ) diff --git a/go.sum b/go.sum index de1e071..3f332b3 100644 --- a/go.sum +++ b/go.sum @@ -4,5 +4,7 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= +github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/proxy.go b/proxy.go index 97062ae..7e8eaf8 100644 --- a/proxy.go +++ b/proxy.go @@ -4,6 +4,8 @@ import ( "crypto/tls" "io" "net" + + proxyproto "github.com/pires/go-proxyproto" ) // Proxy - Manages a Proxy connection, piping data between local and remote. @@ -11,7 +13,7 @@ type Proxy struct { sentBytes uint64 receivedBytes uint64 laddr, raddr *net.TCPAddr - lconn, rconn io.ReadWriteCloser + lconn, rconn net.Conn erred bool errsig chan bool tlsUnwrapp bool @@ -21,9 +23,15 @@ type Proxy struct { Replacer func([]byte) []byte // Settings - Nagles bool - Log Logger - OutputHex bool + Nagles bool + Log Logger + OutputHex bool + ProxyProtocol ProxyProtocol +} + +type ProxyProtocol struct { + Version byte + Enabled bool } // New - Create a new Proxy instance. Takes over local connection passed in, @@ -64,6 +72,7 @@ func (p *Proxy) Start() { } else { p.rconn, err = net.DialTCP("tcp", nil, p.raddr) } + if err != nil { p.Log.Warn("Remote connection failed: %s", err) return @@ -83,6 +92,18 @@ func (p *Proxy) Start() { //display both ends p.Log.Info("Opened %s >>> %s", p.laddr.String(), p.raddr.String()) + if p.ProxyProtocol.Enabled { + header := &proxyproto.Header{ + Version: p.ProxyProtocol.Version, + Command: proxyproto.PROXY, + TransportProtocol: proxyproto.TCPv4, + SourceAddr: p.lconn.RemoteAddr(), + DestinationAddr: p.rconn.RemoteAddr(), + } + p.Log.Info("Send proxy header from (%s to %s)", header.SourceAddr.String(), header.DestinationAddr.String()) + header.WriteTo(p.rconn) + } + //bidirectional copy go p.pipe(p.lconn, p.rconn) go p.pipe(p.rconn, p.lconn)