mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
[added] support for jwt account option DisallowBearer (#3127)
* [added] support for jwt account option DisallowBearer change 3 out of 3. Fixes #3084 corresponds to: https://github.com/nats-io/jwt/pull/177 https://github.com/nats-io/nsc/pull/495 update jwt library to 2.3.0 Signed-off-by: Matthias Hanel <mh@synadia.com>
This commit is contained in:
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.17
|
||||
require (
|
||||
github.com/klauspost/compress v1.15.5
|
||||
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.3.0
|
||||
github.com/nats-io/nats.go v1.16.0
|
||||
github.com/nats-io/nkeys v0.3.0
|
||||
github.com/nats-io/nuid v1.0.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -18,8 +18,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
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.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
|
||||
github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
|
||||
github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g=
|
||||
github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
|
||||
|
||||
@@ -96,10 +96,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.
|
||||
@@ -230,7 +231,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
|
||||
@@ -2838,6 +2839,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()
|
||||
@@ -3242,6 +3250,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
|
||||
@@ -3344,11 +3353,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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user