Skip to content

Commit 6c33673

Browse files
authored
[FIXED/CHANGED] Reject server/cluster/gateway names with spaces (#5676)
Having cluster name with spaces in the context of a leaf node could cause problems when a subscription on the leaf node would be propagated in the cluster's hub. Since when a gateway is specified the name of the gateway needs to match the name of the cluster, the restriction of "no spaces" is added to gateway name. Finally, in the case of the leafnode, if no cluster name is specified the server name is used, so also applying the restriction to the server name. Resolves #5633 Signed-off-by: Ivan Kozlovic <[email protected]>
2 parents fd284c8 + 7a15dea commit 6c33673

File tree

8 files changed

+143
-16
lines changed

8 files changed

+143
-16
lines changed

server/errors.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ var (
153153
// Gateway's name.
154154
ErrWrongGateway = errors.New("wrong gateway")
155155

156+
// ErrGatewayNameHasSpaces signals that the gateway name contains spaces, which is not allowed.
157+
ErrGatewayNameHasSpaces = errors.New("gateway name cannot contain spaces")
158+
156159
// ErrNoSysAccount is returned when an attempt to publish or subscribe is made
157160
// when there is no internal system account defined.
158161
ErrNoSysAccount = errors.New("system account not setup")
@@ -163,6 +166,9 @@ var (
163166
// ErrServerNotRunning is used to signal an error that a server is not running.
164167
ErrServerNotRunning = errors.New("server is not running")
165168

169+
// ErrServerNameHasSpaces signals that the server name contains spaces, which is not allowed.
170+
ErrServerNameHasSpaces = errors.New("server name cannot contain spaces")
171+
166172
// ErrBadMsgHeader signals the parser detected a bad message header
167173
ErrBadMsgHeader = errors.New("bad message header detected")
168174

@@ -180,6 +186,9 @@ var (
180186
// ErrClusterNameRemoteConflict signals that a remote server has a different cluster name.
181187
ErrClusterNameRemoteConflict = errors.New("cluster name from remote server conflicts")
182188

189+
// ErrClusterNameHasSpaces signals that the cluster name contains spaces, which is not allowed.
190+
ErrClusterNameHasSpaces = errors.New("cluster name cannot contain spaces")
191+
183192
// ErrMalformedSubject is returned when a subscription is made with a subject that does not conform to subject rules.
184193
ErrMalformedSubject = errors.New("malformed subject")
185194

server/events_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@ func runTrustedCluster(t *testing.T) (*Server, *Options, *Server, *Options, nkey
103103
mr.Store(apub, jwt)
104104

105105
optsA := DefaultOptions()
106-
optsA.Cluster.Name = "TEST CLUSTER 22"
106+
optsA.Cluster.Name = "TEST_CLUSTER_22"
107107
optsA.Cluster.Host = "127.0.0.1"
108108
optsA.TrustedKeys = []string{pub}
109109
optsA.AccountResolver = mr
110110
optsA.SystemAccount = apub
111111
optsA.ServerName = "A_SRV"
112112
// Add in dummy gateway
113-
optsA.Gateway.Name = "TEST CLUSTER 22"
113+
optsA.Gateway.Name = "TEST_CLUSTER_22"
114114
optsA.Gateway.Host = "127.0.0.1"
115115
optsA.Gateway.Port = -1
116116
optsA.gatewaysSolicitDelay = 30 * time.Second
@@ -1876,7 +1876,7 @@ func TestServerEventsStatsZ(t *testing.T) {
18761876
if m.Server.ID != sa.ID() {
18771877
t.Fatalf("Did not match IDs")
18781878
}
1879-
if m.Server.Cluster != "TEST CLUSTER 22" {
1879+
if m.Server.Cluster != "TEST_CLUSTER_22" {
18801880
t.Fatalf("Did not match cluster name")
18811881
}
18821882
if m.Server.Version != VERSION {
@@ -2978,11 +2978,11 @@ func TestServerEventsPingMonitorz(t *testing.T) {
29782978
[]string{"now", "routes"}},
29792979
{"ROUTEZ", json.RawMessage(`{"name":""}`), &Routez{},
29802980
[]string{"now", "routes"}},
2981-
{"ROUTEZ", json.RawMessage(`{"cluster":"TEST CLUSTER 22"}`), &Routez{},
2981+
{"ROUTEZ", json.RawMessage(`{"cluster":"TEST_CLUSTER_22"}`), &Routez{},
29822982
[]string{"now", "routes"}},
29832983
{"ROUTEZ", json.RawMessage(`{"cluster":"CLUSTER"}`), &Routez{},
29842984
[]string{"now", "routes"}},
2985-
{"ROUTEZ", json.RawMessage(`{"cluster":"TEST CLUSTER 22", "subscriptions":true}`), &Routez{},
2985+
{"ROUTEZ", json.RawMessage(`{"cluster":"TEST_CLUSTER_22", "subscriptions":true}`), &Routez{},
29862986
[]string{"now", "routes"}},
29872987

29882988
{"JSZ", nil, &JSzOptions{}, []string{"now", "disabled"}},
@@ -3083,8 +3083,8 @@ func TestGatewayNameClientInfo(t *testing.T) {
30833083
if err != nil {
30843084
t.Fatalf("Could not parse INFO json: %v\n", err)
30853085
}
3086-
if info.Cluster != "TEST CLUSTER 22" {
3087-
t.Fatalf("Expected a cluster name of 'TEST CLUSTER 22', got %q", info.Cluster)
3086+
if info.Cluster != "TEST_CLUSTER_22" {
3087+
t.Fatalf("Expected a cluster name of 'TEST_CLUSTER_22', got %q", info.Cluster)
30883088
}
30893089
}
30903090

server/gateway.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ import (
1818
"crypto/sha256"
1919
"crypto/tls"
2020
"encoding/json"
21+
"errors"
2122
"fmt"
2223
"math/rand"
2324
"net"
2425
"net/url"
2526
"sort"
2627
"strconv"
28+
"strings"
2729
"sync"
2830
"sync/atomic"
2931
"time"
@@ -299,17 +301,20 @@ func (r *RemoteGatewayOpts) clone() *RemoteGatewayOpts {
299301

300302
// Ensure that gateway is properly configured.
301303
func validateGatewayOptions(o *Options) error {
302-
if o.Gateway.Name == "" && o.Gateway.Port == 0 {
304+
if o.Gateway.Name == _EMPTY_ && o.Gateway.Port == 0 {
303305
return nil
304306
}
305-
if o.Gateway.Name == "" {
306-
return fmt.Errorf("gateway has no name")
307+
if o.Gateway.Name == _EMPTY_ {
308+
return errors.New("gateway has no name")
309+
}
310+
if strings.Contains(o.Gateway.Name, " ") {
311+
return ErrGatewayNameHasSpaces
307312
}
308313
if o.Gateway.Port == 0 {
309314
return fmt.Errorf("gateway %q has no port specified (select -1 for random port)", o.Gateway.Name)
310315
}
311316
for i, g := range o.Gateway.Gateways {
312-
if g.Name == "" {
317+
if g.Name == _EMPTY_ {
313318
return fmt.Errorf("gateway in the list %d has no name", i)
314319
}
315320
if len(g.URLs) == 0 {

server/leafnode.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,13 @@ func (c *client) processLeafnodeInfo(info *Info) {
12881288
c.closeConnection(WrongPort)
12891289
return
12901290
}
1291+
// Reject a cluster that contains spaces.
1292+
if info.Cluster != _EMPTY_ && strings.Contains(info.Cluster, " ") {
1293+
c.mu.Unlock()
1294+
c.sendErrAndErr(ErrClusterNameHasSpaces.Error())
1295+
c.closeConnection(ProtocolViolation)
1296+
return
1297+
}
12911298
// Capture a nonce here.
12921299
c.nonce = []byte(info.Nonce)
12931300
if info.TLSRequired && didSolicit {
@@ -1779,6 +1786,13 @@ func (c *client) processLeafNodeConnect(s *Server, arg []byte, lang string) erro
17791786
return err
17801787
}
17811788

1789+
// Reject a cluster that contains spaces.
1790+
if proto.Cluster != _EMPTY_ && strings.Contains(proto.Cluster, " ") {
1791+
c.sendErrAndErr(ErrClusterNameHasSpaces.Error())
1792+
c.closeConnection(ProtocolViolation)
1793+
return ErrClusterNameHasSpaces
1794+
}
1795+
17821796
// Check for cluster name collisions.
17831797
if cn := s.cachedClusterName(); cn != _EMPTY_ && proto.Cluster != _EMPTY_ && proto.Cluster == cn {
17841798
c.sendErrAndErr(ErrLeafNodeHasSameClusterName.Error())

server/opts.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,13 @@ func (o *Options) processConfigFileLine(k string, v any, errors *[]error, warnin
917917
case "port":
918918
o.Port = int(v.(int64))
919919
case "server_name":
920-
o.ServerName = v.(string)
920+
sn := v.(string)
921+
if strings.Contains(sn, " ") {
922+
err := &configErr{tk, ErrServerNameHasSpaces.Error()}
923+
*errors = append(*errors, err)
924+
return
925+
}
926+
o.ServerName = sn
921927
case "host", "net":
922928
o.Host = v.(string)
923929
case "debug":
@@ -1690,7 +1696,13 @@ func parseCluster(v any, opts *Options, errors *[]error, warnings *[]error) erro
16901696
tk, mv = unwrapValue(mv, &lt)
16911697
switch strings.ToLower(mk) {
16921698
case "name":
1693-
opts.Cluster.Name = mv.(string)
1699+
cn := mv.(string)
1700+
if strings.Contains(cn, " ") {
1701+
err := &configErr{tk, ErrClusterNameHasSpaces.Error()}
1702+
*errors = append(*errors, err)
1703+
continue
1704+
}
1705+
opts.Cluster.Name = cn
16941706
case "listen":
16951707
hp, err := parseListen(mv)
16961708
if err != nil {
@@ -1920,7 +1932,13 @@ func parseGateway(v any, o *Options, errors *[]error, warnings *[]error) error {
19201932
tk, mv = unwrapValue(mv, &lt)
19211933
switch strings.ToLower(mk) {
19221934
case "name":
1923-
o.Gateway.Name = mv.(string)
1935+
gn := mv.(string)
1936+
if strings.Contains(gn, " ") {
1937+
err := &configErr{tk, ErrGatewayNameHasSpaces.Error()}
1938+
*errors = append(*errors, err)
1939+
continue
1940+
}
1941+
o.Gateway.Name = gn
19241942
case "listen":
19251943
hp, err := parseListen(mv)
19261944
if err != nil {

server/server.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,9 @@ func (s *Server) ClientURL() string {
10041004
}
10051005

10061006
func validateCluster(o *Options) error {
1007+
if o.Cluster.Name != _EMPTY_ && strings.Contains(o.Cluster.Name, " ") {
1008+
return ErrClusterNameHasSpaces
1009+
}
10071010
if o.Cluster.Compression.Mode != _EMPTY_ {
10081011
if err := validateAndNormalizeCompressionOption(&o.Cluster.Compression, CompressionS2Fast); err != nil {
10091012
return err
@@ -1013,8 +1016,9 @@ func validateCluster(o *Options) error {
10131016
return fmt.Errorf("cluster: %v", err)
10141017
}
10151018
// Check that cluster name if defined matches any gateway name.
1016-
if o.Gateway.Name != "" && o.Gateway.Name != o.Cluster.Name {
1017-
if o.Cluster.Name != "" {
1019+
// Note that we have already verified that the gateway name does not have spaces.
1020+
if o.Gateway.Name != _EMPTY_ && o.Gateway.Name != o.Cluster.Name {
1021+
if o.Cluster.Name != _EMPTY_ {
10181022
return ErrClusterNameConfigConflict
10191023
}
10201024
// Set this here so we do not consider it dynamic.
@@ -1055,6 +1059,9 @@ func validateOptions(o *Options) error {
10551059
return fmt.Errorf("max_payload (%v) cannot be higher than max_pending (%v)",
10561060
o.MaxPayload, o.MaxPending)
10571061
}
1062+
if o.ServerName != _EMPTY_ && strings.Contains(o.ServerName, " ") {
1063+
return errors.New("server name cannot contain spaces")
1064+
}
10581065
// Check that the trust configuration is correct.
10591066
if err := validateTrustedOperators(o); err != nil {
10601067
return err

server/server_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2150,3 +2150,53 @@ func TestServerConfigLastLineComments(t *testing.T) {
21502150
require_NoError(t, err)
21512151
defer nc.Close()
21522152
}
2153+
2154+
func TestServerClusterAndGatewayNameNoSpace(t *testing.T) {
2155+
conf := createConfFile(t, []byte(`
2156+
port: -1
2157+
server_name: "my server"
2158+
`))
2159+
_, err := ProcessConfigFile(conf)
2160+
require_Error(t, err, ErrServerNameHasSpaces)
2161+
2162+
o := DefaultOptions()
2163+
o.ServerName = "my server"
2164+
_, err = NewServer(o)
2165+
require_Error(t, err, ErrServerNameHasSpaces)
2166+
2167+
conf = createConfFile(t, []byte(`
2168+
port: -1
2169+
server_name: "myserver"
2170+
cluster {
2171+
port: -1
2172+
name: "my cluster"
2173+
}
2174+
`))
2175+
_, err = ProcessConfigFile(conf)
2176+
require_Error(t, err, ErrClusterNameHasSpaces)
2177+
2178+
o = DefaultOptions()
2179+
o.Cluster.Name = "my cluster"
2180+
o.Cluster.Port = -1
2181+
_, err = NewServer(o)
2182+
require_Error(t, err, ErrClusterNameHasSpaces)
2183+
2184+
conf = createConfFile(t, []byte(`
2185+
port: -1
2186+
server_name: "myserver"
2187+
gateway {
2188+
port: -1
2189+
name: "my gateway"
2190+
}
2191+
`))
2192+
_, err = ProcessConfigFile(conf)
2193+
require_Error(t, err, ErrGatewayNameHasSpaces)
2194+
2195+
o = DefaultOptions()
2196+
o.Cluster.Name = _EMPTY_
2197+
o.Cluster.Port = 0
2198+
o.Gateway.Name = "my gateway"
2199+
o.Gateway.Port = -1
2200+
_, err = NewServer(o)
2201+
require_Error(t, err, ErrGatewayNameHasSpaces)
2202+
}

test/leafnode_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4426,3 +4426,27 @@ func TestLeafnodeHeaders(t *testing.T) {
44264426
t.Fatalf("leaf msg header is empty")
44274427
}
44284428
}
4429+
4430+
func TestLeafNodeClusterNameWithSpacesRejected(t *testing.T) {
4431+
s, opts := runLeafServer()
4432+
defer s.Shutdown()
4433+
4434+
lc := createLeafConn(t, opts.LeafNode.Host, opts.LeafNode.Port)
4435+
defer lc.Close()
4436+
4437+
checkInfoMsg(t, lc)
4438+
sendProto(t, lc, "CONNECT {\"cluster\":\"my cluster\"}\r\n")
4439+
expect := expectCommand(t, lc)
4440+
expect(errRe)
4441+
expectDisconnect(t, lc)
4442+
4443+
lc = createLeafConn(t, opts.LeafNode.Host, opts.LeafNode.Port)
4444+
defer lc.Close()
4445+
leafSend, leafExpect := setupLeaf(t, lc, 1)
4446+
// The accept side does expect an INFO from an handshake,
4447+
// so it will "ignore" the first one.
4448+
leafSend("INFO {\"server\":\"server\"}\r\n")
4449+
leafSend("INFO {\"cluster\":\"my cluster\"}\r\n")
4450+
leafExpect(errRe)
4451+
expectDisconnect(t, lc)
4452+
}

0 commit comments

Comments
 (0)