diff --git a/go.mod b/go.mod index 07408231b9f..753893f600d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/klauspost/compress v1.14.4 github.com/minio/highwayhash v1.0.2 - github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a + github.com/nats-io/jwt/v2 v2.2.1-0.20220429175140-9f4f1d5cc977 github.com/nats-io/nats.go v1.14.0 github.com/nats-io/nkeys v0.3.0 github.com/nats-io/nuid v1.0.1 diff --git a/go.sum b/go.sum index 81e284461be..fe34d7f2c36 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6 github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I= -github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/jwt/v2 v2.2.1-0.20220429175140-9f4f1d5cc977 h1:cbP9xTzFVGWXCJtApKchvFZIIEDWKfLkThVYLonpOpQ= +github.com/nats-io/jwt/v2 v2.2.1-0.20220429175140-9f4f1d5cc977/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= github.com/nats-io/nats.go v1.14.0 h1:/QLCss4vQ6wvDpbqXucsVRDi13tFIR6kTdau+nXzKJw= github.com/nats-io/nats.go v1.14.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= diff --git a/server/accounts.go b/server/accounts.go index d133395550c..cde23482f07 100644 --- a/server/accounts.go +++ b/server/accounts.go @@ -2983,7 +2983,8 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim // update account signing keys a.signingKeys = nil - _, strict := s.strictSigningKeyUsage[a.Issuer] + op, ok := s.keyToClaim[a.Issuer] + strict := ok && op.StrictSigningKeyUsage if len(ac.SigningKeys) > 0 || !strict { a.signingKeys = make(map[string]jwt.Scope) } diff --git a/server/auth.go b/server/auth.go index e7b4d56a44d..f3a68350bac 100644 --- a/server/auth.go +++ b/server/auth.go @@ -607,9 +607,12 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo c.Debugf("Account JWT lookup error: %v", err) return false } - if !s.isTrustedIssuer(acc.Issuer) { + if isTrusted, noBearer := s.isTrustedIssuer(acc.Issuer); !isTrusted { c.Debugf("Account JWT not signed by trusted operator") return false + } else if noBearer && juc.BearerToken { + c.Debugf("Bearer Token are disallowed by operator") + return false } if scope, ok := acc.hasIssuer(juc.Issuer); !ok { c.Debugf("User JWT issuer is not known") diff --git a/server/events_test.go b/server/events_test.go index e2f12b51acb..ced4c9d3c9b 100644 --- a/server/events_test.go +++ b/server/events_test.go @@ -45,11 +45,10 @@ func createAccount(s *Server) (*Account, nkeys.KeyPair) { return acc, akp } -func createUserCreds(t *testing.T, s *Server, akp nkeys.KeyPair) nats.Option { +func createUserCredsEx(t *testing.T, nuc *jwt.UserClaims, akp nkeys.KeyPair) nats.Option { t.Helper() kp, _ := nkeys.CreateUser() - pub, _ := kp.PublicKey() - nuc := jwt.NewUserClaims(pub) + nuc.Subject, _ = kp.PublicKey() ujwt, err := nuc.Encode(akp) if err != nil { t.Fatalf("Error generating user JWT: %v", err) @@ -64,6 +63,10 @@ func createUserCreds(t *testing.T, s *Server, akp nkeys.KeyPair) nats.Option { return nats.UserJWT(userCB, sigCB) } +func createUserCreds(t *testing.T, s *Server, akp nkeys.KeyPair) nats.Option { + return createUserCredsEx(t, jwt.NewUserClaims("test"), akp) +} + func runTrustedServer(t *testing.T) (*Server, *Options) { t.Helper() opts := DefaultOptions() diff --git a/server/jwt_test.go b/server/jwt_test.go index a148ecf1cc1..7378077b6c1 100644 --- a/server/jwt_test.go +++ b/server/jwt_test.go @@ -5760,6 +5760,51 @@ func TestJWTStrictSigningKeys(t *testing.T) { }) } +func TestJWTDisallowBearer(t *testing.T) { + opId, err := nkeys.CreateOperator() + require_NoError(t, err) + opIdPub, err := opId.PublicKey() + require_NoError(t, err) + oClaim := jwt.NewOperatorClaims(opIdPub) + oClaim.DisallowBearerToken = true + oJwt, err := oClaim.Encode(opId) + require_NoError(t, err) + + accId, err := nkeys.CreateAccount() + require_NoError(t, err) + accIdPub, err := accId.PublicKey() + require_NoError(t, err) + aClaim := jwt.NewAccountClaims(accIdPub) + accJwt, err := aClaim.Encode(opId) + require_NoError(t, err) + + uc := jwt.NewUserClaims("dummy") + uc.BearerToken = true + uOpt1 := createUserCredsEx(t, uc, accId) + uc.BearerToken = false + uOpt2 := createUserCredsEx(t, uc, accId) + + cf := createConfFile(t, []byte(fmt.Sprintf(` + port: -1 + operator = %s + resolver: MEMORY + resolver_preload = { + %s : "%s" + } + `, oJwt, accIdPub, accJwt))) + defer removeFile(t, cf) + s, _ := RunServerWithConfig(cf) + defer s.Shutdown() + + nc1, err := nats.Connect(s.ClientURL(), uOpt1) + defer nc1.Close() + require_Error(t, err) + + nc2, err := nats.Connect(s.ClientURL(), uOpt2) + defer nc2.Close() + require_NoError(t, err) +} + func TestJWTAccountProtectedImport(t *testing.T) { srvFmt := ` port: -1 diff --git a/server/server.go b/server/server.go index 1e97a16df50..aed8229c334 100644 --- a/server/server.go +++ b/server/server.go @@ -209,8 +209,8 @@ type Server struct { // Trusted public operator keys. trustedKeys []string - // map of trusted keys to operator setting StrictSigningKeyUsage - strictSigningKeyUsage map[string]struct{} + // map of trusted keys to operator claims + keyToClaim map[string]*jwt.OperatorClaims // We use this to minimize mem copies for requests to monitoring // endpoint /varz (when it comes from http). @@ -972,25 +972,28 @@ func (s *Server) ActivePeers() (peers []string) { // isTrustedIssuer will check that the issuer is a trusted public key. // This is used to make sure an account was signed by a trusted operator. -func (s *Server) isTrustedIssuer(issuer string) bool { +func (s *Server) isTrustedIssuer(issuer string) (bool, bool) { s.mu.Lock() defer s.mu.Unlock() // If we are not running in trusted mode and there is no issuer, that is ok. if s.trustedKeys == nil && issuer == "" { - return true + return true, false } for _, tk := range s.trustedKeys { if tk == issuer { - return true + if op, ok := s.keyToClaim[issuer]; ok { + return true, op.DisallowBearerToken + } + return true, false } } - return false + return false, false } // processTrustedKeys will process binary stamped and // options-based trusted nkeys. Returns success. func (s *Server) processTrustedKeys() bool { - s.strictSigningKeyUsage = map[string]struct{}{} + s.keyToClaim = map[string]*jwt.OperatorClaims{} if trustedKeys != "" && !s.initStampedTrustedKeys() { return false } else if s.opts.TrustedKeys != nil { @@ -1001,11 +1004,9 @@ func (s *Server) processTrustedKeys() bool { } s.trustedKeys = append([]string(nil), s.opts.TrustedKeys...) for _, claim := range s.opts.TrustedOperators { - if !claim.StrictSigningKeyUsage { - continue - } + s.keyToClaim[claim.Subject] = claim for _, key := range claim.SigningKeys { - s.strictSigningKeyUsage[key] = struct{}{} + s.keyToClaim[key] = claim } } } @@ -1222,7 +1223,7 @@ func (s *Server) setSystemAccount(acc *Account) error { } // If we are running with trusted keys for an operator // make sure we check the account is legit. - if !s.isTrustedIssuer(acc.Issuer) { + if isTrusted, _ := s.isTrustedIssuer(acc.Issuer); !isTrusted { return ErrAccountValidation } @@ -1542,7 +1543,7 @@ func (s *Server) verifyAccountClaims(claimJWT string) (*jwt.AccountClaims, strin if err != nil { return nil, _EMPTY_, err } - if !s.isTrustedIssuer(accClaims.Issuer) { + if isTrusted, _ := s.isTrustedIssuer(accClaims.Issuer); !isTrusted { return nil, _EMPTY_, ErrAccountValidation } vr := jwt.CreateValidationResults()