Skip to content

Commit ea7803b

Browse files
committed
Use non-redacted URLs when computing remote identity
Also, during configuration parsing, the duplicate detection will work but possibly before non normalized URLs (say without default leafnode port). But NewServer() will call validateOptions which will re-check for duplicates (after normalization). These changes have removed the caching of the remote identity in the RemoteLeafOpts object. Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
1 parent 86f7943 commit ea7803b

File tree

5 files changed

+55
-54
lines changed

5 files changed

+55
-54
lines changed

server/leafnode.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func validateLeafNode(o *Options) error {
218218
}
219219
rn := r.name()
220220
if _, dup := names[rn]; dup {
221-
return fmt.Errorf("duplicate remote %s", rn)
221+
return fmt.Errorf("duplicate remote %s", r.safeName())
222222
}
223223
names[rn] = struct{}{}
224224
}

server/opts.go

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -312,35 +312,36 @@ type RemoteLeafOpts struct {
312312
// existing connection will be closed and not solicited again (until it is changed
313313
// to `false` again.
314314
Disabled bool `json:"-"`
315-
316-
// Internal fields
317-
318-
// This is a string representation of the remote, containing the URLs (with
319-
// password redacted in case it is used in logging), the account (or "$G" if
320-
// LocalAccount is empty) and possibly the Credentials file name.
321-
// This should not be accessed directly and instead through name() since
322-
// the value will originally be empty and be set the first time name()
323-
// is invoked. Also, if in the future we add a public `Name` field, that field
324-
// could be returned instead of the internal representation.
325-
iname string
326315
}
327316

