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
38 changes: 37 additions & 1 deletion lib/web/app/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,22 @@ func (t *transport) DialContext(ctx context.Context, _, _ string) (conn net.Conn
appIntegration := appServer.GetApp().GetIntegration()
if appIntegration != "" {
src, dst := net.Pipe()
go t.c.integrationAppHandler.HandleConnection(src)

// Creating the connection using `net.Pipe()` results in both ends having the same local/remote address: "pipe".
// This causes IP Pinning checks to fail when validating that the connection remote address matches the certificate pinned IP.
//
// To fix this, we need to wrap the `src` connection to return the client source address as the remote address.
// The client source address is extracted from the context, the same way as in `dialAppServer`.
srcWithClientSrcAddr, err := wrapConnWithClientSrcAddrFromContext(ctx, src)
if err != nil {
// Log a warning and use the original remote address if we fail to extract the client source address from context.
// This will result in an error if the flow has pinned IP restrictions.
t.c.log.WithFields(logrus.Fields{"app_server": appServer.GetName()}).
Warnf("Failed to extract client source address from context when proxying access to Application with integration credentials. IP Pinning checks will fail. error: %v", err)
srcWithClientSrcAddr = src
}

go t.c.integrationAppHandler.HandleConnection(srcWithClientSrcAddr)
return dst, nil
}

Expand All @@ -387,6 +402,27 @@ func (t *transport) DialContext(ctx context.Context, _, _ string) (conn net.Conn
return nil, trace.ConnectionProblem(nil, "no application servers remaining to connect")
}

type connWithCustomRemoteAddr struct {
net.Conn
remoteAddr net.Addr
}

func (w *connWithCustomRemoteAddr) RemoteAddr() net.Addr {
return w.remoteAddr
}

func wrapConnWithClientSrcAddrFromContext(ctx context.Context, conn net.Conn) (net.Conn, error) {
clientSrcAddr, err := authz.ClientSrcAddrFromContext(ctx)
if err != nil {
return nil, trace.Wrap(err)
}

return &connWithCustomRemoteAddr{
Conn: conn,
remoteAddr: clientSrcAddr,
}, nil
}

// DialWebsocket dials a websocket connection over the transport's reverse
// tunnel.
func (t *transport) DialWebsocket(network, address string) (net.Conn, error) {
Expand Down
10 changes: 9 additions & 1 deletion lib/web/app/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/gravitational/teleport/api/types"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/cryptosuites"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/jwt"
Expand Down Expand Up @@ -493,13 +494,20 @@ func Test_transport_with_integration(t *testing.T) {
})
require.NoError(t, err)

conn, err := tr.DialContext(context.Background(), "", "")
ctxWithClientSrcAddr := authz.ContextWithClientSrcAddr(t.Context(), &utils.NetAddr{
AddrNetwork: "tcp",
Addr: net.JoinHostPort("127.0.0.1", "55555"),
})

conn, err := tr.DialContext(ctxWithClientSrcAddr, "", "")
require.NoError(t, err)

require.Eventually(t, func() bool {
return integrationAppHandler.getConnection() != nil
}, 100*time.Millisecond, 10*time.Millisecond)

require.Equal(t, "127.0.0.1:55555", integrationAppHandler.getConnection().RemoteAddr().String())

message := "hello world"
messageSize := len(message)

Expand Down
Loading