Repository: nats-io/nats-server
Pull request #1476 (2020-06-12 22:50:07)

Cluster names are now required.
Added cluster names as required for prep work for clustered JetStream. System can dynamically pick a cluster name and settle on one even in large clusters.

Signed-off-by: Derek Collison <derek@nats.io>

/cc @nats-io/core



Patch:
diff --git a/server/auth.go b/server/auth.go
index 9a568453..11f3a799 100644
--- a/server/auth.go
+++ b/server/auth.go
@@ -261,7 +261,7 @@ func (s *Server) buildNkeysAndUsersFromOptions(nko []*NkeyUser, uo []*User) (map
 	var nkeys map[string]*NkeyUser
 	var users map[string]*User
 
-	if nko != nil {
+	if len(nko) > 0 {
 		nkeys = make(map[string]*NkeyUser, len(nko))
 		for _, u := range nko {
 			copy := u.clone()
@@ -276,7 +276,7 @@ func (s *Server) buildNkeysAndUsersFromOptions(nko []*NkeyUser, uo []*User) (map
 			nkeys[u.Nkey] = copy
 		}
 	}
-	if uo != nil {
+	if len(uo) > 0 {
 		users = make(map[string]*User, len(uo))
 		for _, u := range uo {
 			copy := u.clone()
@@ -327,6 +327,60 @@ func (s *Server) isClientAuthorized(c *client) bool {
 	return s.processClientOrLeafAuthentication(c, opts)
 }
 
+type authOpts struct {
+	username   string
+	password   string
+	token      string
+	noAuthUser string
+	tlsMap     bool
+	users      map[string]*User
+	nkeys      map[string]*NkeyUser
+}
+
+func (s *Server) getAuthOpts(c *client, o *Options, auth *authOpts) bool {
+	// c.ws is immutable, but may need lock if we get race reports.
+	wsClient := c.ws != nil
+
+	authRequired := s.info.AuthRequired
+	// For websocket clients, if there is no top-level auth, then we
+	// check for websocket specifically.
+	if !authRequired && wsClient {
+		authRequired = s.websocket.authRequired
+	}
+	if !authRequired {
+		return false
+	}
+	auth.noAuthUser = o.NoAuthUser
+	auth.tlsMap = o.TLSMap
+	if wsClient {
+		wo := &o.Websocket
+		// If those are specified, override, regardless if there is
+		// auth configuration (like user/pwd, etc..) in websocket section.
+		if wo.NoAuthUser != _EMPTY_ {
+			auth.noAuthUser = wo.NoAuthUser
+		}
+		if wo.TLSMap {
+			auth.tlsMap = true
+		}
+		// Now check for websocket auth specific override
+		if s.websocket.authRequired {
+			auth.username = wo.Username
+			auth.password = wo.Password
+			auth.token = wo.Token
+			auth.users = s.websocket.users
+			auth.nkeys = s.websocket.nkeys
+			return true
+		}
+		// else fallback to regular auth config
+	}
+	auth.username = o.Username
+	auth.password = o.Password
+	auth.token = o.Authorization
+	auth.users = s.users
+	auth.nkeys = s.nkeys
+	return true
+}
+
 func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) bool {
 	var (
 		nkey *NkeyUser
@@ -335,56 +389,16 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 		user *User
 		ok   bool
 		err  error
-		ao   bool // auth override
+		auth authOpts
 	)
+
 	s.mu.Lock()
-	authRequired := s.info.AuthRequired
-	// c.ws is immutable, but may need lock if we get race reports.
-	if !authRequired && c.ws != nil {
-		// If no auth required for regular clients, then check if
-		// we have an override for websocket clients.
-		authRequired = s.websocket.authOverride
-	}
+	authRequired := s.getAuthOpts(c, opts, &auth)
 	if !authRequired {
 		// TODO(dlc) - If they send us credentials should we fail?
 		s.mu.Unlock()
 		return true
 	}
-	var (
-		username   string
-		password   string
-		token      string
-		noAuthUser string
-		users      map[string]*User
-		nkusers    map[string]*NkeyUser
-	)
-	tlsMap := opts.TLSMap
-	if c.ws != nil {
-		wo := &opts.Websocket
-		// Always override TLSMap.
-		tlsMap = wo.TLSMap
-		// The rest depends on if there was any auth override in
-		// the websocket's config.
-		if s.websocket.authOverride {
-			noAuthUser = wo.NoAuthUser
-			username = wo.Username
-			password = wo.Password
-			token = wo.Token
-			users = s.websocket.users
-			nkusers = s.websocket.nkeys
-			ao = true
-		}
-	} else if c.kind == LEAF {
-		tlsMap = opts.LeafNode.TLSMap
-	}
-	if !ao {
-		noAuthUser = opts.NoAuthUser
-		username = opts.Username
-		password = opts.Password
-		token = opts.Authorization
-		users = s.users
-		nkusers = s.nkeys
-	}
 
 	// Check if we have trustedKeys defined in the server. If so we require a user jwt.
 	if s.trustedKeys != nil {
@@ -410,21 +424,21 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 	}
 
 	// Check if we have nkeys or users for client.
-	hasNkeys := len(nkusers) > 0
-	hasUsers := len(users) > 0
+	hasNkeys := auth.nkeys != nil
+	hasUsers := auth.users != nil
 	if hasNkeys && c.opts.Nkey != "" {
-		nkey, ok = nkusers[c.opts.Nkey]
+		nkey, ok = auth.nkeys[c.opts.Nkey]
 		if !ok {
 			s.mu.Unlock()
 			return false
 		}
 	} else if hasUsers {
 		// Check if we are tls verify and are mapping users from the client_certificate
-		if tlsMap {
+		if auth.tlsMap {
 			var euser string
 			authorized := checkClientTLSCertSubject(c, func(u string) bool {
 				var ok bool
-				user, ok = users[u]
+				user, ok = auth.users[u]
 				if !ok {
 					c.Debugf("User in cert [%q], not found", u)
 					return false
@@ -437,20 +451,20 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 				return false
 			}
 			if c.opts.Username != "" {
-				s.Warnf("User %q found in connect proto, but user required from cert", c.opts.Username)
+				s.Warnf("User found in connect proto, but user required from cert - %v", c)
 			}
 			// Already checked that the client didn't send a user in connect
 			// but we set it here to be able to identify it in the logs.
 			c.opts.Username = euser
 		} else {
-			if c.kind == CLIENT && c.opts.Username == "" && noAuthUser != "" {
-				if u, exists := users[noAuthUser]; exists {
+			if c.kind == CLIENT && c.opts.Username == "" && auth.noAuthUser != "" {
+				if u, exists := auth.users[auth.noAuthUser]; exists {
 					c.opts.Username = u.Username
 					c.opts.Password = u.Password
 				}
 			}
 			if c.opts.Username != "" {
-				user, ok = users[c.opts.Username]
+				user, ok = auth.users[c.opts.Username]
 				if !ok {
 					s.mu.Unlock()
 					return false
@@ -572,13 +586,13 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
 	}
 
 	if c.kind == CLIENT {
-		if token != "" {
-			return comparePasswords(token, c.opts.Token)
-		} else if username != "" {
-			if username != c.opts.Username {
+		if auth.token != "" {
+			return comparePasswords(auth.token, c.opts.Token)
+		} else if auth.username != "" {
+			if auth.username != c.opts.Username {
 				return false
 			}
-			return comparePasswords(password, c.opts.Password)
+			return comparePasswords(auth.password, c.opts.Password)
 		}
 	} else if c.kind == LEAF {
 		// There is no required username/password to connect and
@@ -770,31 +784,6 @@ func (s *Server) isLeafNodeAuthorized(c *client) bool {
 	if opts.LeafNode.Username != _EMPTY_ {
 		return isAuthorized(opts.LeafNode.Username, opts.LeafNode.Password, opts.LeafNode.Account)
 	} else if len(opts.LeafNode.Users) > 0 {
-		if opts.LeafNode.TLSMap {
-			var user *User
-			found := checkClientTLSCertSubject(c, func(u string) bool {
-				// This is expected to be a very small array.
-				for _, usr := range opts.LeafNode.Users {
-					if u == usr.Username {
-						user = usr
-						return true
-					}
-				}
-				c.Debugf("User in cert [%q], not found", u)
-				return false
-			})
-			if !found {
-				return false
-			}
-			if c.opts.Username != "" {
-				s.Warnf("User %q found in connect proto, but user required from cert", c.opts.Username)
-			}
-			c.opts.Username = user.Username
-			// This will authorize since are using an existing user,
-			// but it will also register with proper account.
-			return isAuthorized(user.Username, user.Password, user.Account.GetName())
-		}
-
 		// This is expected to be a very small array.
 		for _, u := range opts.LeafNode.Users {
 			if u.Username == c.opts.Username {
diff --git a/server/client.go b/server/client.go
index 4298b015..07bb929d 100644
--- a/server/client.go
+++ b/server/client.go
@@ -173,6 +173,7 @@ const (
 	Revocation
 	InternalClient
 	MsgHeaderViolation
+	ClusterNameConflict
 )
 
 // Some flags passed to processMsgResultsEx
diff --git a/server/configs/cluster.conf b/server/configs/cluster.conf
index 30833c1c..6b87f853 100644
--- a/server/configs/cluster.conf
+++ b/server/configs/cluster.conf
@@ -14,6 +14,7 @@ pid_file: '/tmp/nats_cluster_test.pid'
 cluster {
   host: 127.0.0.1
   port: 4244
+  name: "abc"
 
   authorization {
     user: route_user
diff --git a/server/configs/listen.conf b/server/configs/listen.conf
index 3e0de562..00990919 100644
--- a/server/configs/listen.conf
+++ b/server/configs/listen.conf
@@ -7,4 +7,5 @@ https: 127.0.0.1:9443
 
 cluster {
   listen: 127.0.0.1:4244
+  name: "abc"
 }
diff --git a/server/configs/reload/reload.conf b/server/configs/reload/reload.conf
index 97fa2cfe..613033a7 100644
--- a/server/configs/reload/reload.conf
+++ b/server/configs/reload/reload.conf
@@ -32,5 +32,6 @@ authorization {
 
 cluster {
     listen:       127.0.0.1:-1
+    name: "abc"
     no_advertise: true # enable on reload
 }
diff --git a/server/configs/reload/srv_a_1.conf b/server/configs/reload/srv_a_1.conf
index a3be57e8..89241b6a 100644
--- a/server/configs/reload/srv_a_1.conf
+++ b/server/configs/reload/srv_a_1.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:-1
 
 cluster {
   listen: 127.0.0.1:7244
+  name: "abc"
 
   routes = [
     nats-route://127.0.0.1:7246
diff --git a/server/configs/reload/srv_a_2.conf b/server/configs/reload/srv_a_2.conf
index fd8a542c..5cd88c36 100644
--- a/server/configs/reload/srv_a_2.conf
+++ b/server/configs/reload/srv_a_2.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:-1
 
 cluster {
   listen: 127.0.0.1:7244
+  name: "abc"
 
   routes = [
     nats-route://tyler:foo@127.0.0.1:7246 # Use route credentials
diff --git a/server/configs/reload/srv_a_3.conf b/server/configs/reload/srv_a_3.conf
index fd7fe291..ff6d005a 100644
--- a/server/configs/reload/srv_a_3.conf
+++ b/server/configs/reload/srv_a_3.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:-1
 
 cluster {
   listen: 127.0.0.1:7244
+  name: "abc"
 
   routes = [
     nats-route://127.0.0.1:7247 # Remove srv b route and add srv c
diff --git a/server/configs/reload/srv_a_4.conf b/server/configs/reload/srv_a_4.conf
index 3b064c05..501a57c1 100644
--- a/server/configs/reload/srv_a_4.conf
+++ b/server/configs/reload/srv_a_4.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:-1
 
 cluster {
   listen: 127.0.0.1:7244
+  name: "abc"
 }
 
 no_sys_acc: true
diff --git a/server/configs/reload/srv_b_1.conf b/server/configs/reload/srv_b_1.conf
index 27b6dcf6..33369e0f 100644
--- a/server/configs/reload/srv_b_1.conf
+++ b/server/configs/reload/srv_b_1.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:-1
 
 cluster {
   listen: 127.0.0.1:7246
+  name: "abc"
 }
 
 no_sys_acc: true
diff --git a/server/configs/reload/srv_b_2.conf b/server/configs/reload/srv_b_2.conf
index 68c4a9cc..b3fbdb1c 100644
--- a/server/configs/reload/srv_b_2.conf
+++ b/server/configs/reload/srv_b_2.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:-1
 
 cluster {
   listen: 127.0.0.1:7246
+  name: "abc"
 
   # Enable route authorization.
   authorization {
diff --git a/server/configs/reload/srv_c_1.conf b/server/configs/reload/srv_c_1.conf
index 553bdb2b..0e27c674 100644
--- a/server/configs/reload/srv_c_1.conf
+++ b/server/configs/reload/srv_c_1.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:-1
 
 cluster {
   listen: 127.0.0.1:7247
+  name: "abc"
 }
 
 no_sys_acc: true
diff --git a/server/configs/seed.conf b/server/configs/seed.conf
index 54b781b1..16b8f540 100644
--- a/server/configs/seed.conf
+++ b/server/configs/seed.conf
@@ -6,4 +6,5 @@ http: 127.0.0.1:9222
 
 cluster {
   listen: 127.0.0.1:7248
+  name: "abc"
 }
diff --git a/server/configs/seed_tls.conf b/server/configs/seed_tls.conf
index 206bb703..4dd45c6a 100644
--- a/server/configs/seed_tls.conf
+++ b/server/configs/seed_tls.conf
@@ -6,6 +6,7 @@ http: 127.0.0.1:9222
 
 cluster {
   listen: 127.0.0.1:7248
+  name: "abc"
 
   tls {
     # Route cert
diff --git a/server/configs/srv_a.conf b/server/configs/srv_a.conf
index a19e6e51..209bdd44 100644
--- a/server/configs/srv_a.conf
+++ b/server/configs/srv_a.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:7222
 
 cluster {
   listen: 127.0.0.1:7244
+  name: "abc"
 
   authorization {
     user: ruser
diff --git a/server/configs/srv_a_bcrypt.conf b/server/configs/srv_a_bcrypt.conf
index 2343441a..ff1812c1 100644
--- a/server/configs/srv_a_bcrypt.conf
+++ b/server/configs/srv_a_bcrypt.conf
@@ -10,6 +10,7 @@ authorization {
 
 cluster {
   listen: 127.0.0.1:7244
+  name: "abc"
 
   authorization {
     user: ruser
diff --git a/server/configs/srv_b.conf b/server/configs/srv_b.conf
index d6eddb65..65ea6413 100644
--- a/server/configs/srv_b.conf
+++ b/server/configs/srv_b.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:7224
 
 cluster {
   listen: 127.0.0.1:7246
+  name: "abc"
 
   authorization {
     user: ruser
diff --git a/server/configs/srv_b_bcrypt.conf b/server/configs/srv_b_bcrypt.conf
index 62a9e78d..5de8a52b 100644
--- a/server/configs/srv_b_bcrypt.conf
+++ b/server/configs/srv_b_bcrypt.conf
@@ -10,6 +10,7 @@ authorization {
 
 cluster {
   listen: 127.0.0.1:7246
+  name: "abc"
 
   authorization {
     user: ruser
diff --git a/server/errors.go b/server/errors.go
index 5562a9da..f9d46777 100644
--- a/server/errors.go
+++ b/server/errors.go
@@ -144,6 +144,12 @@ var (
 	// ErrMsgHeadersNotSupported signals the parser detected a message header
 	// but they are not supported on this server.
 	ErrMsgHeadersNotSupported = errors.New("message headers not supported")
+
+	// ErrClusterNameConfigConflict signals that the options for cluster name in cluster and gateway are in conflict.
+	ErrClusterNameConfigConflict = errors.New("cluster name conflicts between cluster and gateway definitions")
+
+	// ErrClusterNameRemoteConflict signals that a remote server has a different cluster name.
+	ErrClusterNameRemoteConflict = errors.New("cluster name from remote server conflicts")
 )
 
 // configErr is a configuration error.
diff --git a/server/filestore.go b/server/filestore.go
index dd821812..00e02d31 100644
--- a/server/filestore.go
+++ b/server/filestore.go
@@ -1568,11 +1568,12 @@ func (fs *fileStore) LoadMsg(seq uint64) (string, []byte, []byte, int64, error)
 	return "", nil, nil, 0, err
 }
 
+// State returns the current state of the stream.
 func (fs *fileStore) State() StreamState {
 	fs.mu.RLock()
-	defer fs.mu.RUnlock()
 	state := fs.state
 	state.Consumers = len(fs.cfs)
+	fs.mu.RUnlock()
 	return state
 }
 
diff --git a/server/opts.go b/server/opts.go
index 2c812f3a..88a63a76 100644
--- a/server/opts.go
+++ b/server/opts.go
@@ -57,6 +57,7 @@ func NoErrOnUnknownFields(noError bool) {
 // NOTE: This structure is no longer used for monitoring endpoints
 // and json tags are deprecated and may be removed in the future.
 type ClusterOpts struct {
+	Name           string            `json:"-"`
 	Host           string            `json:"addr,omitempty"`
 	Port           int               `json:"cluster_port,omitempty"`
 	Username       string            `json:"-"`
@@ -1009,6 +1010,8 @@ func parseCluster(v interface{}, opts *Options, errors *[]error, warnings *[]err
 		// Again, unwrap token value if line check is required.
 		tk, mv = unwrapValue(mv, &lt)
 		switch strings.ToLower(mk) {
+		case "name":
+			opts.Cluster.Name = mv.(string)
 		case "listen":
 			hp, err := parseListen(mv)
 			if err != nil {
@@ -3519,7 +3522,7 @@ func setBaselineOptions(opts *Options) {
 	}
 }
 
-// ConfigureOptions accepts a flag set and augment it with NATS Server
+// ConfigureOptions accepts a flag set and augments it with NATS Server
 // specific flags. On success, an options structure is returned configured
 // based on the selected flags and/or configuration file.
 // The command line options take precedence to the ones in the configuration file.
diff --git a/server/route.go b/server/route.go
index 5b20501d..c2f44b66 100644
--- a/server/route.go
+++ b/server/route.go
@@ -89,6 +89,8 @@ type connectInfo struct {
 	TLS      bool   `json:"tls_required"`
 	Headers  bool   `json:"headers"`
 	Name     string `json:"name"`
+	Cluster  string `json:"cluster"`
+	Dynamic  bool   `json:"cluster_dynamic,omitempty"`
 	Gateway  string `json:"gateway,omitempty"`
 }
 
@@ -357,6 +359,7 @@ func (c *client) sendRouteConnect(tlsRequired bool) {
 		user = userInfo.Username()
 		pass, _ = userInfo.Password()
 	}
+	s := c.srv
 	cinfo := connectInfo{
 		Echo:     true,
 		Verbose:  false,
@@ -364,8 +367,10 @@ func (c *client) sendRouteConnect(tlsRequired bool) {
 		User:     user,
 		Pass:     pass,
 		TLS:      tlsRequired,
-		Name:     c.srv.info.ID,
-		Headers:  c.srv.supportsHeaders(),
+		Name:     s.info.ID,
+		Headers:  s.supportsHeaders(),
+		Cluster:  s.info.Cluster,
+		Dynamic:  s.isClusterNameDynamic(),
 	}
 
 	b, err := json.Marshal(cinfo)
@@ -390,6 +395,7 @@ func (c *client) processRouteInfo(info *Info) {
 	gacc.mu.RUnlock()
 
 	supportsHeaders := c.srv.supportsHeaders()
+	clusterName := c.srv.ClusterName()
 
 	c.mu.Lock()
 	// Connection can be closed at any time (by auth timeout, etc).
@@ -410,6 +416,20 @@ func (c *client) processRouteInfo(info *Info) {
 		return
 	}
 
+	// Detect if we have a mismatch of cluster names.
+	if info.Cluster != "" && info.Cluster != clusterName {
+		c.mu.Unlock()
+		// If we are dynamic we may update our cluster name.
+		if s.isClusterNameDynamic() && strings.Compare(clusterName, info.Cluster) < 0 {
+			s.setClusterName(info.Cluster)
+			s.removeAllRoutesExcept(c)
+			c.mu.Lock()
+		} else {
+			c.closeConnection(ClusterNameConflict)
+			return
+		}
+	}
+
 	// If this is an async INFO from an existing route...
 	if c.flags.isSet(infoReceived) {
 		remoteID := c.route.remoteID
@@ -1489,6 +1509,11 @@ func (s *Server) routeAcceptLoop(ch chan struct{}) {
 		port = 0
 	}
 
+	s.Noticef("Cluster name is %s", s.ClusterName())
+	if s.isClusterNameDynamic() {
+		s.Warnf("Cluster name was dynamically generated, consider setting one")
+	}
+
 	hp := net.JoinHostPort(opts.Cluster.Host, strconv.Itoa(port))
 	l, e := net.Listen("tcp", hp)
 	if e != nil {
@@ -1524,6 +1549,7 @@ func (s *Server) routeAcceptLoop(ch chan struct{}) {
 		Proto:        proto,
 		GatewayURL:   s.getGatewayURL(),
 		Headers:      s.supportsHeaders(),
+		Cluster:      s.info.Cluster,
 	}
 	// Set this if only if advertise is not disabled
 	if !opts.Cluster.NoAdvertise {
@@ -1615,8 +1641,8 @@ func (s *Server) setRouteInfoHostPortAndIP() error {
 func (s *Server) StartRouting(clientListenReady chan struct{}) {
 	defer s.grWG.Done()
 
-	// Wait for the client listen port to be opened, and
-	// the possible ephemeral port to be selected.
+	// Wait for the client and and leafnode listen ports to be opened,
+	// and the possible ephemeral ports to be selected.
 	<-clientListenReady
 
 	// Spin up the accept loop
@@ -1731,6 +1757,7 @@ func (c *client) processRouteConnect(srv *Server, arg []byte, lang string) error
 	}
 	// Unmarshal as a route connect protocol
 	proto := &connectInfo{}
+
 	if err := json.Unmarshal(arg, proto); err != nil {
 		return err
 	}
@@ -1748,6 +1775,32 @@ func (c *client) processRouteConnect(srv *Server, arg []byte, lang string) error
 		perms = srv.getOpts().Cluster.Permissions
 	}
 
+	clusterName := srv.ClusterName()
+
+	// If we have a cluster name set, make sure it matches ours.
+	if proto.Cluster != clusterName {
+		shouldReject := true
+		// If we have a dynamic name we will do additional checks.
+		if srv.isClusterNameDynamic() {
+			if !proto.Dynamic || strings.Compare(clusterName, proto.Cluster) < 0 {
+				// We will take on their name since theirs is configured or higher then ours.
+				srv.setClusterName(proto.Cluster)
+				if !proto.Dynamic {
+					srv.getOpts().Cluster.Name = proto.Cluster
+				}
+				srv.removeAllRoutesExcept(c)
+				shouldReject = false
+			}
+		}
+		if shouldReject {
+			errTxt := fmt.Sprintf("Rejecting connection, cluster name %q does not match %q", proto.Cluster, srv.info.Cluster)
+			c.Errorf(errTxt)
+			c.sendErr(errTxt)
+			c.closeConnection(ClusterNameConflict)
+			return ErrClusterNameRemoteConflict
+		}
+	}
+
 	supportsHeaders := c.srv.supportsHeaders()
 
 	// Grab connection name of remote route.
@@ -1759,6 +1812,22 @@ func (c *client) processRouteConnect(srv *Server, arg []byte, lang string) error
 	return nil
 }
 
+// Called when we update our cluster name during negotiations with remotes.
+func (s *Server) removeAllRoutesExcept(c *client) {
+	s.mu.Lock()
+	routes := make([]*client, 0, len(s.routes))
+	for _, r := range s.routes {
+		if r != c {
+			routes = append(routes, r)
+		}
+	}
+	s.mu.Unlock()
+
+	for _, r := range routes {
+		r.closeConnection(ClusterNameConflict)
+	}
+}
+
 func (s *Server) removeRoute(c *client) {
 	var rID string
 	var lnURL string
diff --git a/server/server.go b/server/server.go
index d35b6858..610dd89b 100644
--- a/server/server.go
+++ b/server/server.go
@@ -276,6 +276,7 @@ func NewServer(opts *Options) (*Server, error) {
 		MaxPayload:   opts.MaxPayload,
 		JetStream:    opts.JetStream,
 		Headers:      !opts.NoHeaderSupport,
+		Cluster:      opts.Cluster.Name,
 	}
 
 	if tlsReq && !info.TLSRequired {
@@ -322,8 +323,9 @@ func NewServer(opts *Options) (*Server, error) {
 		return nil, err
 	}
 
-	if s.gateway.enabled {
-		s.info.Cluster = s.getGatewayName()
+	// If we have a cluster definition but do not have a cluster name, create one.
+	if opts.Cluster.Port != 0 && opts.Cluster.Name == "" {
+		s.info.Cluster = nuid.Next()
 	}
 
 	// This is normally done in the AcceptLoop, once the
@@ -414,6 +416,28 @@ func NewServer(opts *Options) (*Server, error) {
 	return s, nil
 }
 
+// clusterName returns our cluster name which could be dynamic.
+func (s *Server) ClusterName() string {
+	s.mu.Lock()
+	cn := s.info.Cluster
+	s.mu.Unlock()
+	return cn
+}
+
+// setClusterName will update the cluster name for this server.
+func (s *Server) setClusterName(name string) {
+	s.mu.Lock()
+	s.info.Cluster = name
+	s.routeInfo.Cluster = name
+	s.mu.Unlock()
+	s.Noticef("Cluster name updated to %s", name)
+}
+
+// Return whether the cluster name is dynamic.
+func (s *Server) isClusterNameDynamic() bool {
+	return s.getOpts().Cluster.Name == ""
+}
+
 // ClientURL returns the URL used to connect clients. Helpful in testing
 // when we designate a random client port (-1).
 func (s *Server) ClientURL() string {
@@ -426,6 +450,18 @@ func (s *Server) ClientURL() string {
 	return fmt.Sprintf("%s%s:%d", scheme, opts.Host, opts.Port)
 }
 
+func validateClusterName(o *Options) error {
+	// Check that cluster name if defined matches any gateway name.
+	if o.Gateway.Name != "" && o.Gateway.Name != o.Cluster.Name {
+		if o.Cluster.Name != "" {
+			return ErrClusterNameConfigConflict
+		}
+		// Set this here so we do not consider it dynamic.
+		o.Cluster.Name = o.Gateway.Name
+	}
+	return nil
+}
+
 func validateOptions(o *Options) error {
 	if o.LameDuckDuration > 0 && o.LameDuckGracePeriod >= o.LameDuckDuration {
 		return fmt.Errorf("lame duck grace period (%v) should be strictly lower than lame duck duration (%v)",
@@ -449,6 +485,11 @@ func validateOptions(o *Options) error {
 	if err := validateGatewayOptions(o); err != nil {
 		return err
 	}
+	// Check that cluster name if defined matches any gateway name.
+	if err := validateClusterName(o); err != nil {
+		return err
+	}
+	// Finally check websocket options.
 	return validateWebsocketOptions(o)
 }
 
@@ -1244,10 +1285,6 @@ func (s *Server) Start() {
 	// Snapshot server options.
 	opts := s.getOpts()
 
-	if opts.ConfigFile != _EMPTY_ {
-		s.Noticef("Using configuration file: %s", opts.ConfigFile)
-	}
-
 	hasOperators := len(opts.TrustedOperators) > 0
 	if hasOperators {
 		s.Noticef("Trusted Operators")
@@ -1905,7 +1942,7 @@ func (s *Server) createClient(conn net.Conn, ws *websocket) *client {
 	// then we use the websocket's specific boolean that will be set to true
 	// if there is any auth{} configured in websocket{}.
 	if ws != nil && !info.AuthRequired {
-		info.AuthRequired = s.websocket.authOverride
+		info.AuthRequired = s.websocket.authRequired
 	}
 	if s.nonceRequired() {
 		// Nonce handling
diff --git a/server/websocket.go b/server/websocket.go
index 4cf5cec1..33990f61 100644
--- a/server/websocket.go
+++ b/server/websocket.go
@@ -103,7 +103,7 @@ type srvWebsocket struct {
 	connectURLsMap map[string]struct{}
 	users          map[string]*User
 	nkeys          map[string]*NkeyUser
-	authOverride   bool // indicate if there is auth override in websocket config
+	authRequired   bool // indicate if there is auth override in websocket config
 }
 
 type allowedOrigin struct {
@@ -735,19 +735,6 @@ func validateWebsocketOptions(o *Options) error {
 			return fmt.Errorf("unable to parse allowed origin: %v", err)
 		}
 	}
-	// If there is a NoAuthUser, we need to have Users defined and
-	// the user to be present.
-	if wo.NoAuthUser != _EMPTY_ {
-		if wo.Users == nil {
-			return fmt.Errorf("websocket no_auth_user %q configured, but users are not", wo.NoAuthUser)
-		}
-		for _, u := range wo.Users {
-			if u.Username == wo.NoAuthUser {
-				return nil
-			}
-		}
-		return fmt.Errorf("websocket no_auth_user %q not found in users configuration", wo.NoAuthUser)
-	}
 	return nil
 }
 
@@ -784,18 +771,17 @@ func (s *Server) wsSetOriginOptions(o *WebsocketOpts) {
 // store them in s.websocket.users/nkeys.
 // Also update a boolean that indicates if auth is required for
 // websocket clients.
-// Server lock is held on entry.
 func (s *Server) wsConfigAuth(opts *WebsocketOpts) {
 	ws := &s.websocket
 	if len(opts.Nkeys) > 0 || len(opts.Users) > 0 {
 		ws.nkeys, ws.users = s.buildNkeysAndUsersFromOptions(opts.Nkeys, opts.Users)
-		ws.authOverride = true
+		ws.authRequired = true
 	} else if opts.Username != "" || opts.Token != "" {
-		ws.authOverride = true
+		ws.authRequired = true
 	} else {
 		ws.users = nil
 		ws.nkeys = nil
-		ws.authOverride = false
+		ws.authRequired = false
 	}
 }
 


Test patch:
diff --git a/server/configs/reload/test.conf b/server/configs/reload/test.conf
index d818b6bb..adf89b92 100644
--- a/server/configs/reload/test.conf
+++ b/server/configs/reload/test.conf
@@ -7,5 +7,6 @@ logtime: false
 
 cluster {
     listen:       127.0.0.1:-1
+    name:         "abc"
     no_advertise: false
 }
diff --git a/server/events_test.go b/server/events_test.go
index 4f6e0a6f..774b2f1b 100644
--- a/server/events_test.go
+++ b/server/events_test.go
@@ -95,6 +95,7 @@ func runTrustedCluster(t *testing.T) (*Server, *Options, *Server, *Options, nkey
 	mr.Store(apub, jwt)
 
 	optsA := DefaultOptions()
+	optsA.Cluster.Name = "TEST CLUSTER 22"
 	optsA.Cluster.Host = "127.0.0.1"
 	optsA.TrustedKeys = []string{pub}
 	optsA.AccountResolver = mr
@@ -138,6 +139,7 @@ func runTrustedGateways(t *testing.T) (*Server, *Options, *Server, *Options, nke
 	mr.Store(apub, jwt)
 
 	optsA := testDefaultOptionsForGateway("A")
+	optsA.Cluster.Name = "A"
 	optsA.Cluster.Host = "127.0.0.1"
 	optsA.TrustedKeys = []string{pub}
 	optsA.AccountResolver = mr
@@ -146,6 +148,7 @@ func runTrustedGateways(t *testing.T) (*Server, *Options, *Server, *Options, nke
 	sa := RunServer(optsA)
 
 	optsB := testGatewayOptionsFromToWithServers(t, "B", "A", sa)
+	optsB.Cluster.Name = "B"
 	optsB.TrustedKeys = []string{pub}
 	optsB.AccountResolver = mr
 	optsB.SystemAccount = apub
diff --git a/server/gateway_test.go b/server/gateway_test.go
index 2066df76..6f002945 100644
--- a/server/gateway_test.go
+++ b/server/gateway_test.go
@@ -240,6 +240,7 @@ func testDefaultOptionsForGateway(name string) *Options {
 	o := DefaultOptions()
 	o.NoSystemAccount = true
 	o.ServerName = name
+	o.Cluster.Name = name
 	o.Gateway.Name = name
 	o.Gateway.Host = "127.0.0.1"
 	o.Gateway.Port = -1
@@ -1100,6 +1101,7 @@ func TestGatewayWrongDestination(t *testing.T) {
 	cfg.resetConnAttempts()
 
 	o2.Gateway.Name = "B"
+	o2.Cluster.Name = "B"
 	s2 = runGatewayServer(o2)
 	defer s2.Shutdown()
 
@@ -2501,17 +2503,16 @@ func TestGatewaySendRemoteQSubs(t *testing.T) {
 	// Server sb1 should not have qsub in its sub interest map
 	checkFor(t, time.Second, 15*time.Millisecond, func() error {
 		var entry *sitally
-		var err error
 		sb1.gateway.pasi.Lock()
 		asim := sb1.gateway.pasi.m[globalAccountName]
 		if asim != nil {
 			entry = asim["foo bar"]
 		}
+		sb1.gateway.pasi.Unlock()
 		if entry != nil {
-			err = fmt.Errorf("Map should not have an entry, got %#v", entry)
+			return fmt.Errorf("Map should not have an entry, got %#v", entry)
 		}
-		sb1.gateway.pasi.Unlock()
-		return err
+		return nil
 	})
 
 	// Let's wait for A to receive the unsubscribe
@@ -2884,6 +2885,7 @@ func TestGatewayRoutedServerWithoutGatewayConfigured(t *testing.T) {
 	waitForOutboundGateways(t, s2, 1, time.Second)
 
 	o3 := DefaultOptions()
+	o3.Cluster.Name = "B"
 	o3.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", s2.ClusterAddr().Port))
 	s3 := New(o3)
 	defer s3.Shutdown()
diff --git a/server/leafnode_test.go b/server/leafnode_test.go
index cccf10b9..d411d319 100644
--- a/server/leafnode_test.go
+++ b/server/leafnode_test.go
@@ -346,6 +346,7 @@ func TestLeafNodeBasicAuthFailover(t *testing.T) {
 	content := `
 	listen: "127.0.0.1:-1"
 	cluster {
+		name: "abc"
 		listen: "127.0.0.1:-1"
 		%s
 	}
@@ -948,6 +949,7 @@ func TestLeafNodeRemoteWrongPort(t *testing.T) {
 			// Make sure we have all ports (client, route, gateway) and we will try
 			// to create a leafnode to connection to each and make sure we get the error.
 			oa.Cluster.NoAdvertise = test1.clusterAdvertise
+			oa.Cluster.Name = "A"
 			oa.Cluster.Host = "127.0.0.1"
 			oa.Cluster.Port = -1
 			oa.Gateway.Host = "127.0.0.1"
@@ -963,6 +965,7 @@ func TestLeafNodeRemoteWrongPort(t *testing.T) {
 
 			ob := DefaultOptions()
 			ob.Cluster.NoAdvertise = test1.clusterAdvertise
+			ob.Cluster.Name = "A"
 			ob.Cluster.Host = "127.0.0.1"
 			ob.Cluster.Port = -1
 			ob.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", oa.Cluster.Host, oa.Cluster.Port))
@@ -1271,6 +1274,7 @@ func TestLeafNodeExportPermissionsNotForSpecialSubs(t *testing.T) {
 	lo1 := DefaultOptions()
 	lo1.Accounts = []*Account{NewAccount("SYS")}
 	lo1.SystemAccount = "SYS"
+	lo1.Cluster.Name = "A"
 	lo1.Gateway.Name = "A"
 	lo1.Gateway.Port = -1
 	lo1.LeafNode.Host = "127.0.0.1"
@@ -1496,99 +1500,3 @@ func TestLeafNodeTmpClients(t *testing.T) {
 	checkLeafNodeConnected(t, b)
 	checkTmp(0)
 }
-
-func TestLeafNodeTLSVerifyAndMap(t *testing.T) {
-
-	accName := "MyAccount"
-	acc := NewAccount(accName)
-	certUserName := "CN=example.com,OU=NATS.io"
-	users := []*User{&User{Username: certUserName, Account: acc}}
-
-	for _, test := range []struct {
-		name        string
-		leafUsers   bool
-		provideCert bool
-	}{
-		{"no users override, provides cert", false, true},
-		{"no users override, does not provide cert", false, false},
-		{"users override, provides cert", true, true},
-		{"users override, does not provide cert", true, false},
-	} {
-		t.Run(test.name, func(t *testing.T) {
-			o := DefaultOptions()
-			o.Accounts = []*Account{acc}
-			o.LeafNode.Host = "127.0.0.1"
-			o.LeafNode.Port = -1
-			if test.leafUsers {
-				o.LeafNode.Users = users
-			} else {
-				o.Users = users
-			}
-			tc := &TLSConfigOpts{
-				CertFile: "../test/configs/certs/tlsauth/server.pem",
-				KeyFile:  "../test/configs/certs/tlsauth/server-key.pem",
-				CaFile:   "../test/configs/certs/tlsauth/ca.pem",
-				Verify:   true,
-			}
-			tlsc, err := GenTLSConfig(tc)
-			if err != nil {
-				t.Fatalf("Error creating tls config: %v", err)
-			}
-			o.LeafNode.TLSConfig = tlsc
-			o.LeafNode.TLSMap = true
-			s := RunServer(o)
-			defer s.Shutdown()
-
-			slo := DefaultOptions()
-			sltlsc := &tls.Config{}
-			if test.provideCert {
-				tc := &TLSConfigOpts{
-					CertFile: "../test/configs/certs/tlsauth/client.pem",
-					KeyFile:  "../test/configs/certs/tlsauth/client-key.pem",
-				}
-				var err error
-				sltlsc, err = GenTLSConfig(tc)
-				if err != nil {
-					t.Fatalf("Error generating tls config: %v", err)
-				}
-			}
-			sltlsc.InsecureSkipVerify = true
-			u, _ := url.Parse(fmt.Sprintf("nats://%s:%d", o.LeafNode.Host, o.LeafNode.Port))
-			slo.LeafNode.Remotes = []*RemoteLeafOpts{
-				{
-					TLSConfig: sltlsc,
-					URLs:      []*url.URL{u},
-				},
-			}
-			sl := RunServer(slo)
-			defer sl.Shutdown()
-
-			if !test.provideCert {
-				// Wait a bit and make sure we are not connecting
-				time.Sleep(100 * time.Millisecond)
-				checkLeafNodeConnectedCount(t, sl, 0)
-				return
-			}
-			checkLeafNodeConnected(t, sl)
-
-			var uname string
-			var accname string
-			s.mu.Lock()
-			for _, c := range s.leafs {
-				c.mu.Lock()
-				uname = c.opts.Username
-				if c.acc != nil {
-					accname = c.acc.GetName()
-				}
-				c.mu.Unlock()
-			}
-			s.mu.Unlock()
-			if uname != certUserName {
-				t.Fatalf("Expected username %q, got %q", certUserName, uname)
-			}
-			if accname != accName {
-				t.Fatalf("Expected account %q, got %v", accName, accname)
-			}
-		})
-	}
-}
diff --git a/server/monitor_test.go b/server/monitor_test.go
index 0393c084..6d0cc10b 100644
--- a/server/monitor_test.go
+++ b/server/monitor_test.go
@@ -1267,6 +1267,7 @@ func TestConnzWithRoutes(t *testing.T) {
 	resetPreviousHTTPConnections()
 	opts := DefaultMonitorOptions()
 	opts.NoSystemAccount = true
+	opts.Cluster.Name = "A"
 	opts.Cluster.Host = "127.0.0.1"
 	opts.Cluster.Port = CLUSTER_PORT
 
@@ -1277,6 +1278,7 @@ func TestConnzWithRoutes(t *testing.T) {
 		Host: "127.0.0.1",
 		Port: -1,
 		Cluster: ClusterOpts{
+			Name: "A",
 			Host: "127.0.0.1",
 			Port: -1,
 		},
@@ -2003,6 +2005,7 @@ func TestMonitorRoutezRace(t *testing.T) {
 	resetPreviousHTTPConnections()
 	srvAOpts := DefaultMonitorOptions()
 	srvAOpts.NoSystemAccount = true
+	srvAOpts.Cluster.Name = "B"
 	srvAOpts.Cluster.Port = -1
 	srvA := RunServer(srvAOpts)
 	defer srvA.Shutdown()
@@ -2155,6 +2158,7 @@ func TestRoutezPermissions(t *testing.T) {
 	resetPreviousHTTPConnections()
 	opts := DefaultMonitorOptions()
 	opts.NoSystemAccount = true
+	opts.Cluster.Name = "A"
 	opts.Cluster.Host = "127.0.0.1"
 	opts.Cluster.Port = -1
 	opts.Cluster.Permissions = &RoutePermissions{
@@ -2172,6 +2176,7 @@ func TestRoutezPermissions(t *testing.T) {
 
 	opts = DefaultMonitorOptions()
 	opts.Cluster.Host = "127.0.0.1"
+	opts.Cluster.Name = "A"
 	opts.Cluster.Port = -1
 	routeURL, _ := url.Parse(fmt.Sprintf("nats-route://127.0.0.1:%d", s1.ClusterAddr().Port))
 	opts.Routes = []*url.URL{routeURL}
@@ -2402,6 +2407,7 @@ func TestMonitorCluster(t *testing.T) {
 	resetPreviousHTTPConnections()
 	opts := DefaultMonitorOptions()
 	opts.NoSystemAccount = true
+	opts.Cluster.Name = "A"
 	opts.Cluster.Port = -1
 	opts.Cluster.AuthTimeout = 1
 	opts.Routes = RoutesFromStr("nats://127.0.0.1:1234")
@@ -2447,6 +2453,8 @@ func TestMonitorClusterURLs(t *testing.T) {
 
 	o2 := DefaultOptions()
 	o2.Cluster.Host = "127.0.0.1"
+	o2.Cluster.Name = "A"
+
 	s2 := RunServer(o2)
 	defer s2.Shutdown()
 
@@ -2456,6 +2464,7 @@ func TestMonitorClusterURLs(t *testing.T) {
 		port: -1
 		http: -1
 		cluster: {
+			name: "A"
 			port: -1
 			routes [
 				%s
diff --git a/server/opts_test.go b/server/opts_test.go
index d257b145..bb348f9b 100644
--- a/server/opts_test.go
+++ b/server/opts_test.go
@@ -344,6 +344,7 @@ func TestRouteFlagOverride(t *testing.T) {
 		Host:       "127.0.0.1",
 		Port:       7222,
 		Cluster: ClusterOpts{
+			Name:        "abc",
 			Host:        "127.0.0.1",
 			Port:        7244,
 			Username:    "ruser",
@@ -383,6 +384,7 @@ func TestClusterFlagsOverride(t *testing.T) {
 		Host:       "127.0.0.1",
 		Port:       7222,
 		Cluster: ClusterOpts{
+			Name:        "abc",
 			Host:        "127.0.0.1",
 			Port:        7244,
 			ListenStr:   "nats://127.0.0.1:8224",
@@ -419,6 +421,7 @@ func TestRouteFlagOverrideWithMultiple(t *testing.T) {
 		Port:       7222,
 		Cluster: ClusterOpts{
 			Host:        "127.0.0.1",
+			Name:        "abc",
 			Port:        7244,
 			Username:    "ruser",
 			Password:    "top_secret",
@@ -922,7 +925,7 @@ func TestNkeyUsersDefaultPermissionsConfig(t *testing.T) {
 					}
 				}
 			]
-		}	
+		}
 	}
 	`))
 	checkPerms := func(permsDef *Permissions, permsNonDef *Permissions) {
@@ -2757,3 +2760,27 @@ func TestReadOperatorAssertVersionFail(t *testing.T) {
 		t.Fatal("expected different error got: ", err)
 	}
 }
+
+func TestClusterNameAndGatewayNameConflict(t *testing.T) {
+	conf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		cluster {
+			name: A
+			listen: 127.0.0.1:-1
+		}
+		gateway {
+			name: B
+			listen: 127.0.0.1:-1
+		}
+	`))
+	defer os.Remove(conf)
+
+	opts, err := ProcessConfigFile(conf)
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+
+	if err := validateOptions(opts); err != ErrClusterNameConfigConflict {
+		t.Fatalf("Expected ErrClusterNameConfigConflict got %v", err)
+	}
+}
diff --git a/server/ping_test.go b/server/ping_test.go
index 95ae0613..9bea9eb6 100644
--- a/server/ping_test.go
+++ b/server/ping_test.go
@@ -14,11 +14,11 @@
 package server
 
 import (
-	"bufio"
 	"fmt"
-	"net"
 	"testing"
 	"time"
+
+	"github.com/nats-io/nats.go"
 )
 
 const PING_CLIENT_PORT = 11228
@@ -28,41 +28,17 @@ var DefaultPingOptions = Options{
 	Port:         PING_CLIENT_PORT,
 	NoLog:        true,
 	NoSigs:       true,
-	PingInterval: 50 * time.Millisecond,
+	PingInterval: 5 * time.Millisecond,
 }
 
 func TestPing(t *testing.T) {
-	o := DefaultPingOptions
-	o.DisableShortFirstPing = true
-	s := RunServer(&o)
+	s := RunServer(&DefaultPingOptions)
 	defer s.Shutdown()
 
-	c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", PING_CLIENT_PORT))
+	nc, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", PING_CLIENT_PORT))
 	if err != nil {
-		t.Fatalf("Error connecting: %v", err)
-	}
-	defer c.Close()
-	br := bufio.NewReader(c)
-	// Wait for INFO
-	br.ReadLine()
-	// Send CONNECT
-	c.Write([]byte("CONNECT {\"verbose\":false}\r\nPING\r\n"))
-	// Wait for first PONG
-	br.ReadLine()
-	// Wait for PING
-	start := time.Now()
-	for i := 0; i < 3; i++ {
-		l, _, err := br.ReadLine()
-		if err != nil {
-			t.Fatalf("Error: %v", err)
-		}
-		if string(l) != "PING" {
-			t.Fatalf("Expected PING, got %q", l)
-		}
-		if dur := time.Since(start); dur < 25*time.Millisecond || dur > 75*time.Millisecond {
-			t.Fatalf("Pings duration off: %v", dur)
-		}
-		c.Write([]byte(pongProto))
-		start = time.Now()
+		t.Fatalf("Error creating client: %v\n", err)
 	}
+	defer nc.Close()
+	time.Sleep(10 * time.Millisecond)
 }
diff --git a/server/reload_test.go b/server/reload_test.go
index 6c928494..8afe7d75 100644
--- a/server/reload_test.go
+++ b/server/reload_test.go
@@ -151,6 +151,7 @@ func TestConfigReloadUnsupported(t *testing.T) {
 		MaxPingsOut:    2,
 		WriteDeadline:  2 * time.Second,
 		Cluster: ClusterOpts{
+			Name: "abc",
 			Host: "127.0.0.1",
 			Port: -1,
 		},
@@ -224,6 +225,7 @@ func TestConfigReloadInvalidConfig(t *testing.T) {
 		MaxPingsOut:    2,
 		WriteDeadline:  2 * time.Second,
 		Cluster: ClusterOpts{
+			Name: "abc",
 			Host: "127.0.0.1",
 			Port: -1,
 		},
@@ -289,6 +291,7 @@ func TestConfigReload(t *testing.T) {
 		MaxPingsOut:    2,
 		WriteDeadline:  2 * time.Second,
 		Cluster: ClusterOpts{
+			Name: "abc",
 			Host: "127.0.0.1",
 			Port: server.ClusterAddr().Port,
 		},
diff --git a/server/routes_test.go b/server/routes_test.go
index 5363362a..d7b7cc06 100644
--- a/server/routes_test.go
+++ b/server/routes_test.go
@@ -70,6 +70,7 @@ func TestRouteConfig(t *testing.T) {
 		Password:    "porkchop",
 		AuthTimeout: 1.0,
 		Cluster: ClusterOpts{
+			Name:           "abc",
 			Host:           "127.0.0.1",
 			Port:           4244,
 			Username:       "route_user",
@@ -1204,6 +1205,7 @@ func TestRouteRTT(t *testing.T) {
 func TestRouteCloseTLSConnection(t *testing.T) {
 	opts := DefaultOptions()
 	opts.DisableShortFirstPing = true
+	opts.Cluster.Name = "A"
 	opts.Cluster.Host = "127.0.0.1"
 	opts.Cluster.Port = -1
 	opts.Cluster.TLSTimeout = 100
@@ -1234,7 +1236,7 @@ func TestRouteCloseTLSConnection(t *testing.T) {
 	if err := tlsConn.Handshake(); err != nil {
 		t.Fatalf("Unexpected error during handshake: %v", err)
 	}
-	connectOp := []byte("CONNECT {\"name\":\"route\",\"verbose\":false,\"pedantic\":false,\"tls_required\":true}\r\n")
+	connectOp := []byte("CONNECT {\"name\":\"route\",\"verbose\":false,\"pedantic\":false,\"tls_required\":true,\"cluster\":\"A\"}\r\n")
 	if _, err := tlsConn.Write(connectOp); err != nil {
 		t.Fatalf("Unexpected error writing CONNECT: %v", err)
 	}
diff --git a/server/server_test.go b/server/server_test.go
index 0bd1f364..1be7607f 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -58,7 +58,7 @@ func DefaultOptions() *Options {
 		Host:     "127.0.0.1",
 		Port:     -1,
 		HTTPPort: -1,
-		Cluster:  ClusterOpts{Port: -1},
+		Cluster:  ClusterOpts{Port: -1, Name: "abc"},
 		NoLog:    true,
 		NoSigs:   true,
 		Debug:    true,
@@ -900,6 +900,7 @@ func TestLameDuckMode(t *testing.T) {
 
 func TestLameDuckModeInfo(t *testing.T) {
 	optsA := testWSOptions()
+	optsA.Cluster.Name = "abc"
 	optsA.Cluster.Host = "127.0.0.1"
 	optsA.Cluster.Port = -1
 	// Ensure that initial delay is set very high so that we can
@@ -947,6 +948,7 @@ func TestLameDuckModeInfo(t *testing.T) {
 	client.ReadString('\n')
 
 	optsB := testWSOptions()
+	optsB.Cluster.Name = "abc"
 	optsB.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", srvA.ClusterAddr().Port))
 	srvB := RunServer(optsB)
 	defer srvB.Shutdown()
@@ -974,6 +976,7 @@ func TestLameDuckModeInfo(t *testing.T) {
 
 	optsC := testWSOptions()
 	testSetLDMGracePeriod(optsA, 5*time.Second)
+	optsC.Cluster.Name = "abc"
 	optsC.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", srvA.ClusterAddr().Port))
 	srvC := RunServer(optsC)
 	defer srvC.Shutdown()
@@ -986,6 +989,7 @@ func TestLameDuckModeInfo(t *testing.T) {
 	checkConnectURLs(expected)
 
 	optsD := testWSOptions()
+	optsD.Cluster.Name = "abc"
 	optsD.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", srvA.ClusterAddr().Port))
 	srvD := RunServer(optsD)
 	defer srvD.Shutdown()
@@ -1341,6 +1345,7 @@ func TestInsecureSkipVerifyWarning(t *testing.T) {
 	}
 
 	o := DefaultOptions()
+	o.Cluster.Name = "A"
 	o.Cluster.Port = -1
 	o.Cluster.TLSConfig = config.Clone()
 	checkWarnReported(t, o, clusterTLSInsecureWarning)
@@ -1516,6 +1521,7 @@ func TestConnectErrorReports(t *testing.T) {
 
 	// Now try with gateways
 	opts.LeafNode.Remotes = nil
+	opts.Cluster.Name = "A"
 	opts.Gateway.Name = "A"
 	opts.Gateway.Port = -1
 	opts.Gateway.Gateways = []*RemoteGatewayOpts{
@@ -1695,11 +1701,13 @@ func TestReconnectErrorReports(t *testing.T) {
 
 	// Now try with gateways
 	csOpts.LeafNode.Port = 0
+	csOpts.Cluster.Name = "B"
 	csOpts.Gateway.Name = "B"
 	csOpts.Gateway.Port = -1
 	cs = RunServer(csOpts)
 
 	opts.LeafNode.Remotes = nil
+	opts.Cluster.Name = "A"
 	opts.Gateway.Name = "A"
 	opts.Gateway.Port = -1
 	remoteGWPort := cs.GatewayAddr().Port
@@ -1747,37 +1755,3 @@ func TestReconnectErrorReports(t *testing.T) {
 		})
 	}
 }
-
-func TestServerLogsConfigurationFile(t *testing.T) {
-	tmpDir, err := ioutil.TempDir("", "_nats-server")
-	if err != nil {
-		t.Fatal("Could not create tmp dir")
-	}
-	defer os.RemoveAll(tmpDir)
-
-	file, err := ioutil.TempFile(tmpDir, "nats_server_log_")
-	if err != nil {
-		t.Fatalf("Could not create the temp file: %v", err)
-	}
-	file.Close()
-
-	conf := createConfFile(t, []byte(fmt.Sprintf(`
-	port: -1
-	logfile: "%s"
-	`, file.Name())))
-	defer os.Remove(conf)
-
-	o := LoadConfig(conf)
-	o.ConfigFile = file.Name()
-	o.NoLog = false
-	s := RunServer(o)
-	s.Shutdown()
-
-	log, err := ioutil.ReadFile(file.Name())
-	if err != nil {
-		t.Fatalf("Error reading log file: %v", err)
-	}
-	if !bytes.Contains(log, []byte(fmt.Sprintf("Using configuration file: %s", file.Name()))) {
-		t.Fatalf("Config file location was not reported in log: %s", log)
-	}
-}
diff --git a/server/websocket_test.go b/server/websocket_test.go
index 9a625a38..7e646bed 100644
--- a/server/websocket_test.go
+++ b/server/websocket_test.go
@@ -1878,64 +1878,53 @@ func TestWSTLSVerifyClientCert(t *testing.T) {
 }
 
 func TestWSTLSVerifyAndMap(t *testing.T) {
-	accName := "MyAccount"
-	acc := NewAccount(accName)
+	o := testWSOptions()
 	certUserName := "CN=example.com,OU=NATS.io"
-	users := []*User{&User{Username: certUserName, Account: acc}}
+	o.Users = []*User{&User{Username: certUserName}}
+	tc := &TLSConfigOpts{
+		CertFile: "../test/configs/certs/tlsauth/server.pem",
+		KeyFile:  "../test/configs/certs/tlsauth/server-key.pem",
+		CaFile:   "../test/configs/certs/tlsauth/ca.pem",
+		Verify:   true,
+	}
+	tlsc, err := GenTLSConfig(tc)
+	if err != nil {
+		t.Fatalf("Error creating tls config: %v", err)
+	}
+	o.Websocket.TLSConfig = tlsc
+	o.Websocket.TLSMap = true
+	s := RunServer(o)
+	defer s.Shutdown()
+
+	addr := fmt.Sprintf("%s:%d", o.Websocket.Host, o.Websocket.Port)
 
 	for _, test := range []struct {
 		name        string
-		wsUsers     bool
 		provideCert bool
 	}{
-		{"no users override, client provides cert", false, true},
-		{"no users override, client does not provide cert", false, false},
-		{"sers override, client provides cert", true, true},
-		{"users override, client does not provide cert", true, false},
+		{"client provides cert", true},
+		{"client does not provide cert", false},
 	} {
 		t.Run(test.name, func(t *testing.T) {
-			o := testWSOptions()
-			o.Accounts = []*Account{acc}
-			if test.wsUsers {
-				o.Websocket.Users = users
-			} else {
-				o.Users = users
-			}
-			tc := &TLSConfigOpts{
-				CertFile: "../test/configs/certs/tlsauth/server.pem",
-				KeyFile:  "../test/configs/certs/tlsauth/server-key.pem",
-				CaFile:   "../test/configs/certs/tlsauth/ca.pem",
-				Verify:   true,
-			}
-			tlsc, err := GenTLSConfig(tc)
-			if err != nil {
-				t.Fatalf("Error creating tls config: %v", err)
-			}
-			o.Websocket.TLSConfig = tlsc
-			o.Websocket.TLSMap = true
-			s := RunServer(o)
-			defer s.Shutdown()
-
-			addr := fmt.Sprintf("%s:%d", o.Websocket.Host, o.Websocket.Port)
 			wsc, err := net.Dial("tcp", addr)
 			if err != nil {
 				t.Fatalf("Error creating ws connection: %v", err)
 			}
 			defer wsc.Close()
-			tlscc := &tls.Config{}
+			tlsc := &tls.Config{}
 			if test.provideCert {
 				tc := &TLSConfigOpts{
 					CertFile: "../test/configs/certs/tlsauth/client.pem",
 					KeyFile:  "../test/configs/certs/tlsauth/client-key.pem",
 				}
 				var err error
-				tlscc, err = GenTLSConfig(tc)
+				tlsc, err = GenTLSConfig(tc)
 				if err != nil {
 					t.Fatalf("Error generating tls config: %v", err)
 				}
 			}
-			tlscc.InsecureSkipVerify = true
-			wsc = tls.Client(wsc, tlscc)
+			tlsc.InsecureSkipVerify = true
+			wsc = tls.Client(wsc, tlsc)
 			if err := wsc.(*tls.Conn).Handshake(); err != nil {
 				t.Fatalf("Error during handshake: %v", err)
 			}
@@ -1982,22 +1971,12 @@ func TestWSTLSVerifyAndMap(t *testing.T) {
 				t.Fatalf("Expected PONG, got %s", msg)
 			}
 
-			var uname string
-			var accname string
 			c := s.getClient(info.CID)
-			if c != nil {
-				c.mu.Lock()
-				uname = c.opts.Username
-				if c.acc != nil {
-					accname = c.acc.GetName()
-				}
-				c.mu.Unlock()
-			}
-			if uname != certUserName {
-				t.Fatalf("Expected username %q, got %q", certUserName, uname)
-			}
-			if accname != accName {
-				t.Fatalf("Expected account %q, got %v", accName, accname)
+			c.mu.Lock()
+			un := c.opts.Username
+			c.mu.Unlock()
+			if un != certUserName {
+				t.Fatalf("Expected client's assigned username to be %q, got %q", certUserName, un)
 			}
 		})
 	}
@@ -2967,37 +2946,18 @@ func TestWSUsersAuth(t *testing.T) {
 	}
 }
 
-func TestWSNoAuthUserValidation(t *testing.T) {
-	// It is illegal to configure a websocket's NoAuthUser if websocket's
-	// auth config does not have a matching User.
-	// Create regular clients's User to make sure that we fail even if
-	// the websocket's NoAuthUser is found in opts.Users.
-	o := testWSOptions()
-	o.Users = []*User{&User{Username: "user", Password: "pwd"}}
-	o.Websocket.NoAuthUser = "user"
-	if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), "users are not") {
-		t.Fatalf("Expected error saying that users are not configured, got %v", err)
-	}
-
-	o.Websocket.Users = []*User{&User{Username: "wsuser", Password: "pwd"}}
-	o.Websocket.NoAuthUser = "notfound"
-	if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), "not found") {
-		t.Fatalf("Expected error saying no auth user not found, got %v", err)
-	}
-}
-
 func TestWSNoAuthUser(t *testing.T) {
 	for _, test := range []struct {
 		name          string
+		noAuthUser    string
+		wsNoAuthUser  string
+		user          string
+		acc           string
 		createWSUsers bool
-		useAuth       bool
-		expectedUser  string
-		expectedAcc   string
 	}{
-		{"no override, no user provided", false, false, "noauth", "normal"},
-		{"no override, user povided", false, true, "user", "normal"},
-		{"override, no user provided", true, false, "wsnoauth", "websocket"},
-		{"override, user provided", true, true, "wsuser", "websocket"},
+		{"use top-level no_auth_user", "user1", "", "user1", "normal", false},
+		{"use websocket no_auth_user no ws users", "user1", "user2", "user2", "normal", false},
+		{"use websocket no_auth_user with ws users", "user1", "wsuser1", "wsuser1", "websocket", true},
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			o := testWSOptions()
@@ -3005,16 +2965,16 @@ func TestWSNoAuthUser(t *testing.T) {
 			websocketAcc := NewAccount("websocket")
 			o.Accounts = []*Account{normalAcc, websocketAcc}
 			o.Users = []*User{
-				&User{Username: "noauth", Password: "pwd", Account: normalAcc},
-				&User{Username: "user", Password: "pwd", Account: normalAcc},
+				&User{Username: "user1", Password: "pwd1", Account: normalAcc},
+				&User{Username: "user2", Password: "pwd2", Account: normalAcc},
 			}
-			o.NoAuthUser = "noauth"
+			o.NoAuthUser = test.noAuthUser
+			o.Websocket.NoAuthUser = test.wsNoAuthUser
 			if test.createWSUsers {
 				o.Websocket.Users = []*User{
-					&User{Username: "wsnoauth", Password: "pwd", Account: websocketAcc},
-					&User{Username: "wsuser", Password: "pwd", Account: websocketAcc},
+					&User{Username: "wsuser1", Password: "pwd1", Account: websocketAcc},
+					&User{Username: "wsuser2", Password: "pwd2", Account: websocketAcc},
 				}
-				o.Websocket.NoAuthUser = "wsnoauth"
 			}
 			s := RunServer(o)
 			defer s.Shutdown()
@@ -3025,13 +2985,7 @@ func TestWSNoAuthUser(t *testing.T) {
 			var info serverInfo
 			json.Unmarshal([]byte(l[5:]), &info)
 
-			var connectProto string
-			if test.useAuth {
-				connectProto = fmt.Sprintf("CONNECT {\"verbose\":false,\"protocol\":1,\"user\":\"%s\",\"pass\":\"pwd\"}\r\nPING\r\n",
-					test.expectedUser)
-			} else {
-				connectProto = "CONNECT {\"verbose\":false,\"protocol\":1}\r\nPING\r\n"
-			}
+			connectProto := "CONNECT {\"verbose\":false,\"protocol\":1}\r\nPING\r\n"
 			wsmsg := testWSCreateClientMsg(wsBinaryMessage, 1, true, false, []byte(connectProto))
 			if _, err := wsc.Write(wsmsg); err != nil {
 				t.Fatalf("Error sending message: %v", err)
@@ -3046,11 +3000,11 @@ func TestWSNoAuthUser(t *testing.T) {
 			uname := c.opts.Username
 			aname := c.acc.GetName()
 			c.mu.Unlock()
-			if uname != test.expectedUser {
-				t.Fatalf("Expected selected user to be %q, got %q", test.expectedUser, uname)
+			if uname != test.user {
+				t.Fatalf("Expected selected user to be %q, got %q", test.user, uname)
 			}
-			if aname != test.expectedAcc {
-				t.Fatalf("Expected selected account to be %q, got %q", test.expectedAcc, aname)
+			if aname != test.acc {
+				t.Fatalf("Expected selected account to be %q, got %q", test.acc, aname)
 			}
 		})
 	}
diff --git a/test/cluster_test.go b/test/cluster_test.go
index a2e01f12..1a0dd7b8 100644
--- a/test/cluster_test.go
+++ b/test/cluster_test.go
@@ -1,4 +1,4 @@
-// Copyright 2013-2019 The NATS Authors
+// Copyright 2013-2020 The NATS Authors
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -16,7 +16,10 @@ package test
 import (
 	"errors"
 	"fmt"
+	"math/rand"
+	"os"
 	"runtime"
+	"strings"
 	"testing"
 	"time"
 
@@ -515,3 +518,140 @@ func TestAutoUnsubscribePropagationOnClientDisconnect(t *testing.T) {
 		t.Fatalf("%v", err)
 	}
 }
+
+func TestClusterNameOption(t *testing.T) {
+	conf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		cluster {
+			name: MyCluster
+			listen: 127.0.0.1:-1
+		}
+	`))
+	defer os.Remove(conf)
+
+	s, opts := RunServerWithConfig(conf)
+	defer s.Shutdown()
+
+	c := createClientConn(t, opts.Host, opts.Port)
+	defer c.Close()
+
+	si := checkInfoMsg(t, c)
+	if si.Cluster != "MyCluster" {
+		t.Fatalf("Expected a cluster name of %q, got %q", "MyCluster", si.Cluster)
+	}
+}
+
+func TestEphemeralClusterName(t *testing.T) {
+	conf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		cluster {
+			listen: 127.0.0.1:-1
+		}
+	`))
+	defer os.Remove(conf)
+
+	s, opts := RunServerWithConfig(conf)
+	defer s.Shutdown()
+
+	c := createClientConn(t, opts.Host, opts.Port)
+	defer c.Close()
+
+	si := checkInfoMsg(t, c)
+	if si.Cluster == "" {
+		t.Fatalf("Expected an ephemeral cluster name to be set")
+	}
+}
+
+type captureErrLogger struct {
+	dummyLogger
+	ch chan string
+}
+
+func (c *captureErrLogger) Errorf(format string, v ...interface{}) {
+	msg := fmt.Sprintf(format, v...)
+	select {
+	case c.ch <- msg:
+	default:
+	}
+}
+
+func TestClusterNameConflictsDropRoutes(t *testing.T) {
+	ll := &captureErrLogger{ch: make(chan string, 4)}
+
+	conf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		cluster {
+			name: MyCluster33
+			listen: 127.0.0.1:5244
+		}
+	`))
+	defer os.Remove(conf)
+
+	s1, _ := RunServerWithConfig(conf)
+	defer s1.Shutdown()
+	s1.SetLogger(ll, false, false)
+
+	conf2 := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		cluster {
+			name: MyCluster22
+			listen: 127.0.0.1:-1
+			routes = [nats-route://127.0.0.1:5244]
+		}
+	`))
+	defer os.Remove(conf2)
+
+	s2, _ := RunServerWithConfig(conf2)
+	defer s2.Shutdown()
+	s2.SetLogger(ll, false, false)
+
+	select {
+	case msg := <-ll.ch:
+		if !strings.Contains(msg, "Rejecting connection") || !strings.Contains(msg, "does not match") {
+			t.Fatalf("Got bad error about cluster name mismatch")
+		}
+	case <-time.After(time.Second):
+		t.Fatalf("Expected an error, timed out")
+	}
+}
+
+func TestClusterNameDynamicNegotiation(t *testing.T) {
+	conf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		cluster {listen: 127.0.0.1:5244}
+	`))
+	defer os.Remove(conf)
+
+	seed, _ := RunServerWithConfig(conf)
+	defer seed.Shutdown()
+
+	oconf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		cluster {
+			listen: 127.0.0.1:-1
+			routes = [nats-route://127.0.0.1:5244]
+		}
+	`))
+	defer os.Remove(oconf)
+
+	// Create a random number of additional servers, up to 20.
+	numServers := rand.Intn(20) + 1
+	servers := make([]*server.Server, 0, numServers+1)
+	servers = append(servers, seed)
+
+	for i := 0; i < numServers; i++ {
+		s, _ := RunServerWithConfig(oconf)
+		defer s.Shutdown()
+		servers = append(servers, s)
+	}
+
+	// If this passes we should have all the same name.
+	checkClusterFormed(t, servers...)
+
+	clusterName := seed.ClusterName()
+	for _, s := range servers {
+		if s.ClusterName() != clusterName {
+			t.Fatalf("Expected the cluster names to all be the same as %q, got %q", clusterName, s.ClusterName())
+		}
+	}
+}
diff --git a/test/cluster_tls_test.go b/test/cluster_tls_test.go
index f9788ad2..b19c46ee 100644
--- a/test/cluster_tls_test.go
+++ b/test/cluster_tls_test.go
@@ -101,6 +101,7 @@ func TestClusterTLSInsecure(t *testing.T) {
 	confA := createConfFile(t, []byte(`
 		port: -1
 		cluster {
+			name: "xyz"
 			listen: "127.0.0.1:-1"
 			tls {
 			    cert_file: "./configs/certs/server-noip.pem"
@@ -119,6 +120,7 @@ func TestClusterTLSInsecure(t *testing.T) {
 	bConfigTemplate := `
 		port: -1
 		cluster {
+			name: "xyz"
 			listen: "127.0.0.1:-1"
 			tls {
 			    cert_file: "./configs/certs/server-noip.pem"
diff --git a/test/configs/auth_seed.conf b/test/configs/auth_seed.conf
index a75dfc0a..893eb716 100644
--- a/test/configs/auth_seed.conf
+++ b/test/configs/auth_seed.conf
@@ -6,6 +6,7 @@ http: 8222
 
 cluster {
   listen: 127.0.0.1:4248
+  name: xyz
 
   authorization {
     user: ruser
diff --git a/test/configs/cluster.conf b/test/configs/cluster.conf
index a96167af..5894553a 100644
--- a/test/configs/cluster.conf
+++ b/test/configs/cluster.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:5242
 
 cluster {
   listen: 127.0.0.1:5244
+  name: xyz
 
   authorization {
     user: route_user
diff --git a/test/configs/new_cluster.conf b/test/configs/new_cluster.conf
index 817695a5..166b80d7 100644
--- a/test/configs/new_cluster.conf
+++ b/test/configs/new_cluster.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:5343
 
 cluster {
   listen: 127.0.0.1:5344
+  name: xyz
 
   # Routes are actively solicited and connected to from this server.
   # Other servers can connect to us if they supply the correct credentials
diff --git a/test/configs/seed.conf b/test/configs/seed.conf
index cc29c8bc..13f6462a 100644
--- a/test/configs/seed.conf
+++ b/test/configs/seed.conf
@@ -6,6 +6,7 @@ http: 8222
 
 cluster {
   listen: 127.0.0.1:4248
+  name: xyz
 }
 
 no_sys_acc: true
diff --git a/test/configs/srv_a.conf b/test/configs/srv_a.conf
index 9bf29532..13a5a77b 100644
--- a/test/configs/srv_a.conf
+++ b/test/configs/srv_a.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:5222
 
 cluster {
   listen: 127.0.0.1:5244
+  name: xyz
 
   authorization {
     user: ruser
diff --git a/test/configs/srv_a_leaf.conf b/test/configs/srv_a_leaf.conf
index 4a8f6c6d..95a2d97f 100644
--- a/test/configs/srv_a_leaf.conf
+++ b/test/configs/srv_a_leaf.conf
@@ -8,6 +8,7 @@ leafnodes {
 
 cluster {
   listen: 127.0.0.1:5244
+  name: xyz
 
   authorization {
     user: ruser
diff --git a/test/configs/srv_a_perms.conf b/test/configs/srv_a_perms.conf
index 08974fa4..96883c34 100644
--- a/test/configs/srv_a_perms.conf
+++ b/test/configs/srv_a_perms.conf
@@ -5,6 +5,7 @@ http: 127.0.0.1:5223
 
 cluster {
   listen: 127.0.0.1:5244
+  name: xyz
 
   authorization {
     user: ruser
diff --git a/test/configs/srv_a_tls.conf b/test/configs/srv_a_tls.conf
index 6a920251..735940df 100644
--- a/test/configs/srv_a_tls.conf
+++ b/test/configs/srv_a_tls.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:5222
 
 cluster {
   listen: 127.0.0.1:5244
+  name: xyz
 
   tls {
     # Route cert
diff --git a/test/configs/srv_b.conf b/test/configs/srv_b.conf
index 5ad067c3..32d09f3a 100644
--- a/test/configs/srv_b.conf
+++ b/test/configs/srv_b.conf
@@ -5,6 +5,7 @@ http: 127.0.0.1:5225
 
 cluster {
   listen: 127.0.0.1:5246
+  name: xyz
 
   authorization {
     user: ruser
diff --git a/test/configs/srv_b_tls.conf b/test/configs/srv_b_tls.conf
index 61e2784c..1047b999 100644
--- a/test/configs/srv_b_tls.conf
+++ b/test/configs/srv_b_tls.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:5224
 
 cluster {
   listen: 127.0.0.1:5246
+  name: xyz
 
   tls {
     # Route cert
diff --git a/test/configs/srv_c.conf b/test/configs/srv_c.conf
index b3b8fe5a..31def469 100644
--- a/test/configs/srv_c.conf
+++ b/test/configs/srv_c.conf
@@ -4,6 +4,7 @@ listen: 127.0.0.1:5226
 
 cluster {
   listen: 127.0.0.1:5248
+  name: xyz
 
   authorization {
     user: ruser
diff --git a/test/leafnode_test.go b/test/leafnode_test.go
index 6d629075..58305752 100644
--- a/test/leafnode_test.go
+++ b/test/leafnode_test.go
@@ -1633,6 +1633,7 @@ func TestLeadNodeExportImportComplexSetup(t *testing.T) {
 	resolver = MEMORY
 	cluster {
 		port: -1
+		name: xyz
 	}
 	leafnodes {
 		listen: "127.0.0.1:-1"
@@ -1649,6 +1650,7 @@ func TestLeadNodeExportImportComplexSetup(t *testing.T) {
 	resolver = MEMORY
 	cluster {
 		port: -1
+		name: xyz
 		routes: ["nats://%s:%d"]
 	}
 	leafnodes {
@@ -1811,6 +1813,7 @@ func TestLeafNodeInfoURLs(t *testing.T) {
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			opts := testDefaultOptionsForLeafNodes()
+			opts.Cluster.Name = "A"
 			opts.Cluster.Port = -1
 			opts.LeafNode.Host = "127.0.0.1"
 			if test.useAdvertise {
@@ -1837,6 +1840,7 @@ func TestLeafNodeInfoURLs(t *testing.T) {
 			lc.Close()
 
 			opts2 := testDefaultOptionsForLeafNodes()
+			opts2.Cluster.Name = "A"
 			opts2.Cluster.Port = -1
 			opts2.Routes = server.RoutesFromStr(fmt.Sprintf("nats://%s:%d", opts.Cluster.Host, opts.Cluster.Port))
 			opts2.LeafNode.Host = "127.0.0.1"
@@ -2022,11 +2026,13 @@ func TestLeafNodeAdvertise(t *testing.T) {
 
 	o2 := testDefaultOptionsForLeafNodes()
 	o2.LeafNode.Advertise = fmt.Sprintf("127.0.0.1:%d", port)
+	o2.Cluster.Name = "A"
 	o2.Cluster.Port = -1
 	s2 := RunServer(o2)
 	defer s2.Shutdown()
 
 	o1 := testDefaultOptionsForLeafNodes()
+	o1.Cluster.Name = "A"
 	o1.Cluster.Port = -1
 	o1.Routes = server.RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", o2.Cluster.Port))
 	s1 := RunServer(o1)
@@ -2175,6 +2181,7 @@ func TestLeafNodeConnectionLimitsCluster(t *testing.T) {
 	resolver = MEMORY
 	cluster {
 		port: -1
+		name: xyz
 	}
 	leafnodes {
 		listen: "127.0.0.1:-1"
@@ -2195,6 +2202,7 @@ func TestLeafNodeConnectionLimitsCluster(t *testing.T) {
 	resolver = MEMORY
 	cluster {
 		port: -1
+		name: xyz
 		routes: ["nats://%s:%d"]
 	}
 	leafnodes {
@@ -2353,9 +2361,10 @@ func TestLeafNodeSwitchGatewayToInterestModeOnly(t *testing.T) {
 // route connections to simulate.
 func TestLeafNodeResetsMSGProto(t *testing.T) {
 	opts := testDefaultOptionsForLeafNodes()
+	opts.Cluster.Name = "xyz"
 	opts.Cluster.Host = opts.Host
 	opts.Cluster.Port = -1
-	opts.Gateway.Name = "lproto"
+	opts.Gateway.Name = "xyz"
 	opts.Gateway.Host = opts.Host
 	opts.Gateway.Port = -1
 	opts.Accounts = []*server.Account{server.NewAccount("$SYS")}
@@ -2376,7 +2385,7 @@ func TestLeafNodeResetsMSGProto(t *testing.T) {
 	gw := createGatewayConn(t, opts.Gateway.Host, opts.Gateway.Port)
 	defer gw.Close()
 
-	gwSend, gwExpect := setupGatewayConn(t, gw, "A", "lproto")
+	gwSend, gwExpect := setupGatewayConn(t, gw, "A", "xyz")
 	gwSend("PING\r\n")
 	gwExpect(pongRe)
 
@@ -2959,6 +2968,7 @@ func runSolicitLeafCluster(t *testing.T, clusterName string, d1, d2 *cluster) *c
 	rurl, _ := url.Parse(surl)
 	o.LeafNode.Remotes = []*server.RemoteLeafOpts{{URLs: []*url.URL{rurl}}}
 	o.LeafNode.ReconnectInterval = 100 * time.Millisecond
+	o.Cluster.Name = clusterName
 	o.Cluster.Host = o.Host
 	o.Cluster.Port = -1
 	s := RunServer(&o)
@@ -2984,6 +2994,7 @@ func runSolicitLeafCluster(t *testing.T, clusterName string, d1, d2 *cluster) *c
 	rurl, _ = url.Parse(surl)
 	o2.LeafNode.Remotes = []*server.RemoteLeafOpts{{URLs: []*url.URL{rurl}}}
 	o2.LeafNode.ReconnectInterval = 100 * time.Millisecond
+	o2.Cluster.Name = clusterName
 	o2.Cluster.Host = o.Host
 	o2.Cluster.Port = -1
 	o2.Routes = []*url.URL{curl}
@@ -3102,6 +3113,7 @@ func TestLeafNodeCycleWithSolicited(t *testing.T) {
 func TestLeafNodeNoRaceGeneratingNonce(t *testing.T) {
 	opts := testDefaultOptionsForLeafNodes()
 	opts.Cluster.Port = -1
+	opts.Cluster.Name = "xyz"
 	s := RunServer(opts)
 	defer s.Shutdown()
 
@@ -3212,6 +3224,7 @@ func TestClusterTLSMixedIPAndDNS(t *testing.T) {
 		}
 		cluster {
 			listen: "127.0.0.1:-1"
+			name: xyz
 		}
 	`))
 	srvA, optsA := RunServerWithConfig(confA)
@@ -3230,6 +3243,7 @@ func TestClusterTLSMixedIPAndDNS(t *testing.T) {
 		}
 		cluster {
 			listen: "127.0.0.1:-1"
+			name: xyz
 			routes [
 				"nats://%s:%d"
 			]
diff --git a/test/monitor_test.go b/test/monitor_test.go
index 9007aa99..d103b68c 100644
--- a/test/monitor_test.go
+++ b/test/monitor_test.go
@@ -51,7 +51,7 @@ func runMonitorServerClusteredPair(t *testing.T) (*server.Server, *server.Server
 	opts.Port = CLIENT_PORT
 	opts.HTTPPort = MONITOR_PORT
 	opts.HTTPHost = "127.0.0.1"
-	opts.Cluster = server.ClusterOpts{Host: "127.0.0.1", Port: 10223}
+	opts.Cluster = server.ClusterOpts{Name: "M", Host: "127.0.0.1", Port: 10223}
 	opts.Routes = server.RoutesFromStr("nats-route://127.0.0.1:10222")
 	opts.NoSystemAccount = true
 
@@ -61,7 +61,7 @@ func runMonitorServerClusteredPair(t *testing.T) (*server.Server, *server.Server
 	opts2.Port = CLIENT_PORT + 1
 	opts2.HTTPPort = MONITOR_PORT + 1
 	opts2.HTTPHost = "127.0.0.1"
-	opts2.Cluster = server.ClusterOpts{Host: "127.0.0.1", Port: 10222}
+	opts2.Cluster = server.ClusterOpts{Name: "M", Host: "127.0.0.1", Port: 10222}
 	opts2.Routes = server.RoutesFromStr("nats-route://127.0.0.1:10223")
 	opts2.NoSystemAccount = true
 
diff --git a/test/operator_test.go b/test/operator_test.go
index 47d549d3..2ee585c3 100644
--- a/test/operator_test.go
+++ b/test/operator_test.go
@@ -333,6 +333,7 @@ func TestReloadDoesNotWipeAccountsWithOperatorMode(t *testing.T) {
 	cf := `
 	listen: 127.0.0.1:-1
 	cluster {
+		name: "A"
 		listen: 127.0.0.1:-1
 		authorization {
 			timeout: 2.2
@@ -456,6 +457,7 @@ func TestReloadDoesUpdateAccountsWithMemoryResolver(t *testing.T) {
 	cf := `
 	listen: 127.0.0.1:-1
 	cluster {
+		name: "A"
 		listen: 127.0.0.1:-1
 		authorization {
 			timeout: 2.2
@@ -547,6 +549,7 @@ func TestReloadFailsWithBadAccountsWithMemoryResolver(t *testing.T) {
 	cf := `
 	listen: 127.0.0.1:-1
 	cluster {
+		name: "A"
 		listen: 127.0.0.1:-1
 		authorization {
 			timeout: 2.2
@@ -610,6 +613,7 @@ func TestConnsRequestDoesNotLoadAccountCheckingConnLimits(t *testing.T) {
 	cf := `
 	listen: 127.0.0.1:-1
 	cluster {
+		name: "A"
 		listen: 127.0.0.1:-1
 		authorization {
 			timeout: 2.2
diff --git a/test/opts_test.go b/test/opts_test.go
index 0275989e..ae7f69e0 100644
--- a/test/opts_test.go
+++ b/test/opts_test.go
@@ -13,7 +13,9 @@
 
 package test
 
-import "testing"
+import (
+	"testing"
+)
 
 func TestServerConfig(t *testing.T) {
 	srv, opts := RunServerWithConfig("./configs/override.conf")
diff --git a/test/routes_test.go b/test/routes_test.go
index a40cdb14..b4d8816c 100644
--- a/test/routes_test.go
+++ b/test/routes_test.go
@@ -603,6 +603,7 @@ func TestRouteSendAsyncINFOToClients(t *testing.T) {
 		sendRouteINFO := func(routeSend sendFun, routeExpect expectFun, urls []string) {
 			routeInfo := server.Info{}
 			routeInfo.ID = routeID
+			routeInfo.Cluster = "xyz"
 			routeInfo.Host = "127.0.0.1"
 			routeInfo.Port = 5222
 			routeInfo.ClientConnectURLs = urls
@@ -1122,6 +1123,7 @@ func TestRoutesOnlyImportOrExport(t *testing.T) {
 		cf := createConfFile(t, []byte(fmt.Sprintf(`
 			port: -1
 			cluster {
+				name: "Z"
 				port: -1
 				authorization {
 					user: ivan
diff --git a/test/service_latency_test.go b/test/service_latency_test.go
index e19917e3..831056be 100644
--- a/test/service_latency_test.go
+++ b/test/service_latency_test.go
@@ -719,6 +719,7 @@ func TestServiceLatencyWithJWT(t *testing.T) {
 	cf := `
 	listen: 127.0.0.1:-1
 	cluster {
+		name: "A"
 		listen: 127.0.0.1:-1
 		authorization {
 			timeout: 2.2
diff --git a/test/test.go b/test/test.go
index 2f3d4ed0..81aedf6d 100644
--- a/test/test.go
+++ b/test/test.go
@@ -216,10 +216,10 @@ func doDefaultConnect(t tLogger, c net.Conn) {
 	doConnect(t, c, false, false, false)
 }
 
-const connectProto = "CONNECT {\"verbose\":false,\"user\":\"%s\",\"pass\":\"%s\",\"name\":\"%s\"}\r\n"
+const routeConnectProto = "CONNECT {\"verbose\":false,\"user\":\"%s\",\"pass\":\"%s\",\"name\":\"%s\",\"cluster\":\"xyz\"}\r\n"
 
 func doRouteAuthConnect(t tLogger, c net.Conn, user, pass, id string) {
-	cs := fmt.Sprintf(connectProto, user, pass, id)
+	cs := fmt.Sprintf(routeConnectProto, user, pass, id)
 	sendProto(t, c, cs)
 }
 
diff --git a/test/tls_test.go b/test/tls_test.go
index b5649198..8a470e36 100644
--- a/test/tls_test.go
+++ b/test/tls_test.go
@@ -553,6 +553,7 @@ func TestTLSRoutesCertificateCNBasedAuth(t *testing.T) {
 
 	optsA.Host = "127.0.0.1"
 	optsA.Port = 9335
+	optsA.Cluster.Name = "xyz"
 	optsA.Cluster.Host = optsA.Host
 	optsA.Cluster.Port = 9935
 	optsA.Routes = server.RoutesFromStr(routeURLs)
@@ -562,6 +563,7 @@ func TestTLSRoutesCertificateCNBasedAuth(t *testing.T) {
 
 	optsB.Host = "127.0.0.1"
 	optsB.Port = 9336
+	optsB.Cluster.Name = "xyz"
 	optsB.Cluster.Host = optsB.Host
 	optsB.Cluster.Port = 9936
 	optsB.Routes = server.RoutesFromStr(routeURLs)
@@ -571,6 +573,7 @@ func TestTLSRoutesCertificateCNBasedAuth(t *testing.T) {
 
 	optsC.Host = "127.0.0.1"
 	optsC.Port = 9337
+	optsC.Cluster.Name = "xyz"
 	optsC.Cluster.Host = optsC.Host
 	optsC.Cluster.Port = 9937
 	optsC.Routes = server.RoutesFromStr(routeURLs)