diff --git a/server/opts.go b/server/opts.go index 0869418a..bed56494 100644 --- a/server/opts.go +++ b/server/opts.go @@ -281,6 +281,15 @@ type WebsocketOpts struct { // Timeout for the authentication process. AuthTimeout float64 + // By default the server will enforce the use of TLS. If no TLS configuration + // is provided, you need to explicitly set NoTLS to true to allow the server + // to start without TLS configuration. Note that if a TLS configuration is + // present, this boolean is ignored and the server will run the Websocket + // server with that TLS configuration. + // Running without TLS is less secure since Websocket clients that use bearer + // tokens will send them in clear. So this should not be used in production. + NoTLS bool + // TLS configuration is required. TLSConfig *tls.Config // If true, map certificate values for authentication purposes. @@ -3070,6 +3079,8 @@ func parseWebsocket(v interface{}, o *Options, errors *[]error, warnings *[]erro o.Websocket.Host = mv.(string) case "advertise": o.Websocket.Advertise = mv.(string) + case "no_tls": + o.Websocket.NoTLS = mv.(bool) case "tls": tc, err := parseTLS(tk) if err != nil { diff --git a/server/server_test.go b/server/server_test.go index e2f4bddd..bb8254e6 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1229,9 +1229,6 @@ func TestAcceptError(t *testing.T) { } func TestAcceptLoopsDoNotLeaveOpenedConn(t *testing.T) { - testWebsocketAllowNonTLS = true - defer func() { testWebsocketAllowNonTLS = false }() - for _, test := range []struct { name string url func(o *Options) (string, int) @@ -1258,6 +1255,7 @@ func TestAcceptLoopsDoNotLeaveOpenedConn(t *testing.T) { o.Websocket.Host = "127.0.0.1" o.Websocket.Port = -1 o.Websocket.HandshakeTimeout = 1 + o.Websocket.NoTLS = true s := RunServer(o) defer s.Shutdown() @@ -1321,9 +1319,6 @@ func TestAcceptLoopsDoNotLeaveOpenedConn(t *testing.T) { } func TestServerShutdownDuringStart(t *testing.T) { - testWebsocketAllowNonTLS = true - defer func() { testWebsocketAllowNonTLS = false }() - o := DefaultOptions() o.DisableShortFirstPing = true o.Accounts = []*Account{NewAccount("$SYS")} @@ -1339,6 +1334,7 @@ func TestServerShutdownDuringStart(t *testing.T) { o.Websocket.Host = "127.0.0.1" o.Websocket.Port = -1 o.Websocket.HandshakeTimeout = 1 + o.Websocket.NoTLS = true // We are going to test that if the server is shutdown // while Start() runs (in this case, before), we don't diff --git a/server/websocket.go b/server/websocket.go index 54de84a5..3e877395 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -82,9 +82,6 @@ var decompressorPool sync.Pool // From https://tools.ietf.org/html/rfc6455#section-1.3 var wsGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") -// Can be set for tests -var testWebsocketAllowNonTLS = false - type websocket struct { frames net.Buffers fs int64 @@ -734,8 +731,8 @@ func validateWebsocketOptions(o *Options) error { if wo.Port == 0 { return nil } - // Enforce TLS... - if !testWebsocketAllowNonTLS && wo.TLSConfig == nil { + // Enforce TLS... unless NoTLS is set to true. + if wo.TLSConfig == nil && !wo.NoTLS { return errors.New("websocket requires TLS configuration") } // Make sure that allowed origins, if specified, can be parsed. @@ -840,6 +837,10 @@ func (s *Server) startWebsocketServer() { s.mu.Unlock() return } + // Do not check o.NoTLS here. If a TLS configuration is available, use it, + // regardless of NoTLS. If we don't have a TLS config, it means that the + // user has configured NoTLS because otherwise the server would have failed + // to start due to options validation. if o.TLSConfig != nil { proto = "wss" config := o.TLSConfig.Clone() @@ -854,6 +855,9 @@ func (s *Server) startWebsocketServer() { return } s.Noticef("Listening for websocket clients on %s://%s:%d", proto, o.Host, port) + if proto == "ws" { + s.Warnf("Websocket not configured with TLS. DO NOT USE IN PRODUCTION!") + } s.websocket.tls = proto == "wss" if port == 0 {