Skip to content

Commit 89276a5

Browse files
authored
VAULT-6803: fix listener issue if using proxy_protocol_behavior with deny_unauthorized for untrusted upstream connections (#27589)
* timeout 'testListenerConnFn' waiting on the server connection after 3 secs * return the invalid upstream error so the library knows not to stop listening/serving * update go-proxyproto to use fork/tag * test that fails before library and code update, but passes afterwards
1 parent aa828f1 commit 89276a5

File tree

6 files changed

+90
-8
lines changed

6 files changed

+90
-8
lines changed

changelog/27589.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
```release-note:bug
2+
core/config: fix issue when using `proxy_protocol_behavior` with `deny_unauthorized`,
3+
which causes the Vault TCP listener to close after receiving an untrusted upstream proxy connection.
4+
```

command/server/listener_tcp_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,73 @@ func TestTCPListener_proxyProtocol(t *testing.T) {
432432
})
433433
}
434434
}
435+
436+
// TestTCPListener_proxyProtocol_keepAcceptingOnInvalidUpstream ensures that the server side listener
437+
// never returns an error from the listener.Accept method if the error is that the
438+
// upstream proxy isn't trusted. If an error is returned, underlying Go HTTP native
439+
// libraries may close down a server and stop listening.
440+
func TestTCPListener_proxyProtocol_keepAcceptingOnInvalidUpstream(t *testing.T) {
441+
timeout := 3 * time.Second
442+
443+
// Configure proxy so we hit the deny unauthorized behavior.
444+
header := &proxyproto.Header{
445+
Version: 1,
446+
Command: proxyproto.PROXY,
447+
TransportProtocol: proxyproto.TCPv4,
448+
SourceAddr: &net.TCPAddr{
449+
IP: net.ParseIP("10.1.1.1"),
450+
Port: 1000,
451+
},
452+
DestinationAddr: &net.TCPAddr{
453+
IP: net.ParseIP("20.2.2.2"),
454+
Port: 2000,
455+
},
456+
}
457+
458+
var authAddrs []*sockaddr.SockAddrMarshaler
459+
sockAddr, err := sockaddr.NewSockAddr("10.0.0.1/32")
460+
require.NoError(t, err)
461+
authAddrs = append(authAddrs, &sockaddr.SockAddrMarshaler{SockAddr: sockAddr})
462+
463+
ln, _, _, err := tcpListenerFactory(&configutil.Listener{
464+
Address: "127.0.0.1:0",
465+
TLSDisable: true,
466+
ProxyProtocolBehavior: "deny_unauthorized",
467+
ProxyProtocolAuthorizedAddrs: authAddrs,
468+
}, nil, cli.NewMockUi())
469+
require.NoError(t, err)
470+
471+
// Kick off setting up server side, if we ever accept a connection send it out
472+
// via a channel.
473+
serverConnCh := make(chan net.Conn, 1)
474+
go func() {
475+
serverConn, err := ln.Accept()
476+
// We shouldn't ever have an error if the problem was only that the upstream
477+
// proxy wasn't trusted.
478+
// An error would lead to the http.Serve closing the listener and giving up.
479+
require.NoError(t, err, "server side listener errored")
480+
serverConnCh <- serverConn
481+
}()
482+
483+
// Now try to connect as the client.
484+
d := net.Dialer{Timeout: timeout}
485+
clientConn, err := d.Dial("tcp", ln.Addr().String())
486+
require.NoError(t, err)
487+
defer clientConn.Close()
488+
_, err = header.WriteTo(clientConn)
489+
require.NoError(t, err)
490+
491+
// Wait for the server to have accepted a connection, or we time out.
492+
select {
493+
case <-time.After(timeout):
494+
// The server still hasn't accepted any valid client connection.
495+
// Try to write another header using the same connection which should have
496+
// been closed by the server, we expect that this client side connection was
497+
// closed as it us untrusted,
498+
_, err = header.WriteTo(clientConn)
499+
require.Error(t, err, "reused a rejected connection without error")
500+
case serverConn := <-serverConnCh:
501+
require.NotNil(t, serverConn)
502+
defer serverConn.Close()
503+
}
504+
}

command/server/listener_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io"
1010
"net"
1111
"testing"
12+
"time"
1213
)
1314

1415
type testListenerConnFn func(net.Listener) (net.Conn, error)
@@ -60,7 +61,11 @@ func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn,
6061
}
6162
}
6263

63-
server := <-serverCh
64+
var server net.Conn
65+
select {
66+
case <-time.After(3 * time.Second):
67+
case server = <-serverCh:
68+
}
6469

6570
if server == nil {
6671
if !expectError {

go.mod

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ require (
187187
github.com/ory/dockertest v3.3.5+incompatible
188188
github.com/ory/dockertest/v3 v3.10.0
189189
github.com/patrickmn/go-cache v2.1.0+incompatible
190-
github.com/pires/go-proxyproto v0.7.0
190+
github.com/pires/go-proxyproto v1.0.0
191191
github.com/pkg/errors v0.9.1
192192
github.com/posener/complete v1.2.3
193193
github.com/pquerna/otp v1.2.1-0.20191009055518-468c2dd2b58d
@@ -542,3 +542,7 @@ require (
542542
)
543543

544544
replace github.com/ma314smith/signedxml v1.1.1 => github.com/moov-io/signedxml v1.1.1
545+
546+
// Support using the forked repository until https://github.com/pires/go-proxyproto/pull/110 merges
547+
// and is released.
548+
replace github.com/pires/go-proxyproto v1.0.0 => github.com/peteski22/go-proxyproto v1.0.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,6 +1980,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
19801980
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
19811981
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
19821982
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
1983+
github.com/peteski22/go-proxyproto v1.0.0 h1:838NKdKEeViAMkaz08Pe+lvvAnGLYhZ0M0z246iCYv0=
1984+
github.com/peteski22/go-proxyproto v1.0.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
19831985
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
19841986
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
19851987
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
@@ -1988,8 +1990,6 @@ github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
19881990
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
19891991
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
19901992
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
1991-
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
1992-
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
19931993
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
19941994
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
19951995
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=

helper/proxyutil/proxyutil.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
package proxyutil
55

66
import (
7-
"errors"
87
"fmt"
98
"net"
109
"sync"
1110
"time"
1211

1312
"github.com/hashicorp/go-secure-stdlib/parseutil"
14-
sockaddr "github.com/hashicorp/go-sockaddr"
15-
proxyproto "github.com/pires/go-proxyproto"
13+
"github.com/hashicorp/go-sockaddr"
14+
"github.com/pires/go-proxyproto"
1615
)
1716

1817
// ProxyProtoConfig contains configuration for the PROXY protocol
@@ -72,7 +71,7 @@ func WrapInProxyProto(listener net.Listener, config *ProxyProtoConfig) (net.List
7271
return proxyproto.IGNORE, nil
7372
}
7473

75-
return proxyproto.REJECT, errors.New(`upstream connection not trusted proxy_protocol_behavior is "deny_unauthorized"`)
74+
return proxyproto.REJECT, proxyproto.ErrInvalidUpstream
7675
},
7776
}
7877
default:

0 commit comments

Comments
 (0)