Skip to content

Commit

Permalink
MEN-5273: proxy: Fix websocket connection for advanced auth settings
Browse files Browse the repository at this point in the history
By switching to the "enhanced" API for websocket.Dialer from
mendersoftware's fork.

There is a limitation in current gorilla/websocket.Dialer API in that
the user cannot specify a dial method for TLS/TCP connections. The TLS
handshake is always done by the library based on user's TLSClientConfig,
but that is not enough for Mender as we need it to be done via OpenSSL
(aka our dial wrapper for TLS) so that advance auth features like
getting the keys from HSM.

This commit switches to mendersoftware's fork and modifies the code
accordingly (one line change!).

The patch has been submitted upstream. See:
* gorilla/websocket#745
* gorilla/websocket#746

Changelog: None
No changelog, commit 84204a3 claims to support websockets, this commit
just fixes a bug there which has not been released.

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>
  • Loading branch information
lluiscampos committed Dec 10, 2021
1 parent 0a1882f commit b657deb
Show file tree
Hide file tree
Showing 16 changed files with 206 additions and 33 deletions.
2 changes: 1 addition & 1 deletion app/proxy/proxy_ws.go
Expand Up @@ -84,7 +84,7 @@ func (pc *proxyControllerInner) DoWsUpgrade(w http.ResponseWriter, r *http.Reque

connBackend, resp, err := pc.wsDialer.Dial(wsUrl.String(), requestHeader)
if err != nil {
log.Errorf("couldn't dial to remote backend url %s", err)
log.Errorf("couldn't dial to remote backend url %q, err: %s", wsUrl.String(), err.Error())
if resp != nil {
// WebSocket handshake failed, reply the client with backend's resp
if err := copyResponse(w, resp); err != nil {
Expand Down
153 changes: 139 additions & 14 deletions app/proxy/proxy_ws_test.go
Expand Up @@ -14,6 +14,9 @@
package proxy

import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"reflect"
"runtime"
Expand All @@ -26,21 +29,33 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/mendersoftware/mender/client"
cltest "github.com/mendersoftware/mender/client/test"
"github.com/mendersoftware/mender/conf"
)

func prepareProxyWsTest(
t *testing.T,
srv *cltest.ClientTestWsServer,
) (*ProxyController, *websocket.Conn) {
func prepareProxyWsTest(t *testing.T, srv *cltest.ClientTestWsServer) *ProxyController {

wsDialer, err := client.NewWebsocketDialer(client.Config{})
require.NoError(t, err)

proxyController, err := NewProxyController(
&http.Client{},
nil,
wsDialer,
srv.TestServer.URL,
"SecretJwtToken",
)
require.NoError(t, err)

return proxyController
}

func connectProxyWsTest(
t *testing.T,
srv *cltest.ClientTestWsServer,
proxyController *ProxyController,
) *websocket.Conn {

proxyServerUrl := proxyController.GetServerUrl()
require.Contains(t, proxyServerUrl, "http://localhost")

Expand All @@ -51,14 +66,25 @@ func prepareProxyWsTest(
require.NoError(t, err)
require.Equal(t, http.StatusSwitchingProtocols, resp.StatusCode)

return proxyController, conn
return conn
}

func TestProxyWsConnect(t *testing.T) {
srv := cltest.NewClientTestWsServer()
defer srv.StopWs()
defer srv.Close()
func prepareAndConnectProxyWsTest(
t *testing.T,
srv *cltest.ClientTestWsServer,
) (*ProxyController, *websocket.Conn) {

proxyController := prepareProxyWsTest(t, srv)
conn := connectProxyWsTest(t, srv, proxyController)

return proxyController, conn
}

func runTestSendReceiveWs(
t *testing.T,
srv *cltest.ClientTestWsServer,
proxyController *ProxyController,
) {
// Expectations for the test
srv.Connect.SendMessages = append(
srv.Connect.SendMessages,
Expand All @@ -82,8 +108,8 @@ func TestProxyWsConnect(t *testing.T) {
{MsgType: websocket.TextMessage, Msg: []byte("hello-world")},
}

proxyController, conn := prepareProxyWsTest(t, srv)
defer proxyController.Stop()
conn := connectProxyWsTest(t, srv, proxyController)

defer conn.Close()

wg := sync.WaitGroup{}
Expand Down Expand Up @@ -150,6 +176,17 @@ func TestProxyWsConnect(t *testing.T) {
)
}

func TestProxyWsConnect(t *testing.T) {
srv := cltest.NewClientTestWsServer()
defer srv.StopWs()
defer srv.Close()

proxyController := prepareProxyWsTest(t, srv)
defer proxyController.Stop()

runTestSendReceiveWs(t, srv, proxyController)
}

func TestProxyWsWebSocketProtocolHeader(t *testing.T) {
srv := cltest.NewClientTestWsServer()
defer srv.StopWs()
Expand Down Expand Up @@ -195,7 +232,7 @@ func TestProxyWsTooMany(t *testing.T) {
defer srv.StopWs()
defer srv.Close()

proxyController, conn := prepareProxyWsTest(t, srv)
proxyController, conn := prepareAndConnectProxyWsTest(t, srv)
defer proxyController.Stop()
defer conn.Close()

Expand All @@ -218,7 +255,7 @@ func TestProxyWsStop(t *testing.T) {
defer srv.StopWs()
defer srv.Close()

proxyController, conn := prepareProxyWsTest(t, srv)
proxyController, conn := prepareAndConnectProxyWsTest(t, srv)
defer proxyController.Stop()
defer conn.Close()

Expand Down Expand Up @@ -309,3 +346,91 @@ func TestProxyWsGoroutines(t *testing.T) {
1*time.Millisecond,
)
}

func TestProxyWsConnectCustomCert(t *testing.T) {
serverCert, err := tls.LoadX509KeyPair(
"../../client/test/server.crt",
"../../client/test/server.key",
)
require.NoError(t, err)

tc := tls.Config{
Certificates: []tls.Certificate{serverCert},
}

srv := cltest.NewClientTestWsServer(&tc)
defer srv.StopWs()
defer srv.Close()

conffromfile := conf.MenderConfigFromFile{
ServerCertificate: "../../client/test/server.crt",
}
testconf := &conf.MenderConfig{MenderConfigFromFile: conffromfile}
httpConfig := testconf.GetHttpConfig()

api, err := client.New(httpConfig)
require.NoError(t, err)

wsDialer, err := client.NewWebsocketDialer(httpConfig)
require.NoError(t, err)

proxyController, err := NewProxyController(
api,
wsDialer,
srv.TestServer.URL,
"SecretJwtToken",
)
require.NoError(t, err)
defer proxyController.Stop()

runTestSendReceiveWs(t, srv, proxyController)
}
func TestProxyWsConnectMutualTLS(t *testing.T) {
serverCert, err := tls.LoadX509KeyPair(
"../../client/test/server.crt",
"../../client/test/server.key",
)
require.NoError(t, err)

clientClientCertPool := x509.NewCertPool()
pb, err := ioutil.ReadFile("../../client/testdata/client.crt")
require.NoError(t, err)
clientClientCertPool.AppendCertsFromPEM(pb)

tc := tls.Config{
Certificates: []tls.Certificate{serverCert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: clientClientCertPool,
}

srv := cltest.NewClientTestWsServer(&tc)
defer srv.StopWs()
defer srv.Close()

conffromfile := conf.MenderConfigFromFile{
ServerCertificate: "../../client/test/server.crt",
HttpsClient: client.HttpsClient{
Certificate: "../../client/testdata/client.crt",
Key: "../../client/testdata/client-cert.key",
},
}
testconf := &conf.MenderConfig{MenderConfigFromFile: conffromfile}
httpConfig := testconf.GetHttpConfig()

api, err := client.New(httpConfig)
require.NoError(t, err)

wsDialer, err := client.NewWebsocketDialer(httpConfig)
require.NoError(t, err)

proxyController, err := NewProxyController(
api,
wsDialer,
srv.TestServer.URL,
"SecretJwtToken",
)
require.NoError(t, err)
defer proxyController.Stop()

runTestSendReceiveWs(t, srv, proxyController)
}
5 changes: 2 additions & 3 deletions client/client.go
Expand Up @@ -489,8 +489,7 @@ func loadClientTrust(ctx *openssl.Ctx, conf *Config) (*openssl.Ctx, error) {
return ctx, nil
}

func dialOpenSSL(ctx *openssl.Ctx, conf *Config, network string, addr string) (net.Conn, error) {

func dialOpenSSL(ctx *openssl.Ctx, conf *Config, _ string, addr string) (net.Conn, error) {
flags := openssl.DialFlags(0)

if conf.NoVerify {
Expand Down Expand Up @@ -694,7 +693,7 @@ func newWebsocketDialerTLS(conf Config) (*websocket.Dialer, error) {
}

dialer := websocket.Dialer{
NetDial: func(network string, addr string) (net.Conn, error) {
NetDialTLS: func(network string, addr string) (net.Conn, error) {
return dialOpenSSL(ctx, &conf, network, addr)
},
}
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Expand Up @@ -21,3 +21,5 @@ require (
)

replace github.com/urfave/cli/v2 => github.com/mendersoftware/cli/v2 v2.1.1-minimal

replace github.com/gorilla/websocket => github.com/mendersoftware/websocket v1.4.3-0.20211210145825-8a45e5d03918
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -10,8 +10,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc=
github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
Expand All @@ -34,6 +32,8 @@ github.com/mendersoftware/openssl v1.1.0 h1:eRiG3CwzkMIna1xrTE9SiX9lrsme9irlb6i5
github.com/mendersoftware/openssl v1.1.0/go.mod h1:tikEC94q+Y0TU6r19L6mHzwruoTNYPEkrQPvsHEcQyU=
github.com/mendersoftware/progressbar v0.0.3 h1:AUdBGPvXO0l9i39rmXKZbEAPet2FzBeiG8b30D5/2Vc=
github.com/mendersoftware/progressbar v0.0.3/go.mod h1:NYaLNLhy3UXkRweGjhR3We3Q1ngmUmOWjC3+m8EzwjE=
github.com/mendersoftware/websocket v1.4.3-0.20211210145825-8a45e5d03918 h1:bxs2j1011PQiBPAP127cmBdAnw+zzq65tWOUeCFxVXU=
github.com/mendersoftware/websocket v1.4.3-0.20211210145825-8a45e5d03918/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand Down
58 changes: 48 additions & 10 deletions vendor/github.com/gorilla/websocket/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/gorilla/websocket/client_clone.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/gorilla/websocket/client_clone_legacy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/gorilla/websocket/conn_write.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vendor/github.com/gorilla/websocket/conn_write_legacy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b657deb

Please sign in to comment.