Codebase list golang-github-pires-go-proxyproto / 3d9e80e
Merge branch 'debian/experimental' into debian/sid Pirate Praveen 10 months ago
17 changed file(s) with 651 addition(s) and 556 deletion(s). Raw diff Collapse all Expand all
118118 }
119119 ```
120120
121 ### HTTP Server
122 ```go
123 package main
124
125 import (
126 "net"
127 "net/http"
128 "time"
129
130 "github.com/pires/go-proxyproto"
131 )
132
133 func main() {
134 server := http.Server{
135 Addr: ":8080",
136 }
137
138 ln, err := net.Listen("tcp", server.Addr)
139 if err != nil {
140 panic(err)
141 }
142
143 proxyListener := &proxyproto.Listener{
144 Listener: ln,
145 ReadHeaderTimeout: 10 * time.Second,
146 }
147 defer proxyListener.Close()
148
149 server.Serve(proxyListener)
150 }
151 ```
152
121153 ## Special notes
122154
123155 ### AWS
0 golang-github-pires-go-proxyproto (0.6.0-1) experimental; urgency=medium
1
2 * Team upload.
3 * New upstream release 0.6.0
4
5 -- Pirate Praveen <praveen@debian.org> Thu, 11 Nov 2021 20:47:11 +0530
6
07 golang-github-pires-go-proxyproto (0.4.2-3) unstable; urgency=medium
18
29 * Team upload
+0
-248
debian/patches/01-add-tests-proving-issue-69.patch less more
0 From: "Iestyn C. Elfick" <dev@chimera.dev>
1 Date: Wed, 3 Mar 2021 01:45:02 +0000
2 Subject: add tests proving issue #69
3
4 (cherry picked from commit ac2f466ac556f4a0e21f33252fb63965c0853a58)
5 ---
6 header.go | 3 +-
7 header_test.go | 11 ++--
8 v1_test.go | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
9 3 files changed, 167 insertions(+), 7 deletions(-)
10
11 diff --git a/header.go b/header.go
12 index db877e8..fd67e17 100644
13 --- a/header.go
14 +++ b/header.go
15 @@ -16,7 +16,8 @@ var (
16 SIGV1 = []byte{'\x50', '\x52', '\x4F', '\x58', '\x59'}
17 SIGV2 = []byte{'\x0D', '\x0A', '\x0D', '\x0A', '\x00', '\x0D', '\x0A', '\x51', '\x55', '\x49', '\x54', '\x0A'}
18
19 - ErrLineMustEndWithCrlf = errors.New("proxyproto: header is invalid, must end with \\r\\n")
20 + ErrVersion1HeaderTooLong = errors.New("proxyproto: version 1 header must be 107 bytes or less")
21 + ErrLineMustEndWithCrlf = errors.New("proxyproto: version 1 header is invalid, must end with \\r\\n")
22 ErrCantReadProtocolVersionAndCommand = errors.New("proxyproto: can't read proxy protocol version and command")
23 ErrCantReadAddressFamilyAndProtocol = errors.New("proxyproto: can't read address family or protocol")
24 ErrCantReadLength = errors.New("proxyproto: can't read length")
25 diff --git a/header_test.go b/header_test.go
26 index 5385369..9b8662a 100644
27 --- a/header_test.go
28 +++ b/header_test.go
29 @@ -13,11 +13,12 @@ import (
30 // Stuff to be used in both versions tests.
31
32 const (
33 - NO_PROTOCOL = "There is no spoon"
34 - IP4_ADDR = "127.0.0.1"
35 - IP6_ADDR = "::1"
36 - PORT = 65533
37 - INVALID_PORT = 99999
38 + NO_PROTOCOL = "There is no spoon"
39 + IP4_ADDR = "127.0.0.1"
40 + IP6_ADDR = "::1"
41 + IP6_LONG_ADDR = "1234:5678:9abc:def0:cafe:babe:dead:2bad"
42 + PORT = 65533
43 + INVALID_PORT = 99999
44 )
45
46 var (
47 diff --git a/v1_test.go b/v1_test.go
48 index 8d6dab1..1ee218f 100644
49 --- a/v1_test.go
50 +++ b/v1_test.go
51 @@ -3,19 +3,25 @@ package proxyproto
52 import (
53 "bufio"
54 "bytes"
55 + "io"
56 + "net"
57 "strconv"
58 "strings"
59 "testing"
60 + "time"
61 )
62
63 var (
64 IPv4AddressesAndPorts = strings.Join([]string{IP4_ADDR, IP4_ADDR, strconv.Itoa(PORT), strconv.Itoa(PORT)}, separator)
65 IPv4AddressesAndInvalidPorts = strings.Join([]string{IP4_ADDR, IP4_ADDR, strconv.Itoa(INVALID_PORT), strconv.Itoa(INVALID_PORT)}, separator)
66 IPv6AddressesAndPorts = strings.Join([]string{IP6_ADDR, IP6_ADDR, strconv.Itoa(PORT), strconv.Itoa(PORT)}, separator)
67 + IPv6LongAddressesAndPorts = strings.Join([]string{IP6_LONG_ADDR, IP6_LONG_ADDR, strconv.Itoa(PORT), strconv.Itoa(PORT)}, separator)
68
69 fixtureTCP4V1 = "PROXY TCP4 " + IPv4AddressesAndPorts + crlf + "GET /"
70 fixtureTCP6V1 = "PROXY TCP6 " + IPv6AddressesAndPorts + crlf + "GET /"
71
72 + fixtureTCP6V1Overflow = "PROXY TCP6 " + IPv6LongAddressesAndPorts
73 +
74 fixtureUnknown = "PROXY UNKNOWN" + crlf
75 fixtureUnknownWithAddresses = "PROXY UNKNOWN " + IPv4AddressesAndInvalidPorts + crlf
76 )
77 @@ -75,13 +81,18 @@ var invalidParseV1Tests = []struct {
78 reader: newBufioReader([]byte("PROXY TCP4 " + IPv4AddressesAndInvalidPorts + crlf)),
79 expectedError: ErrInvalidPortNumber,
80 },
81 + {
82 + desc: "header too long",
83 + reader: newBufioReader([]byte("PROXY UNKNOWN " + IPv6LongAddressesAndPorts + " " + crlf)),
84 + expectedError: ErrVersion1HeaderTooLong,
85 + },
86 }
87
88 func TestReadV1Invalid(t *testing.T) {
89 for _, tt := range invalidParseV1Tests {
90 t.Run(tt.desc, func(t *testing.T) {
91 if _, err := Read(tt.reader); err != tt.expectedError {
92 - t.Fatalf("expected %s, actual %s", tt.expectedError, err.Error())
93 + t.Fatalf("expected %s, actual %v", tt.expectedError, err)
94 }
95 })
96 }
97 @@ -175,3 +186,150 @@ func TestWriteV1Valid(t *testing.T) {
98 })
99 }
100 }
101 +
102 +// Tests for parseVersion1 overflow - issue #69.
103 +
104 +type dataSource struct {
105 + NBytes int
106 + NRead int
107 +}
108 +
109 +func (ds *dataSource) Read(b []byte) (int, error) {
110 + if ds.NRead >= ds.NBytes {
111 + return 0, io.EOF
112 + }
113 + avail := ds.NBytes - ds.NRead
114 + if len(b) < avail {
115 + avail = len(b)
116 + }
117 + for i := 0; i < avail; i++ {
118 + b[i] = 0x20
119 + }
120 + ds.NRead += avail
121 + return avail, nil
122 +}
123 +
124 +func TestParseVersion1Overflow(t *testing.T) {
125 + ds := &dataSource{}
126 + reader := bufio.NewReader(ds)
127 + bufSize := reader.Size()
128 + ds.NBytes = bufSize * 16
129 + parseVersion1(reader)
130 + if ds.NRead > bufSize {
131 + t.Fatalf("read: expected max %d bytes, actual %d\n", bufSize, ds.NRead)
132 + }
133 +}
134 +
135 +func listen(t *testing.T) *Listener {
136 + l, err := net.Listen("tcp", "127.0.0.1:0")
137 + if err != nil {
138 + t.Fatalf("listen: %v", err)
139 + }
140 + return &Listener{Listener: l}
141 +}
142 +
143 +func client(t *testing.T, addr, header string, length int, terminate bool, wait time.Duration, done chan struct{}) {
144 + c, err := net.Dial("tcp", addr)
145 + if err != nil {
146 + t.Fatalf("dial: %v", err)
147 + }
148 + defer c.Close()
149 +
150 + if terminate && length < 2 {
151 + length = 2
152 + }
153 +
154 + buf := make([]byte, len(header)+length)
155 + copy(buf, []byte(header))
156 + for i := 0; i < length-2; i++ {
157 + buf[i+len(header)] = 0x20
158 + }
159 + if terminate {
160 + copy(buf[len(header)+length-2:], []byte(crlf))
161 + }
162 +
163 + n, err := c.Write(buf)
164 + if err != nil {
165 + t.Fatalf("write: %v", err)
166 + }
167 + if n != len(buf) {
168 + t.Fatalf("write; short write")
169 + }
170 +
171 + time.Sleep(wait)
172 + close(done)
173 +}
174 +
175 +func TestVersion1Overflow(t *testing.T) {
176 + done := make(chan struct{})
177 +
178 + l := listen(t)
179 + go client(t, l.Addr().String(), fixtureTCP6V1Overflow, 10240, true, 10*time.Second, done)
180 +
181 + c, err := l.Accept()
182 + if err != nil {
183 + t.Fatalf("accept: %v", err)
184 + }
185 +
186 + b := []byte{}
187 + _, err = c.Read(b)
188 + if err == nil {
189 + t.Fatalf("net.Conn: no error reported for oversized header")
190 + }
191 +}
192 +
193 +func TestVersion1SlowLoris(t *testing.T) {
194 + done := make(chan struct{})
195 + timeout := make(chan error)
196 +
197 + l := listen(t)
198 + go client(t, l.Addr().String(), fixtureTCP6V1Overflow, 0, false, 10*time.Second, done)
199 +
200 + c, err := l.Accept()
201 + if err != nil {
202 + t.Fatalf("accept: %v", err)
203 + }
204 +
205 + go func() {
206 + b := []byte{}
207 + _, err = c.Read(b)
208 + timeout <- err
209 + }()
210 +
211 + select {
212 + case <-done:
213 + t.Fatalf("net.Conn: reader still blocked after 10 seconds")
214 + case err := <-timeout:
215 + if err == nil {
216 + t.Fatalf("net.Conn: no error reported for incomplete header")
217 + }
218 + }
219 +}
220 +
221 +func TestVersion1SlowLorisOverflow(t *testing.T) {
222 + done := make(chan struct{})
223 + timeout := make(chan error)
224 +
225 + l := listen(t)
226 + go client(t, l.Addr().String(), fixtureTCP6V1Overflow, 10240, false, 10*time.Second, done)
227 +
228 + c, err := l.Accept()
229 + if err != nil {
230 + t.Fatalf("accept: %v", err)
231 + }
232 +
233 + go func() {
234 + b := []byte{}
235 + _, err = c.Read(b)
236 + timeout <- err
237 + }()
238 +
239 + select {
240 + case <-done:
241 + t.Fatalf("net.Conn: reader still blocked after 10 seconds")
242 + case err := <-timeout:
243 + if err == nil {
244 + t.Fatalf("net.Conn: no error reported for incomplete and overflowed header")
245 + }
246 + }
247 +}
+0
-136
debian/patches/02-fixes-for-69.patch less more
0 From: "Iestyn C. Elfick" <dev@chimera.dev>
1 Date: Wed, 3 Mar 2021 02:39:12 +0000
2 Subject: fixes for #69
3
4 (cherry picked from commit a368adcec57657ab34c0b53c8f771f2224cc5d30)
5 ---
6 header.go | 1 +
7 v1.go | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
8 v1_test.go | 2 +-
9 3 files changed, 73 insertions(+), 9 deletions(-)
10
11 diff --git a/header.go b/header.go
12 index fd67e17..81ebeb3 100644
13 --- a/header.go
14 +++ b/header.go
15 @@ -16,6 +16,7 @@ var (
16 SIGV1 = []byte{'\x50', '\x52', '\x4F', '\x58', '\x59'}
17 SIGV2 = []byte{'\x0D', '\x0A', '\x0D', '\x0A', '\x00', '\x0D', '\x0A', '\x51', '\x55', '\x49', '\x54', '\x0A'}
18
19 + ErrCantReadVersion1Header = errors.New("proxyproto: can't read version 1 header")
20 ErrVersion1HeaderTooLong = errors.New("proxyproto: version 1 header must be 107 bytes or less")
21 ErrLineMustEndWithCrlf = errors.New("proxyproto: version 1 header is invalid, must end with \\r\\n")
22 ErrCantReadProtocolVersionAndCommand = errors.New("proxyproto: can't read proxy protocol version and command")
23 diff --git a/v1.go b/v1.go
24 index 9ff686a..23de95e 100644
25 --- a/v1.go
26 +++ b/v1.go
27 @@ -3,6 +3,7 @@ package proxyproto
28 import (
29 "bufio"
30 "bytes"
31 + "fmt"
32 "net"
33 "strconv"
34 "strings"
35 @@ -22,17 +23,79 @@ func initVersion1() *Header {
36 }
37
38 func parseVersion1(reader *bufio.Reader) (*Header, error) {
39 - // Read until LF shows up, otherwise fail.
40 - // At this point, can't be sure CR precedes LF which will be validated next.
41 - line, err := reader.ReadString('\n')
42 - if err != nil {
43 - return nil, ErrLineMustEndWithCrlf
44 - }
45 - if !strings.HasSuffix(line, crlf) {
46 + //The header cannot be more than 107 bytes long. Per spec:
47 + //
48 + // (...)
49 + // - worst case (optional fields set to 0xff) :
50 + // "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
51 + // => 5 + 1 + 7 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 107 chars
52 + //
53 + // So a 108-byte buffer is always enough to store all the line and a
54 + // trailing zero for string processing.
55 + //
56 + // It must also be CRLF terminated, as above. The header does not otherwise
57 + // contain a CR or LF byte.
58 + //
59 + // ISSUE #69
60 + // We can't use Peek here as it will block trying to fill the buffer, which
61 + // will never happen if the header is TCP4 or TCP6 (max. 56 and 104 bytes
62 + // respectively) and the server is expected to speak first.
63 + //
64 + // Similarly, we can't use ReadString or ReadBytes as these will keep reading
65 + // until the delimiter is found; an abusive client could easily disrupt a
66 + // server by sending a large amount of data that do not contain a LF byte.
67 + // Another means of attack would be to start connections and simply not send
68 + // data after the initial PROXY signature bytes, accumulating a large
69 + // number of blocked goroutines on the server. ReadSlice will also block for
70 + // a delimiter when the internal buffer does not fill up.
71 + //
72 + // A plain Read is also problematic since we risk reading past the end of the
73 + // header without being able to easily put the excess bytes back into the reader's
74 + // buffer (with the current implementation's design).
75 + //
76 + // So we use a ReadByte loop, which solves the overflow problem and avoids
77 + // reading beyond the end of the header. However, we need one more trick to harden
78 + // against partial header attacks (slow loris) - per spec:
79 + //
80 + // (..) The sender must always ensure that the header is sent at once, so that
81 + // the transport layer maintains atomicity along the path to the receiver. The
82 + // receiver may be tolerant to partial headers or may simply drop the connection
83 + // when receiving a partial header. Recommendation is to be tolerant, but
84 + // implementation constraints may not always easily permit this.
85 + //
86 + // We are subject to such implementation constraints. So we return an error if
87 + // the header cannot be fully extracted with a single read of the underlying
88 + // reader.
89 + buf := make([]byte, 0, 107)
90 + for {
91 + b, err := reader.ReadByte()
92 + if err != nil {
93 + return nil, fmt.Errorf(ErrCantReadVersion1Header.Error()+": %v", err)
94 + }
95 + buf = append(buf, b)
96 + if b == '\n' {
97 + // End of header found
98 + break
99 + }
100 + if len(buf) == 107 {
101 + // No delimiter in first 107 bytes
102 + return nil, ErrVersion1HeaderTooLong
103 + }
104 + if reader.Buffered() == 0 {
105 + // Header was not buffered in a single read. Since we can't
106 + // differentiate between genuine slow writers and DoS agents,
107 + // we abort. On healthy networks, this should never happen.
108 + return nil, ErrCantReadVersion1Header
109 + }
110 + }
111 +
112 + // Check for CR before LF.
113 + if len(buf) < 2 || buf[len(buf)-2] != '\r' {
114 return nil, ErrLineMustEndWithCrlf
115 }
116 +
117 // Check full signature.
118 - tokens := strings.Split(line[:len(line)-2], separator)
119 + tokens := strings.Split(string(buf[:len(buf)-2]), separator)
120
121 // Expect at least 2 tokens: "PROXY" and the transport protocol.
122 if len(tokens) < 2 {
123 diff --git a/v1_test.go b/v1_test.go
124 index 1ee218f..0267580 100644
125 --- a/v1_test.go
126 +++ b/v1_test.go
127 @@ -64,7 +64,7 @@ var invalidParseV1Tests = []struct {
128 {
129 desc: "incomplete signature TCP4",
130 reader: newBufioReader([]byte("PROXY TCP4 " + IPv4AddressesAndPorts)),
131 - expectedError: ErrLineMustEndWithCrlf,
132 + expectedError: ErrCantReadVersion1Header,
133 },
134 {
135 desc: "TCP6 with IPv4 addresses",
+0
-149
debian/patches/03-Add-support-for-ReadHeaderTimeout.patch less more
0 From: Marshall Beddoe <mbeddoe@gmail.com>
1 Date: Fri, 9 Apr 2021 20:50:09 -0500
2 Subject: Add support for ReadHeaderTimeout
3
4 Set a read deadline when waiting for the PROXY protocol header.
5 Fix for #65
6
7 (cherry picked from commit cdc63867da24fc609b727231f682670d0d1cd346)
8
9 Closes: #991498, CVE-2021-23409
10 ---
11 README.md | 32 ++++++++++++++++++++++++++++++++
12 protocol.go | 11 ++++++++---
13 protocol_test.go | 38 ++++++++++++++++++++++++++++++++++++++
14 3 files changed, 78 insertions(+), 3 deletions(-)
15
16 diff --git a/README.md b/README.md
17 index 1aedea5..982707c 100644
18 --- a/README.md
19 +++ b/README.md
20 @@ -119,6 +119,38 @@ func main() {
21 }
22 ```
23
24 +### HTTP Server
25 +```go
26 +package main
27 +
28 +import (
29 + "net"
30 + "net/http"
31 + "time"
32 +
33 + "github.com/pires/go-proxyproto"
34 +)
35 +
36 +func main() {
37 + server := http.Server{
38 + Addr: ":8080",
39 + }
40 +
41 + ln, err := net.Listen("tcp", server.Addr)
42 + if err != nil {
43 + panic(err)
44 + }
45 +
46 + proxyListener := &proxyproto.Listener{
47 + Listener: ln,
48 + ReadHeaderTimeout: 10 * time.Second,
49 + }
50 + defer proxyListener.Close()
51 +
52 + server.Serve(proxyListener)
53 +}
54 +```
55 +
56 ## Special notes
57
58 ### AWS
59 diff --git a/protocol.go b/protocol.go
60 index 878ca1c..ebad481 100644
61 --- a/protocol.go
62 +++ b/protocol.go
63 @@ -12,9 +12,10 @@ import (
64 // If the connection is using the protocol, the RemoteAddr() will return
65 // the correct client address.
66 type Listener struct {
67 - Listener net.Listener
68 - Policy PolicyFunc
69 - ValidateHeader Validator
70 + Listener net.Listener
71 + Policy PolicyFunc
72 + ValidateHeader Validator
73 + ReadHeaderTimeout time.Duration
74 }
75
76 // Conn is used to wrap and underlying connection which
77 @@ -51,6 +52,10 @@ func (p *Listener) Accept() (net.Conn, error) {
78 return nil, err
79 }
80
81 + if d := p.ReadHeaderTimeout; d != 0 {
82 + conn.SetReadDeadline(time.Now().Add(d))
83 + }
84 +
85 proxyHeaderPolicy := USE
86 if p.Policy != nil {
87 proxyHeaderPolicy, err = p.Policy(conn.RemoteAddr())
88 diff --git a/protocol_test.go b/protocol_test.go
89 index f5c08be..b3d61ed 100644
90 --- a/protocol_test.go
91 +++ b/protocol_test.go
92 @@ -6,11 +6,13 @@ package proxyproto
93
94 import (
95 "bytes"
96 + "context"
97 "crypto/tls"
98 "crypto/x509"
99 "fmt"
100 "net"
101 "testing"
102 + "time"
103 )
104
105 func TestPassthrough(t *testing.T) {
106 @@ -59,6 +61,42 @@ func TestPassthrough(t *testing.T) {
107 }
108 }
109
110 +func TestReadHeaderTimeout(t *testing.T) {
111 + l, err := net.Listen("tcp", "127.0.0.1:0")
112 + if err != nil {
113 + t.Fatalf("err: %v", err)
114 + }
115 +
116 + pl := &Listener{
117 + Listener: l,
118 + ReadHeaderTimeout: 1 * time.Millisecond,
119 + }
120 +
121 + ctx, cancel := context.WithCancel(context.Background())
122 + defer cancel()
123 +
124 + go func() {
125 + conn, err := net.Dial("tcp", pl.Addr().String())
126 + if err != nil {
127 + t.Fatalf("err: %v", err)
128 + }
129 + defer conn.Close()
130 +
131 + <-ctx.Done()
132 + }()
133 +
134 + conn, err := pl.Accept()
135 + if err != nil {
136 + t.Fatalf("err: %v", err)
137 + }
138 + defer conn.Close()
139 +
140 + // Read blocks forever if there is no ReadHeaderTimeout
141 + recv := make([]byte, 4)
142 + _, err = conn.Read(recv)
143 +
144 +}
145 +
146 func TestParse_ipv4(t *testing.T) {
147 l, err := net.Listen("tcp", "127.0.0.1:0")
148 if err != nil {
+0
-3
debian/patches/series less more
0 01-add-tests-proving-issue-69.patch
1 02-fixes-for-69.patch
2 03-Add-support-for-ReadHeaderTimeout.patch
0 package main
1
2 import (
3 "io"
4 "log"
5 "net"
6
7 proxyproto "github.com/pires/go-proxyproto"
8 )
9
10 func chkErr(err error) {
11 if err != nil {
12 log.Fatalf("Error: %s", err.Error())
13 }
14 }
15
16 func main() {
17 // Dial some proxy listener e.g. https://github.com/mailgun/proxyproto
18 target, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9876")
19 chkErr(err)
20
21 conn, err := net.DialTCP("tcp", nil, target)
22 chkErr(err)
23
24 defer conn.Close()
25
26 // Create a proxyprotocol header or use HeaderProxyFromAddrs() if you
27 // have two conn's
28 header := &proxyproto.Header{
29 Version: 1,
30 Command: proxyproto.PROXY,
31 TransportProtocol: proxyproto.TCPv4,
32 SourceAddr: &net.TCPAddr{
33 IP: net.ParseIP("10.1.1.1"),
34 Port: 1000,
35 },
36 DestinationAddr: &net.TCPAddr{
37 IP: net.ParseIP("20.2.2.2"),
38 Port: 2000,
39 },
40 }
41 // After the connection was created write the proxy headers first
42 _, err = header.WriteTo(conn)
43 chkErr(err)
44 // Then your data... e.g.:
45 _, err = io.WriteString(conn, "HELO")
46 chkErr(err)
47 }
0 package main
1
2 import (
3 "log"
4 "net"
5 "net/http"
6 "time"
7
8 "github.com/pires/go-proxyproto"
9 )
10
11 // TODO: add httpclient example
12
13 func main() {
14 server := http.Server{
15 Addr: ":8080",
16 ConnState: func(c net.Conn, s http.ConnState) {
17 if s == http.StateNew {
18 log.Printf("[ConnState] %s -> %s", c.LocalAddr().String(), c.RemoteAddr().String())
19 }
20 },
21 Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22 log.Printf("[Handler] remote ip %q", r.RemoteAddr)
23 }),
24 }
25
26 ln, err := net.Listen("tcp", server.Addr)
27 if err != nil {
28 panic(err)
29 }
30
31 proxyListener := &proxyproto.Listener{
32 Listener: ln,
33 ReadHeaderTimeout: 10 * time.Second,
34 }
35 defer proxyListener.Close()
36
37 server.Serve(proxyListener)
38 }
0 package main
1
2 import (
3 "log"
4 "net"
5
6 proxyproto "github.com/pires/go-proxyproto"
7 )
8
9 func main() {
10 // Create a listener
11 addr := "localhost:9876"
12 list, err := net.Listen("tcp", addr)
13 if err != nil {
14 log.Fatalf("couldn't listen to %q: %q\n", addr, err.Error())
15 }
16
17 // Wrap listener in a proxyproto listener
18 proxyListener := &proxyproto.Listener{Listener: list}
19 defer proxyListener.Close()
20
21 // Wait for a connection and accept it
22 conn, err := proxyListener.Accept()
23 defer conn.Close()
24
25 // Print connection details
26 if conn.LocalAddr() == nil {
27 log.Fatal("couldn't retrieve local address")
28 }
29 log.Printf("local address: %q", conn.LocalAddr().String())
30
31 if conn.RemoteAddr() == nil {
32 log.Fatal("couldn't retrieve remote address")
33 }
34 log.Printf("remote address: %q", conn.RemoteAddr().String())
35 }
1515 SIGV1 = []byte{'\x50', '\x52', '\x4F', '\x58', '\x59'}
1616 SIGV2 = []byte{'\x0D', '\x0A', '\x0D', '\x0A', '\x00', '\x0D', '\x0A', '\x51', '\x55', '\x49', '\x54', '\x0A'}
1717
18 ErrLineMustEndWithCrlf = errors.New("proxyproto: header is invalid, must end with \\r\\n")
18 ErrCantReadVersion1Header = errors.New("proxyproto: can't read version 1 header")
19 ErrVersion1HeaderTooLong = errors.New("proxyproto: version 1 header must be 107 bytes or less")
20 ErrLineMustEndWithCrlf = errors.New("proxyproto: version 1 header is invalid, must end with \\r\\n")
1921 ErrCantReadProtocolVersionAndCommand = errors.New("proxyproto: can't read proxy protocol version and command")
2022 ErrCantReadAddressFamilyAndProtocol = errors.New("proxyproto: can't read address family or protocol")
2123 ErrCantReadLength = errors.New("proxyproto: can't read length")
1212 // Stuff to be used in both versions tests.
1313
1414 const (
15 NO_PROTOCOL = "There is no spoon"
16 IP4_ADDR = "127.0.0.1"
17 IP6_ADDR = "::1"
18 PORT = 65533
19 INVALID_PORT = 99999
15 NO_PROTOCOL = "There is no spoon"
16 IP4_ADDR = "127.0.0.1"
17 IP6_ADDR = "::1"
18 IP6_LONG_ADDR = "1234:5678:9abc:def0:cafe:babe:dead:2bad"
19 PORT = 65533
20 INVALID_PORT = 99999
2021 )
2122
2223 var (
11
22 import (
33 "bufio"
4 "io"
45 "net"
56 "sync"
67 "time"
1112 // If the connection is using the protocol, the RemoteAddr() will return
1213 // the correct client address.
1314 type Listener struct {
14 Listener net.Listener
15 Policy PolicyFunc
16 ValidateHeader Validator
15 Listener net.Listener
16 Policy PolicyFunc
17 ValidateHeader Validator
18 ReadHeaderTimeout time.Duration
1719 }
1820
1921 // Conn is used to wrap and underlying connection which
4850 conn, err := p.Listener.Accept()
4951 if err != nil {
5052 return nil, err
53 }
54
55 if d := p.ReadHeaderTimeout; d != 0 {
56 conn.SetReadDeadline(time.Now().Add(d))
5157 }
5258
5359 proxyHeaderPolicy := USE
236242
237243 return err
238244 }
245
246 // ReadFrom implements the io.ReaderFrom ReadFrom method
247 func (p *Conn) ReadFrom(r io.Reader) (int64, error) {
248 if rf, ok := p.conn.(io.ReaderFrom); ok {
249 return rf.ReadFrom(r)
250 }
251 return io.Copy(p.conn, r)
252 }
253
254 // WriteTo implements io.WriterTo
255 func (p *Conn) WriteTo(w io.Writer) (int64, error) {
256 p.once.Do(func() { p.readErr = p.readHeader() })
257 if p.readErr != nil {
258 return 0, p.readErr
259 }
260 return p.bufReader.WriteTo(w)
261 }
55
66 import (
77 "bytes"
8 "context"
89 "crypto/tls"
910 "crypto/x509"
1011 "fmt"
12 "io"
13 "io/ioutil"
1114 "net"
1215 "testing"
16 "time"
1317 )
1418
1519 func TestPassthrough(t *testing.T) {
5660 if _, err := conn.Write([]byte("pong")); err != nil {
5761 t.Fatalf("err: %v", err)
5862 }
63 }
64
65 func TestReadHeaderTimeout(t *testing.T) {
66 l, err := net.Listen("tcp", "127.0.0.1:0")
67 if err != nil {
68 t.Fatalf("err: %v", err)
69 }
70
71 pl := &Listener{
72 Listener: l,
73 ReadHeaderTimeout: 1 * time.Millisecond,
74 }
75
76 ctx, cancel := context.WithCancel(context.Background())
77 defer cancel()
78
79 go func() {
80 conn, err := net.Dial("tcp", pl.Addr().String())
81 if err != nil {
82 t.Fatalf("err: %v", err)
83 }
84 defer conn.Close()
85
86 <-ctx.Done()
87 }()
88
89 conn, err := pl.Accept()
90 if err != nil {
91 t.Fatalf("err: %v", err)
92 }
93 defer conn.Close()
94
95 // Read blocks forever if there is no ReadHeaderTimeout
96 recv := make([]byte, 4)
97 _, err = conn.Read(recv)
98
5999 }
60100
61101 func TestParse_ipv4(t *testing.T) {
738778 if err.Error() != "tls: first record does not look like a TLS handshake" {
739779 t.Fatalf("expected tls handshake error, got %s", err)
740780 }
781 }
782
783 type testConn struct {
784 readFromCalledWith io.Reader
785 reads int
786 net.Conn // nil; crash on any unexpected use
787 }
788
789 func (c *testConn) ReadFrom(r io.Reader) (int64, error) {
790 c.readFromCalledWith = r
791 b, err := ioutil.ReadAll(r)
792 return int64(len(b)), err
793 }
794 func (c *testConn) Write(p []byte) (int, error) {
795 return len(p), nil
796 }
797 func (c *testConn) Read(p []byte) (int, error) {
798 if c.reads == 0 {
799 return 0, io.EOF
800 }
801 c.reads--
802 return 1, nil
803 }
804
805 func TestCopyToWrappedConnection(t *testing.T) {
806 innerConn := &testConn{}
807 wrappedConn := NewConn(innerConn)
808 dummySrc := &testConn{reads: 1}
809
810 io.Copy(wrappedConn, dummySrc)
811 if innerConn.readFromCalledWith != dummySrc {
812 t.Error("Expected io.Copy to delegate to ReadFrom function of inner destination connection")
813 }
814 }
815
816 func TestCopyFromWrappedConnection(t *testing.T) {
817 wrappedConn := NewConn(&testConn{reads: 1})
818 dummyDst := &testConn{}
819
820 io.Copy(dummyDst, wrappedConn)
821 if dummyDst.readFromCalledWith != wrappedConn.conn {
822 t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom method of destination")
823 }
824 }
825
826 func TestCopyFromWrappedConnectionToWrappedConnection(t *testing.T) {
827 innerConn1 := &testConn{reads: 1}
828 wrappedConn1 := NewConn(innerConn1)
829 innerConn2 := &testConn{}
830 wrappedConn2 := NewConn(innerConn2)
831
832 io.Copy(wrappedConn1, wrappedConn2)
833 if innerConn1.readFromCalledWith != innerConn2 {
834 t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom of inner destination connection")
835 }
836 }
837
838 func benchmarkTCPProxy(size int, b *testing.B) {
839 //create and start the echo backend
840 backend, err := net.Listen("tcp", "127.0.0.1:0")
841 if err != nil {
842 b.Fatalf("err: %v", err)
843 }
844 defer backend.Close()
845 go func() {
846 for {
847 conn, err := backend.Accept()
848 if err != nil {
849 break
850 }
851 _, err = io.Copy(conn, conn)
852 conn.Close()
853 if err != nil {
854 b.Fatalf("Failed to read entire payload: %v", err)
855 }
856 }
857 }()
858
859 //start the proxyprotocol enabled tcp proxy
860 l, err := net.Listen("tcp", "127.0.0.1:0")
861 if err != nil {
862 b.Fatalf("err: %v", err)
863 }
864 defer l.Close()
865 pl := &Listener{Listener: l}
866 go func() {
867 for {
868 conn, err := pl.Accept()
869 if err != nil {
870 break
871 }
872 bConn, err := net.Dial("tcp", backend.Addr().String())
873 if err != nil {
874 b.Fatalf("failed to dial backend: %v", err)
875 }
876 go func() {
877 _, err = io.Copy(bConn, conn)
878 if err != nil {
879 b.Fatalf("Failed to proxy incoming data to backend: %v", err)
880 }
881 bConn.(*net.TCPConn).CloseWrite()
882 }()
883 _, err = io.Copy(conn, bConn)
884 if err != nil {
885 b.Fatalf("Failed to proxy data from backend: %v", err)
886 }
887 conn.Close()
888 bConn.Close()
889 }
890 }()
891
892 data := make([]byte, size)
893
894 header := &Header{
895 Version: 2,
896 Command: PROXY,
897 TransportProtocol: TCPv4,
898 SourceAddr: &net.TCPAddr{
899 IP: net.ParseIP("10.1.1.1"),
900 Port: 1000,
901 },
902 DestinationAddr: &net.TCPAddr{
903 IP: net.ParseIP("20.2.2.2"),
904 Port: 2000,
905 },
906 }
907
908 //now for the actual benchmark
909 b.ResetTimer()
910 for n := 0; n < b.N; n++ {
911 conn, err := net.Dial("tcp", pl.Addr().String())
912 if err != nil {
913 b.Fatalf("err: %v", err)
914 }
915 // Write out the header!
916 header.WriteTo(conn)
917 //send data
918 go func() {
919 _, err = conn.Write(data)
920 if err != nil {
921 b.Fatalf("Failed to write data: %v", err)
922 }
923 conn.(*net.TCPConn).CloseWrite()
924
925 }()
926 //receive data
927 n, err := io.Copy(ioutil.Discard, conn)
928 if n != int64(len(data)) {
929 b.Fatalf("Expected to receive %d bytes, got %d", len(data), n)
930 }
931 if err != nil {
932 b.Fatalf("Failed to read data: %v", err)
933 }
934 conn.Close()
935 }
936 }
937
938 func BenchmarkTCPProxy16KB(b *testing.B) {
939 benchmarkTCPProxy(16*1024, b)
940 }
941 func BenchmarkTCPProxy32KB(b *testing.B) {
942 benchmarkTCPProxy(32*1024, b)
943 }
944 func BenchmarkTCPProxy64KB(b *testing.B) {
945 benchmarkTCPProxy(64*1024, b)
946 }
947 func BenchmarkTCPProxy128KB(b *testing.B) {
948 benchmarkTCPProxy(128*1024, b)
949 }
950 func BenchmarkTCPProxy256KB(b *testing.B) {
951 benchmarkTCPProxy(256*1024, b)
952 }
953 func BenchmarkTCPProxy512KB(b *testing.B) {
954 benchmarkTCPProxy(512*1024, b)
955 }
956 func BenchmarkTCPProxy1024KB(b *testing.B) {
957 benchmarkTCPProxy(1024*1024, b)
958 }
959 func BenchmarkTCPProxy2048KB(b *testing.B) {
960 benchmarkTCPProxy(2048*1024, b)
741961 }
742962
743963 // copied from src/net/http/internal/testcert.go
1515 PP2_TYPE_AUTHORITY PP2Type = 0x02
1616 PP2_TYPE_CRC32C PP2Type = 0x03
1717 PP2_TYPE_NOOP PP2Type = 0x04
18 PP2_TYPE_UNIQUE_ID PP2Type = 0x05
1819 PP2_TYPE_SSL PP2Type = 0x20
1920 PP2_SUBTYPE_SSL_VERSION PP2Type = 0x21
2021 PP2_SUBTYPE_SSL_CN PP2Type = 0x22
9697 PP2_TYPE_AUTHORITY,
9798 PP2_TYPE_CRC32C,
9899 PP2_TYPE_NOOP,
100 PP2_TYPE_UNIQUE_ID,
99101 PP2_TYPE_SSL,
100102 PP2_SUBTYPE_SSL_VERSION,
101103 PP2_SUBTYPE_SSL_CN,
9999
100100 func TestV2TLVPP2Registered(t *testing.T) {
101101 pp2RegTypes := []PP2Type{
102 PP2_TYPE_ALPN, PP2_TYPE_AUTHORITY, PP2_TYPE_CRC32C, PP2_TYPE_NOOP,
102 PP2_TYPE_ALPN, PP2_TYPE_AUTHORITY, PP2_TYPE_CRC32C, PP2_TYPE_NOOP, PP2_TYPE_UNIQUE_ID,
103103 PP2_TYPE_SSL, PP2_SUBTYPE_SSL_VERSION, PP2_SUBTYPE_SSL_CN,
104104 PP2_SUBTYPE_SSL_CIPHER, PP2_SUBTYPE_SSL_SIG_ALG, PP2_SUBTYPE_SSL_KEY_ALG,
105105 PP2_TYPE_NETNS,
22 import (
33 "bufio"
44 "bytes"
5 "fmt"
56 "net"
67 "strconv"
78 "strings"
2122 }
2223
2324 func parseVersion1(reader *bufio.Reader) (*Header, error) {
24 // Read until LF shows up, otherwise fail.
25 // At this point, can't be sure CR precedes LF which will be validated next.
26 line, err := reader.ReadString('\n')
27 if err != nil {
25 //The header cannot be more than 107 bytes long. Per spec:
26 //
27 // (...)
28 // - worst case (optional fields set to 0xff) :
29 // "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
30 // => 5 + 1 + 7 + 1 + 39 + 1 + 39 + 1 + 5 + 1 + 5 + 2 = 107 chars
31 //
32 // So a 108-byte buffer is always enough to store all the line and a
33 // trailing zero for string processing.
34 //
35 // It must also be CRLF terminated, as above. The header does not otherwise
36 // contain a CR or LF byte.
37 //
38 // ISSUE #69
39 // We can't use Peek here as it will block trying to fill the buffer, which
40 // will never happen if the header is TCP4 or TCP6 (max. 56 and 104 bytes
41 // respectively) and the server is expected to speak first.
42 //
43 // Similarly, we can't use ReadString or ReadBytes as these will keep reading
44 // until the delimiter is found; an abusive client could easily disrupt a
45 // server by sending a large amount of data that do not contain a LF byte.
46 // Another means of attack would be to start connections and simply not send
47 // data after the initial PROXY signature bytes, accumulating a large
48 // number of blocked goroutines on the server. ReadSlice will also block for
49 // a delimiter when the internal buffer does not fill up.
50 //
51 // A plain Read is also problematic since we risk reading past the end of the
52 // header without being able to easily put the excess bytes back into the reader's
53 // buffer (with the current implementation's design).
54 //
55 // So we use a ReadByte loop, which solves the overflow problem and avoids
56 // reading beyond the end of the header. However, we need one more trick to harden
57 // against partial header attacks (slow loris) - per spec:
58 //
59 // (..) The sender must always ensure that the header is sent at once, so that
60 // the transport layer maintains atomicity along the path to the receiver. The
61 // receiver may be tolerant to partial headers or may simply drop the connection
62 // when receiving a partial header. Recommendation is to be tolerant, but
63 // implementation constraints may not always easily permit this.
64 //
65 // We are subject to such implementation constraints. So we return an error if
66 // the header cannot be fully extracted with a single read of the underlying
67 // reader.
68 buf := make([]byte, 0, 107)
69 for {
70 b, err := reader.ReadByte()
71 if err != nil {
72 return nil, fmt.Errorf(ErrCantReadVersion1Header.Error()+": %v", err)
73 }
74 buf = append(buf, b)
75 if b == '\n' {
76 // End of header found
77 break
78 }
79 if len(buf) == 107 {
80 // No delimiter in first 107 bytes
81 return nil, ErrVersion1HeaderTooLong
82 }
83 if reader.Buffered() == 0 {
84 // Header was not buffered in a single read. Since we can't
85 // differentiate between genuine slow writers and DoS agents,
86 // we abort. On healthy networks, this should never happen.
87 return nil, ErrCantReadVersion1Header
88 }
89 }
90
91 // Check for CR before LF.
92 if len(buf) < 2 || buf[len(buf)-2] != '\r' {
2893 return nil, ErrLineMustEndWithCrlf
2994 }
30 if !strings.HasSuffix(line, crlf) {
31 return nil, ErrLineMustEndWithCrlf
32 }
95
3396 // Check full signature.
34 tokens := strings.Split(line[:len(line)-2], separator)
97 tokens := strings.Split(string(buf[:len(buf)-2]), separator)
3598
3699 // Expect at least 2 tokens: "PROXY" and the transport protocol.
37100 if len(tokens) < 2 {
22 import (
33 "bufio"
44 "bytes"
5 "io"
6 "net"
57 "strconv"
68 "strings"
79 "testing"
10 "time"
811 )
912
1013 var (
1114 IPv4AddressesAndPorts = strings.Join([]string{IP4_ADDR, IP4_ADDR, strconv.Itoa(PORT), strconv.Itoa(PORT)}, separator)
1215 IPv4AddressesAndInvalidPorts = strings.Join([]string{IP4_ADDR, IP4_ADDR, strconv.Itoa(INVALID_PORT), strconv.Itoa(INVALID_PORT)}, separator)
1316 IPv6AddressesAndPorts = strings.Join([]string{IP6_ADDR, IP6_ADDR, strconv.Itoa(PORT), strconv.Itoa(PORT)}, separator)
17 IPv6LongAddressesAndPorts = strings.Join([]string{IP6_LONG_ADDR, IP6_LONG_ADDR, strconv.Itoa(PORT), strconv.Itoa(PORT)}, separator)
1418
1519 fixtureTCP4V1 = "PROXY TCP4 " + IPv4AddressesAndPorts + crlf + "GET /"
1620 fixtureTCP6V1 = "PROXY TCP6 " + IPv6AddressesAndPorts + crlf + "GET /"
21
22 fixtureTCP6V1Overflow = "PROXY TCP6 " + IPv6LongAddressesAndPorts
1723
1824 fixtureUnknown = "PROXY UNKNOWN" + crlf
1925 fixtureUnknownWithAddresses = "PROXY UNKNOWN " + IPv4AddressesAndInvalidPorts + crlf
5763 {
5864 desc: "incomplete signature TCP4",
5965 reader: newBufioReader([]byte("PROXY TCP4 " + IPv4AddressesAndPorts)),
60 expectedError: ErrLineMustEndWithCrlf,
66 expectedError: ErrCantReadVersion1Header,
6167 },
6268 {
6369 desc: "TCP6 with IPv4 addresses",
7379 desc: "TCP4 with invalid port",
7480 reader: newBufioReader([]byte("PROXY TCP4 " + IPv4AddressesAndInvalidPorts + crlf)),
7581 expectedError: ErrInvalidPortNumber,
82 },
83 {
84 desc: "header too long",
85 reader: newBufioReader([]byte("PROXY UNKNOWN " + IPv6LongAddressesAndPorts + " " + crlf)),
86 expectedError: ErrVersion1HeaderTooLong,
7687 },
7788 }
7889
8091 for _, tt := range invalidParseV1Tests {
8192 t.Run(tt.desc, func(t *testing.T) {
8293 if _, err := Read(tt.reader); err != tt.expectedError {
83 t.Fatalf("expected %s, actual %s", tt.expectedError, err.Error())
94 t.Fatalf("expected %s, actual %v", tt.expectedError, err)
8495 }
8596 })
8697 }
174185 })
175186 }
176187 }
188
189 // Tests for parseVersion1 overflow - issue #69.
190
191 type dataSource struct {
192 NBytes int
193 NRead int
194 }
195
196 func (ds *dataSource) Read(b []byte) (int, error) {
197 if ds.NRead >= ds.NBytes {
198 return 0, io.EOF
199 }
200 avail := ds.NBytes - ds.NRead
201 if len(b) < avail {
202 avail = len(b)
203 }
204 for i := 0; i < avail; i++ {
205 b[i] = 0x20
206 }
207 ds.NRead += avail
208 return avail, nil
209 }
210
211 func TestParseVersion1Overflow(t *testing.T) {
212 ds := &dataSource{}
213 reader := bufio.NewReader(ds)
214 bufSize := reader.Size()
215 ds.NBytes = bufSize * 16
216 parseVersion1(reader)
217 if ds.NRead > bufSize {
218 t.Fatalf("read: expected max %d bytes, actual %d\n", bufSize, ds.NRead)
219 }
220 }
221
222 func listen(t *testing.T) *Listener {
223 l, err := net.Listen("tcp", "127.0.0.1:0")
224 if err != nil {
225 t.Fatalf("listen: %v", err)
226 }
227 return &Listener{Listener: l}
228 }
229
230 func client(t *testing.T, addr, header string, length int, terminate bool, wait time.Duration, done chan struct{}) {
231 c, err := net.Dial("tcp", addr)
232 if err != nil {
233 t.Fatalf("dial: %v", err)
234 }
235 defer c.Close()
236
237 if terminate && length < 2 {
238 length = 2
239 }
240
241 buf := make([]byte, len(header)+length)
242 copy(buf, []byte(header))
243 for i := 0; i < length-2; i++ {
244 buf[i+len(header)] = 0x20
245 }
246 if terminate {
247 copy(buf[len(header)+length-2:], []byte(crlf))
248 }
249
250 n, err := c.Write(buf)
251 if err != nil {
252 t.Fatalf("write: %v", err)
253 }
254 if n != len(buf) {
255 t.Fatalf("write; short write")
256 }
257
258 time.Sleep(wait)
259 close(done)
260 }
261
262 func TestVersion1Overflow(t *testing.T) {
263 done := make(chan struct{})
264
265 l := listen(t)
266 go client(t, l.Addr().String(), fixtureTCP6V1Overflow, 10240, true, 10*time.Second, done)
267
268 c, err := l.Accept()
269 if err != nil {
270 t.Fatalf("accept: %v", err)
271 }
272
273 b := []byte{}
274 _, err = c.Read(b)
275 if err == nil {
276 t.Fatalf("net.Conn: no error reported for oversized header")
277 }
278 }
279
280 func TestVersion1SlowLoris(t *testing.T) {
281 done := make(chan struct{})
282 timeout := make(chan error)
283
284 l := listen(t)
285 go client(t, l.Addr().String(), fixtureTCP6V1Overflow, 0, false, 10*time.Second, done)
286
287 c, err := l.Accept()
288 if err != nil {
289 t.Fatalf("accept: %v", err)
290 }
291
292 go func() {
293 b := []byte{}
294 _, err = c.Read(b)
295 timeout <- err
296 }()
297
298 select {
299 case <-done:
300 t.Fatalf("net.Conn: reader still blocked after 10 seconds")
301 case err := <-timeout:
302 if err == nil {
303 t.Fatalf("net.Conn: no error reported for incomplete header")
304 }
305 }
306 }
307
308 func TestVersion1SlowLorisOverflow(t *testing.T) {
309 done := make(chan struct{})
310 timeout := make(chan error)
311
312 l := listen(t)
313 go client(t, l.Addr().String(), fixtureTCP6V1Overflow, 10240, false, 10*time.Second, done)
314
315 c, err := l.Accept()
316 if err != nil {
317 t.Fatalf("accept: %v", err)
318 }
319
320 go func() {
321 b := []byte{}
322 _, err = c.Read(b)
323 timeout <- err
324 }()
325
326 select {
327 case <-done:
328 t.Fatalf("net.Conn: reader still blocked after 10 seconds")
329 case err := <-timeout:
330 if err == nil {
331 t.Fatalf("net.Conn: no error reported for incomplete and overflowed header")
332 }
333 }
334 }