Skip to content

Commit

Permalink
[added] support for jwt operator option DisallowBearerToken
Browse files Browse the repository at this point in the history
change 3 out of 3 for #3084
corresponds to:
nats-io/jwt#177
nats-io/nsc#495

Signed-off-by: Matthias Hanel <mh@synadia.com>
  • Loading branch information
matthiashanel committed Apr 29, 2022
1 parent 0bb7abc commit 33d41ef
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 21 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
3 changes: 2 additions & 1 deletion server/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
5 changes: 4 additions & 1 deletion server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
9 changes: 6 additions & 3 deletions server/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Expand Down
45 changes: 45 additions & 0 deletions server/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 14 additions & 13 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
}
}
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 33d41ef

Please sign in to comment.