From 66dafcfc83d889b10fe27c8f4f6e972e7b2b48b6 Mon Sep 17 00:00:00 2001 From: Alberto Ricart Date: Fri, 13 Sep 2024 12:49:17 -0500 Subject: [PATCH] update account issuer always (#5886) changed to update the account issuer for an account to always update otherwise the information shown in accountz will not be consistent with the decoded JWT --- server/auth.go | 5 +- server/monitor_test.go | 109 +++++++++++++++++++++++++++++++++++++++++ server/server.go | 6 +-- 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/server/auth.go b/server/auth.go index f4e362f37ff..e3143224e1e 100644 --- a/server/auth.go +++ b/server/auth.go @@ -898,7 +898,10 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) (au c.Debugf("Account JWT lookup error: %v", err) return false } - if !s.isTrustedIssuer(acc.Issuer) { + acc.mu.RLock() + aissuer := acc.Issuer + acc.mu.RUnlock() + if !s.isTrustedIssuer(aissuer) { c.Debugf("Account JWT not signed by trusted operator") return false } diff --git a/server/monitor_test.go b/server/monitor_test.go index 699f9982282..8be9e7d8b4a 100644 --- a/server/monitor_test.go +++ b/server/monitor_test.go @@ -4189,6 +4189,115 @@ func TestMonitorAccountzOperatorMode(t *testing.T) { require_Contains(t, body, `"leafnodes": 0,`) } +func TestMonitorAccountzAccountIssuerUpdate(t *testing.T) { + // create an operator set of keys + okp, err := nkeys.CreateOperator() + require_NoError(t, err) + opk, err := okp.PublicKey() + require_NoError(t, err) + + // create the system account + _, sysPK := createKey(t) + sysAc := jwt.NewAccountClaims(sysPK) + sysAc.Name = "SYS" + sysJwt, err := sysAc.Encode(okp) + require_NoError(t, err) + + // create the operator with the system + oc := jwt.NewOperatorClaims(opk) + oc.Name = "O" + // add a signing keys + osk1, err := nkeys.CreateOperator() + require_NoError(t, err) + opk1, err := osk1.PublicKey() + require_NoError(t, err) + // add a second signing key + osk2, err := nkeys.CreateOperator() + require_NoError(t, err) + opk2, err := osk2.PublicKey() + require_NoError(t, err) + oc.SigningKeys.Add(opk1, opk2) + // set the system account + oc.SystemAccount = sysPK + // generate + oJWT, err := oc.Encode(okp) + require_NoError(t, err) + + // create an account + akp, apk := createKey(t) + ac := jwt.NewAccountClaims(apk) + ac.Name = "A" + // sign with the signing key + aJWT, err := ac.Encode(osk1) + require_NoError(t, err) + + // build the mem-resolver + conf := createConfFile(t, []byte(fmt.Sprintf(` + listen: 127.0.0.1:-1 + http: 127.0.0.1:-1 + operator = %s + resolver = MEMORY + system_account: %s + resolver_preload = { + %s : %s + %s : %s + } + `, oJWT, sysPK, sysPK, sysJwt, apk, aJWT))) + + // start the server + s, _ := RunServerWithConfig(conf) + defer s.Shutdown() + + // create an user for account A, or we don't see + // the account in accountsz + createUser := func() (string, string) { + ukp, _ := nkeys.CreateUser() + seed, _ := ukp.Seed() + upub, _ := ukp.PublicKey() + uclaim := newJWTTestUserClaims() + uclaim.Subject = upub + ujwt, err := uclaim.Encode(akp) + require_NoError(t, err) + return upub, genCredsFile(t, ujwt, seed) + } + + _, aCreds := createUser() + // connect the user + nc, err := nats.Connect(s.ClientURL(), nats.UserCredentials(aCreds)) + require_NoError(t, err) + defer nc.Close() + + // lookup the account + data := readBody(t, fmt.Sprintf("http://127.0.0.1:%d%s?acc=%s", s.MonitorAddr().Port, AccountzPath, apk)) + var ci Accountz + require_NoError(t, json.Unmarshal(data, &ci)) + require_Equal(t, ci.Account.IssuerKey, opk1) + + // now update the account + aJWT, err = ac.Encode(osk2) + require_NoError(t, err) + + updatedConf := []byte(fmt.Sprintf(` + listen: 127.0.0.1:-1 + http: 127.0.0.1:-1 + operator = %s + resolver = MEMORY + system_account: %s + resolver_preload = { + %s : %s + %s : %s + } + `, oJWT, sysPK, sysPK, sysJwt, apk, aJWT)) + // update the configuration file + require_NoError(t, os.WriteFile(conf, updatedConf, 0666)) + // reload + require_NoError(t, s.Reload()) + + data = readBody(t, fmt.Sprintf("http://127.0.0.1:%d%s?acc=%s", s.MonitorAddr().Port, AccountzPath, apk)) + require_NoError(t, json.Unmarshal(data, &ci)) + require_Equal(t, ci.Account.IssuerKey, opk2) +} + func TestMonitorAuthorizedUsers(t *testing.T) { kp, _ := nkeys.FromSeed(seed) usrNKey, _ := kp.PublicKey() diff --git a/server/server.go b/server/server.go index 26d048e6327..120f70db6ba 100644 --- a/server/server.go +++ b/server/server.go @@ -2049,9 +2049,9 @@ func (s *Server) updateAccountWithClaimJWT(acc *Account, claimJWT string) error accClaims, _, err := s.verifyAccountClaims(claimJWT) if err == nil && accClaims != nil { acc.mu.Lock() - if acc.Issuer == _EMPTY_ { - acc.Issuer = accClaims.Issuer - } + // if an account is updated with a different operator signing key, we want to + // show a consistent issuer. + acc.Issuer = accClaims.Issuer if acc.Name != accClaims.Subject { acc.mu.Unlock() return ErrAccountValidation