328317
// Returns a string representation of this `RemoteLeafOpts` object, containing
329-
// the URLs (redacted), the account (or "$G" if none is specified) and, if present,
318+
// the URLs (unredacted), the account (or "$G" if none is specified) and, if present,
330319
// the credentials filename.
331320
func (r *RemoteLeafOpts) name() string {
332-
if r.iname == _EMPTY_ {
333-
acc := r.LocalAccount
334-
if acc == _EMPTY_ {
335-
acc = globalAccountName
336-
}
337-
var creds string
338-
if c := r.Credentials; c != _EMPTY_ {
339-
creds = fmt.Sprintf(", credentials=%q", c)
340-
}
341-
r.iname = fmt.Sprintf("urls=%q, account=%q%s", redactURLList(r.URLs), acc, creds)
321+
return generateRemoteLeafOptsName(r, false)
322+
}
323+
324+
// Same than RemoteLeafOpts.name() but uses redacted URLs. This is to be used for logging.
325+
func (r *RemoteLeafOpts) safeName() string {
326+
return generateRemoteLeafOptsName(r, true)
327+
}
328+
329+
func generateRemoteLeafOptsName(r *RemoteLeafOpts, redacted bool) string {
330+
acc := r.LocalAccount
331+
if acc == _EMPTY_ {
332+
acc = globalAccountName
333+
}
334+
var creds string
335+
if c := r.Credentials; c != _EMPTY_ {
336+
creds = fmt.Sprintf(", credentials=%q", c)
337+
}
338+
var urls []*url.URL
339+
if redacted {
340+
urls = redactURLList(r.URLs)
341+
} else {
342+
urls = r.URLs
342343
}
343-
return r.iname
344+
return fmt.Sprintf("urls=%q, account=%q%s", urls, acc, creds)
344345
}
345346

346347
// JSLimitOpts are active limits for the meta cluster
@@ -3196,7 +3197,7 @@ func parseRemoteLeafNodes(v any, errors *[]error, warnings *[]error) ([]*RemoteL
31963197
}
31973198
rn := remote.name()
31983199
if _, dup := names[rn]; dup {
3199-
*errors = append(*errors, &configErr{tk, fmt.Sprintf("duplicate remote %s", rn)})
3200+
*errors = append(*errors, &configErr{tk, fmt.Sprintf("duplicate remote %s", remote.safeName())})
32003201
continue
32013202
}
32023203
names[rn] = struct{}{}

server/opts_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,8 +2425,6 @@ func TestParsingLeafNodeRemotes(t *testing.T) {
24252425
}
24262426
u, _ := url.Parse("nats-leaf://127.0.0.1:2222")
24272427
expected.URLs = append(expected.URLs, u)
2428-
// Force creation of the name for `expected`
2429-
expected.name()
24302428
if !reflect.DeepEqual(opts.LeafNode.Remotes[0], expected) {
24312429
t.Fatalf("Expected %v, got %v", expected, opts.LeafNode.Remotes[0])
24322430
}
@@ -2463,8 +2461,6 @@ func TestParsingLeafNodeRemotes(t *testing.T) {
24632461
}
24642462
u, _ := url.Parse("nats-leaf://127.0.0.1:2222")
24652463
expected.URLs = append(expected.URLs, u)
2466-
// Force creation of the name for `expected`
2467-
expected.name()
24682464
if !reflect.DeepEqual(opts.LeafNode.Remotes[0], expected) {
24692465
t.Fatalf("Expected %v, got %v", expected, opts.LeafNode.Remotes[0])
24702466
}
@@ -2608,10 +2604,6 @@ func TestParsingLeafNodeRemotes(t *testing.T) {
26082604
JetStreamClusterMigrate: false,
26092605
},
26102606
}
2611-
// Force creation of the name for `expected`
2612-
for _, e := range expected {
2613-
e.name()
2614-
}
26152607
if !reflect.DeepEqual(opts.LeafNode.Remotes, expected) {
26162608
t.Fatalf("Expected %v, got %v", expected, opts.LeafNode.Remotes)
26172609
}
@@ -4511,32 +4503,37 @@ func TestOptionsRemoteLeafNodeName(t *testing.T) {
45114503
require_NoError(t, err)
45124504
u2, err := url.Parse("nats://user2:secretpwd@127.0.0.1:7222")
45134505
require_NoError(t, err)
4514-
// We expect redacted versions of the URLs
4515-
urls := []string{"nats://user1:xxxxx@127.0.0.1:7222", "nats://user2:xxxxx@127.0.0.1:7222"}
4506+
// With unredacted versions of the URLs
4507+
urls := []*url.URL{u1, u2}
4508+
safeURLs := redactURLList(urls)
45164509
for _, test := range []struct {
4517-
name string
4518-
input *RemoteLeafOpts
4519-
output string
4510+
name string
4511+
input *RemoteLeafOpts
4512+
output string
4513+
safeOutput string
45204514
}{
45214515
{
45224516
"url only", &RemoteLeafOpts{
45234517
URLs: []*url.URL{u1, u2},
45244518
},
45254519
fmt.Sprintf("urls=%q, account=%q", urls, globalAccountName),
4520+
fmt.Sprintf("urls=%q, account=%q", safeURLs, globalAccountName),
45264521
},
45274522
{
45284523
"url with account", &RemoteLeafOpts{
45294524
URLs: []*url.URL{u1, u2},
45304525
LocalAccount: "A",
45314526
},
45324527
fmt.Sprintf("urls=%q, account=%q", urls, "A"),
4528+
fmt.Sprintf("urls=%q, account=%q", safeURLs, "A"),
45334529
},
45344530
{
45354531
"url with credentials", &RemoteLeafOpts{
45364532
URLs: []*url.URL{u1, u2},
45374533
Credentials: "credsfile",
45384534
},
45394535
fmt.Sprintf("urls=%q, account=%q, credentials=%q", urls, globalAccountName, "credsfile"),
4536+
fmt.Sprintf("urls=%q, account=%q, credentials=%q", safeURLs, globalAccountName, "credsfile"),
45404537
},
45414538
{
45424539
"url with account and credentials", &RemoteLeafOpts{
@@ -4545,18 +4542,21 @@ func TestOptionsRemoteLeafNodeName(t *testing.T) {
45454542
Credentials: "credsfile",
45464543
},
45474544
fmt.Sprintf("urls=%q, account=%q, credentials=%q", urls, "A", "credsfile"),
4545+
fmt.Sprintf("urls=%q, account=%q, credentials=%q", safeURLs, "A", "credsfile"),
45484546
},
45494547
} {
45504548
t.Run(test.name, func(t *testing.T) {
45514549
name := test.input.name()
45524550
require_Equal(t, name, test.output)
4551+
name = test.input.safeName()
4552+
require_Equal(t, name, test.safeOutput)
45534553
})
45544554
}
45554555

45564556
// Because we use `%q` when building the name, those two will have different
45574557
// names:
4558-
// r1=urls=["nats://user1:xxxxx@127.0.0.1:7222"], account="A", credentials="creds"
4559-
// r2=urls=["nats://user1:xxxxx@127.0.0.1:7222"], account="A\", credentials=\"creds"
4558+
// r1=urls=["nats://user1:secretpwd@127.0.0.1:7222"], account="A", credentials="creds"
4559+
// r2=urls=["nats://user1:secretpwd@127.0.0.1:7222"], account="A\", credentials=\"creds"
45604560
r1 := &RemoteLeafOpts{URLs: []*url.URL{u1}, LocalAccount: "A", Credentials: "creds"}
45614561
r2 := &RemoteLeafOpts{URLs: []*url.URL{u1}, LocalAccount: `A", credentials="creds`}
45624562
require_False(t, r1.name() == r2.name())

server/reload.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ DO_REMOTES:
944944
// list. If it is found, that function returns an updated list
945945
// with the element removed from it.
946946
lrc.RLock()
947-
rlo, nlo.added = getRemoteLeafOpts(lrc.RemoteLeafOpts, nlo.added)
947+
rlo, nlo.added = getRemoteLeafOpts(lrc.RemoteLeafOpts.name(), nlo.added)
948948
if rlo == nil {
949949
// Not found, will be removed in leafNodeOption.Apply().
950950
removed = true
@@ -964,7 +964,7 @@ DO_REMOTES:
964964
if err != nil {
965965
lrc.RUnlock()
966966
s.mu.RUnlock()
967-
return nil, fmt.Errorf(remoteErrFormat, rlo.name(), err)
967+
return nil, fmt.Errorf(remoteErrFormat, rlo.safeName(), err)
968968
}
969969
disabledChanged := lrc.Disabled != rlo.Disabled
970970
// If this remote was disabled and is now enabled, we need to make sure
@@ -976,7 +976,7 @@ DO_REMOTES:
976976
if failed++; failed < maxAttempts {
977977
goto DO_REMOTES
978978
}
979-
return nil, fmt.Errorf(remoteErrFormat, rlo.name(),
979+
return nil, fmt.Errorf(remoteErrFormat, rlo.safeName(),
980980
"cannot be enabled at the moment, try again")
981981
}
982982
// Since we will use the new `rlo.TLSConfig` later on, consider all
@@ -1005,7 +1005,7 @@ DO_REMOTES:
10051005
if failed++; failed < maxAttempts {
10061006
goto DO_REMOTES
10071007
}
1008-
return nil, fmt.Errorf(remoteErrFormat, rlo.name(),
1008+
return nil, fmt.Errorf(remoteErrFormat, rlo.safeName(),
10091009
"cannot be added at the moment, try again")
10101010
}
10111011
}
@@ -1050,12 +1050,12 @@ func usersHaveChanged(ousers, nusers []*User) bool {
10501050
return false
10511051
}
10521052

1053-
// Given the `search` remote leafnode options, search for a match in the `list`.
1053+
// Given the `search` remote leafnode options name, searches for a match in the `list`.
10541054
// If found, returns the `*RemoteLeafOpts` from the list, and the updated list
10551055
// without the element in it. If not found, returns `nil` and the unmodified list.
1056-
func getRemoteLeafOpts(search *RemoteLeafOpts, list []*RemoteLeafOpts) (*RemoteLeafOpts, []*RemoteLeafOpts) {
1056+
func getRemoteLeafOpts(search string, list []*RemoteLeafOpts) (*RemoteLeafOpts, []*RemoteLeafOpts) {
10571057
for i, rlo := range list {
1058-
if search.name() == rlo.name() {
1058+
if search == rlo.name() {
10591059
lastIdx := len(list) - 1
10601060
if lastIdx == 0 {
10611061
return rlo, nil
@@ -1098,7 +1098,7 @@ func (l *leafNodeOption) Apply(s *Server) {
10981098
}
10991099
s.rmLeafRemoteCfgs[lrc.name()] = lrc
11001100
lrc.markAsRemoved()
1101-
s.Noticef("Reloaded: LeafNode Remote %s removed", lrc.RemoteLeafOpts.name())
1101+
s.Noticef("Reloaded: LeafNode Remote %s removed", lrc.RemoteLeafOpts.safeName())
11021102
// We will close the existing connection in the next for-loop.
11031103
continue
11041104
}
@@ -1109,12 +1109,12 @@ func (l *leafNodeOption) Apply(s *Server) {
11091109
if rlo.tlsFirstChanged {
11101110
lrc.TLSHandshakeFirst = rlo.opts.TLSHandshakeFirst
11111111
s.Noticef("Reloaded: LeafNode Remote %s TLS HandshakeFirst value is: %v",
1112-
lrc.RemoteLeafOpts.name(), rlo.opts.TLSHandshakeFirst)
1112+
lrc.RemoteLeafOpts.safeName(), rlo.opts.TLSHandshakeFirst)
11131113
}
11141114
if rlo.compressionChanged {
11151115
lrc.Compression = rlo.opts.Compression
11161116
s.Noticef("Reloaded: LeafNode Remote %s Compression value is: %v",
1117-
lrc.RemoteLeafOpts.name(), rlo.opts.Compression)
1117+
lrc.RemoteLeafOpts.safeName(), rlo.opts.Compression)
11181118
}
11191119
if rlo.disabledChanged {
11201120
// Change to new value.
@@ -1125,7 +1125,7 @@ func (l *leafNodeOption) Apply(s *Server) {
11251125
enable = append(enable, lrc)
11261126
}
11271127
s.Noticef("Reloaded: LeafNode Remote %s Disabled value is: %v",
1128-
lrc.RemoteLeafOpts.name(), rlo.opts.Disabled)
1128+
lrc.RemoteLeafOpts.safeName(), rlo.opts.Disabled)
11291129
}
11301130
lrc.Unlock()
11311131
}

server/reload_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6494,7 +6494,7 @@ func TestConfigReloadGetRemoteLeafOpts(t *testing.T) {
64946494
{"list of three and found at pos 3", rlo3, []*RemoteLeafOpts{rlo1, rlo2, rlo3}, rlo3, []*RemoteLeafOpts{rlo1, rlo2}},
64956495
} {
64966496
t.Run(test.name, func(t *testing.T) {
6497-
rlo, list := getRemoteLeafOpts(test.search, test.listIn)
6497+
rlo, list := getRemoteLeafOpts(test.search.name(), test.listIn)
64986498
require_True(t, reflect.DeepEqual(list, test.listOut))
64996499
require_Equal(t, rlo, test.result)
65006500
})
@@ -6796,7 +6796,7 @@ func TestConfigReloadGetLeafNodeOptionsChanges(t *testing.T) {
67966796
return ts, old, new
67976797
},
67986798
nil,
6799-
fmt.Sprintf("remote %s: field %q: old=false, new=true", remote.name(), "Hub"),
6799+
fmt.Sprintf("remote %s: field %q: old=false, new=true", remote.safeName(), "Hub"),
68006800
},
68016801
{
68026802
"remote tlsfirst changed",
@@ -6967,7 +6967,7 @@ func TestConfigReloadLeafNodeUnsupportedChangesFail(t *testing.T) {
69676967

69686968
remote := o2.LeafNode.Remotes[0]
69696969
require_NotNil(t, remote)
6970-
remoteName := remote.name()
6970+
remoteName := remote.safeName()
69716971
require_NotEqual(t, remoteName, _EMPTY_)
69726972

69736973
checkConfigReload := func(content []byte, errTxt string) {

0 commit comments

Comments
 (0)