Skip to content

Commit

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

Signed-off-by: Matthias Hanel <mh@synadia.com>
  • Loading branch information
matthiashanel committed May 14, 2022
1 parent 2cb2a8e commit 66a29ab
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 12 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.20220509180118-3bcd719cc7d0
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.20220509180118-3bcd719cc7d0 h1:R2KQtvAFeZ7AbMYZ90qWE26yrg/PxhZYRsC93ROw6o4=
github.com/nats-io/jwt/v2 v2.2.1-0.20220509180118-3bcd719cc7d0/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
25 changes: 19 additions & 6 deletions server/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ type Account struct {

// Account based limits.
type limits struct {
mpay int32
msubs int32
mconns int32
mleafs int32
mpay int32
msubs int32
mconns int32
mleafs int32
disallowBearer bool
}

// Used to track remote clients and leafnodes per remote server.
Expand Down Expand Up @@ -229,7 +230,7 @@ type importMap struct {
func NewAccount(name string) *Account {
a := &Account{
Name: name,
limits: limits{-1, -1, -1, -1},
limits: limits{-1, -1, -1, -1, false},
eventIds: nuid.New(),
}
return a
Expand Down Expand Up @@ -2850,6 +2851,13 @@ func (a *Account) checkUserRevoked(nkey string, issuedAt int64) bool {
return isRevoked(a.usersRevoked, nkey, issuedAt)
}

// failBearer will return if bearer token are allowed (false) or disallowed (true)
func (a *Account) failBearer() bool {
a.mu.RLock()
defer a.mu.RUnlock()
return a.disallowBearer
}

// Check expiration and set the proper state as needed.
func (a *Account) checkExpiration(claims *jwt.ClaimsData) {
a.mu.Lock()
Expand Down Expand Up @@ -3254,6 +3262,7 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
a.mpay = int32(ac.Limits.Payload)
a.mconns = int32(ac.Limits.Conn)
a.mleafs = int32(ac.Limits.LeafNodeConn)
a.disallowBearer = ac.Limits.DisallowBearer
// Check for any revocations
if len(ac.Revocations) > 0 {
// We will always replace whatever we had with most current, so no
Expand Down Expand Up @@ -3356,11 +3365,15 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
theJWT := c.opts.JWT
c.mu.Unlock()
// Check for being revoked here. We use ac one to avoid the account lock.
if ac.Revocations != nil && theJWT != _EMPTY_ {
if (ac.Revocations != nil || ac.Limits.DisallowBearer) && theJWT != _EMPTY_ {
if juc, err := jwt.DecodeUserClaims(theJWT); err != nil {
c.Debugf("User JWT not valid: %v", err)
c.authViolation()
continue
} else if juc.BearerToken && ac.Limits.DisallowBearer {
c.Debugf("Bearer User JWT not allowed")
c.authViolation()
continue
} else if ok := ac.IsClaimRevoked(juc); ok {
c.sendErrAndDebug("User Authentication Revoked")
c.closeConnection(Revocation)
Expand Down
4 changes: 4 additions & 0 deletions server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,10 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
c.Debugf("Account JWT has expired")
return false
}
if juc.BearerToken && acc.failBearer() {
c.Debugf("Account does not allow bearer token")
return false
}
// skip validation of nonce when presented with a bearer token
// FIXME: if BearerToken is only for WSS, need check for server with that port enabled
if !juc.BearerToken {
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
71 changes: 71 additions & 0 deletions server/jetstream_jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,77 @@ func TestJetStreamJWTLimits(t *testing.T) {
c.Close()
}

func TestJetStreamJWTDisallowBearer(t *testing.T) {
sysKp, syspub := createKey(t)
sysJwt := encodeClaim(t, jwt.NewAccountClaims(syspub), syspub)
sysCreds := newUser(t, sysKp)
defer removeFile(t, sysCreds)

accKp, err := nkeys.CreateAccount()
require_NoError(t, err)
accIdPub, err := accKp.PublicKey()
require_NoError(t, err)
aClaim := jwt.NewAccountClaims(accIdPub)
accJwt1, err := aClaim.Encode(oKp)
require_NoError(t, err)
aClaim.Limits.DisallowBearer = true
accJwt2, err := aClaim.Encode(oKp)
require_NoError(t, err)

uc := jwt.NewUserClaims("dummy")
uc.BearerToken = true
uOpt1 := createUserCredsEx(t, uc, accKp)
uc.BearerToken = false
uOpt2 := createUserCredsEx(t, uc, accKp)

dir := createDir(t, "srv")
defer removeDir(t, dir)
cf := createConfFile(t, []byte(fmt.Sprintf(`
port: -1
operator = %s
system_account: %s
resolver: {
type: full
dir: '%s/jwt'
}
resolver_preload = {
%s : "%s"
}
`, ojwt, syspub, dir, syspub, sysJwt)))
defer removeFile(t, cf)
s, _ := RunServerWithConfig(cf)
defer s.Shutdown()

updateJwt(t, s.ClientURL(), sysCreds, accJwt1, 1)
disconnectErrCh := make(chan error, 10)
defer close(disconnectErrCh)
nc1, err := nats.Connect(s.ClientURL(), uOpt1,
nats.NoReconnect(),
nats.ErrorHandler(func(conn *nats.Conn, s *nats.Subscription, err error) {
disconnectErrCh <- err
}))
require_NoError(t, err)
defer nc1.Close()

// update jwt and observe bearer token get disconnected
updateJwt(t, s.ClientURL(), sysCreds, accJwt2, 1)
select {
case err := <-disconnectErrCh:
require_Contains(t, err.Error(), "authorization violation")
case <-time.After(time.Second):
t.Fatalf("expected error on disconnect")
}

// assure bearer token is not allowed to connect
_, err = nats.Connect(s.ClientURL(), uOpt1)
require_Error(t, err)

// assure non bearer token can connect
nc2, err := nats.Connect(s.ClientURL(), uOpt2)
require_NoError(t, err)
defer nc2.Close()
}

func TestJetStreamJWTMove(t *testing.T) {
sysKp, syspub := createKey(t)
sysJwt := encodeClaim(t, jwt.NewAccountClaims(syspub), syspub)
Expand Down

0 comments on commit 66a29ab

Please sign in to comment.