Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/13540.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:improvement
core: Vault now supports the PROXY protocol v2. Support for UNKNOWN connections
has also been added to the PROXY protocol v1.
```
263 changes: 257 additions & 6 deletions command/server/listener_tcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
"testing"
"time"

"github.com/hashicorp/go-sockaddr"
"github.com/hashicorp/vault/internalshared/configutil"
"github.com/mitchellh/cli"
"github.com/pires/go-proxyproto"
)

func TestTCPListener(t *testing.T) {
Expand All @@ -28,7 +30,7 @@ func TestTCPListener(t *testing.T) {
return net.Dial("tcp", ln.Addr().String())
}

testListenerImpl(t, ln, connFn, "", 0)
testListenerImpl(t, ln, connFn, "", 0, "127.0.0.1", false)
}

// TestTCPListener_tls tests TLS generally
Expand Down Expand Up @@ -85,7 +87,7 @@ func TestTCPListener_tls(t *testing.T) {
}
}

testListenerImpl(t, ln, connFn(true), "foo.example.com", 0)
testListenerImpl(t, ln, connFn(true), "foo.example.com", 0, "127.0.0.1", false)

ln, _, _, err = tcpListenerFactory(&configutil.Listener{
Address: "127.0.0.1:0",
Expand All @@ -110,7 +112,7 @@ func TestTCPListener_tls(t *testing.T) {
t.Fatalf("err: %s", err)
}

testListenerImpl(t, ln, connFn(false), "foo.example.com", 0)
testListenerImpl(t, ln, connFn(false), "foo.example.com", 0, "127.0.0.1", false)
}

func TestTCPListener_tls13(t *testing.T) {
Expand Down Expand Up @@ -167,7 +169,7 @@ func TestTCPListener_tls13(t *testing.T) {
}
}

testListenerImpl(t, ln, connFn(true), "foo.example.com", tls.VersionTLS13)
testListenerImpl(t, ln, connFn(true), "foo.example.com", tls.VersionTLS13, "127.0.0.1", false)

ln, _, _, err = tcpListenerFactory(&configutil.Listener{
Address: "127.0.0.1:0",
Expand All @@ -194,7 +196,7 @@ func TestTCPListener_tls13(t *testing.T) {
t.Fatalf("err: %s", err)
}

testListenerImpl(t, ln, connFn(false), "foo.example.com", tls.VersionTLS13)
testListenerImpl(t, ln, connFn(false), "foo.example.com", tls.VersionTLS13, "127.0.0.1", false)

ln, _, _, err = tcpListenerFactory(&configutil.Listener{
Address: "127.0.0.1:0",
Expand All @@ -208,5 +210,254 @@ func TestTCPListener_tls13(t *testing.T) {
t.Fatalf("err: %s", err)
}

testListenerImpl(t, ln, connFn(false), "foo.example.com", tls.VersionTLS12)
testListenerImpl(t, ln, connFn(false), "foo.example.com", tls.VersionTLS12, "127.0.0.1", false)
}

func TestTCPListener_proxyProtocol(t *testing.T) {
for name, tc := range map[string]struct {
Behavior string
Header *proxyproto.Header
AuthorizedAddr string
ExpectedAddr string
ExpectError bool
}{
"none-no-header": {
Behavior: "",
ExpectedAddr: "127.0.0.1",
Header: nil,
},
"none-v1": {
Behavior: "",
ExpectedAddr: "127.0.0.1",
ExpectError: true,
Header: &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},
"none-v2": {
Behavior: "",
ExpectedAddr: "127.0.0.1",
ExpectError: true,
Header: &proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},

// use_always makes it possible to send the PROXY header but does not
// require it
"use_always-no-header": {
Behavior: "use_always",
ExpectedAddr: "127.0.0.1",
Header: nil,
},

"use_always-header-v1": {
Behavior: "use_always",
ExpectedAddr: "10.1.1.1",
Header: &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},
"use_always-header-v1-unknown": {
Behavior: "use_always",
ExpectedAddr: "127.0.0.1",
Header: &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.UNSPEC,
},
},
"use_always-header-v2": {
Behavior: "use_always",
ExpectedAddr: "10.1.1.1",
Header: &proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},
"use_always-header-v2-unknown": {
Behavior: "use_always",
ExpectedAddr: "127.0.0.1",
Header: &proxyproto.Header{
Version: 2,
Command: proxyproto.LOCAL,
TransportProtocol: proxyproto.UNSPEC,
},
},
"allow_authorized-no-header-in": {
Behavior: "allow_authorized",
AuthorizedAddr: "127.0.0.1/32",
ExpectedAddr: "127.0.0.1",
},
"allow_authorized-no-header-not-in": {
Behavior: "allow_authorized",
AuthorizedAddr: "10.0.0.1/32",
ExpectedAddr: "127.0.0.1",
},
"allow_authorized-v1-in": {
Behavior: "allow_authorized",
AuthorizedAddr: "127.0.0.1/32",
ExpectedAddr: "10.1.1.1",
Header: &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},

// allow_authorized still accepts the PROXY header when not in the
// authorized addresses but discards it silently
"allow_authorized-v1-not-in": {
Behavior: "allow_authorized",
AuthorizedAddr: "10.0.0.1/32",
ExpectedAddr: "127.0.0.1",
Header: &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},

"deny_unauthorized-no-header-in": {
Behavior: "deny_unauthorized",
AuthorizedAddr: "127.0.0.1/32",
ExpectedAddr: "127.0.0.1",
},
"deny_unauthorized-no-header-not-in": {
Behavior: "deny_unauthorized",
AuthorizedAddr: "10.0.0.1/32",
ExpectedAddr: "127.0.0.1",
ExpectError: true,
},
"deny_unauthorized-v1-in": {
Behavior: "deny_unauthorized",
AuthorizedAddr: "127.0.0.1/32",
ExpectedAddr: "10.1.1.1",
Header: &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},
"deny_unauthorized-v1-not-in": {
Behavior: "deny_unauthorized",
AuthorizedAddr: "10.0.0.1/32",
ExpectedAddr: "127.0.0.1",
ExpectError: true,
Header: &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
},
},
} {
t.Run(name, func(t *testing.T) {
proxyProtocolAuthorizedAddrs := []*sockaddr.SockAddrMarshaler{}
if tc.AuthorizedAddr != "" {
sockAddr, err := sockaddr.NewSockAddr(tc.AuthorizedAddr)
if err != nil {
t.Fatal(err)
}
proxyProtocolAuthorizedAddrs = append(
proxyProtocolAuthorizedAddrs,
&sockaddr.SockAddrMarshaler{SockAddr: sockAddr},
)
}

ln, _, _, err := tcpListenerFactory(&configutil.Listener{
Address: "127.0.0.1:0",
TLSDisable: true,
ProxyProtocolBehavior: tc.Behavior,
ProxyProtocolAuthorizedAddrs: proxyProtocolAuthorizedAddrs,
}, nil, cli.NewMockUi())
if err != nil {
t.Fatalf("err: %s", err)
}

connFn := func(lnReal net.Listener) (net.Conn, error) {
conn, err := net.Dial("tcp", ln.Addr().String())
if err != nil {
return nil, err
}

if tc.Header != nil {
_, err = tc.Header.WriteTo(conn)
}
return conn, err
}

testListenerImpl(t, ln, connFn, "", 0, tc.ExpectedAddr, tc.ExpectError)
})
}
}
27 changes: 23 additions & 4 deletions command/server/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,29 @@ import (

type testListenerConnFn func(net.Listener) (net.Conn, error)

func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn, certName string, expectedVersion uint16) {
func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn, certName string, expectedVersion uint16, expectedAddr string, expectError bool) {
serverCh := make(chan net.Conn, 1)
go func() {
server, err := ln.Accept()
if err != nil {
t.Errorf("err: %s", err)
if !expectError {
t.Errorf("err: %s", err)
}
close(serverCh)
return
}
if certName != "" {
tlsConn := server.(*tls.Conn)
tlsConn.Handshake()
}
serverCh <- server
addr, _, err := net.SplitHostPort(server.RemoteAddr().String())
if err != nil {
t.Error(err)
}
if addr != expectedAddr {
t.Errorf("expected: %s, got: %s", expectedAddr, addr)
}
}()

client, err := connFn(ln)
Expand All @@ -45,6 +55,15 @@ func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn,
}

server := <-serverCh

if server == nil {
if !expectError {
// Something failed already so we abort the test early
t.Fatal("aborting test because the server did not accept the connection")
}
return
}

defer client.Close()
defer server.Close()

Expand All @@ -62,8 +81,8 @@ func testListenerImpl(t *testing.T, ln net.Listener, connFn testListenerConnFn,
client.Close()

<-copyCh
if buf.String() != "foo" {
t.Fatalf("bad: %v", buf.String())
if (buf.String() != "foo" && !expectError) || (buf.String() == "foo" && expectError) {
t.Fatalf("bad: %q, expectError: %t", buf.String(), expectError)
}
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ require (
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5
github.com/apple/foundationdb/bindings/go v0.0.0-20190411004307-cd5c9d91fad2
github.com/armon/go-metrics v0.3.10
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a
github.com/armon/go-radix v1.0.0
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/aws/aws-sdk-go v1.37.19
Expand Down Expand Up @@ -151,6 +150,7 @@ require (
github.com/ory/dockertest v3.3.5+incompatible
github.com/ory/dockertest/v3 v3.8.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pires/go-proxyproto v0.6.1
github.com/pkg/errors v0.9.1
github.com/posener/complete v1.2.3
github.com/pquerna/otp v1.2.1-0.20191009055518-468c2dd2b58d
Expand Down
Loading