mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
[added] support for jwt operator option DisallowBearerToken
I modified an existing data structure that held a similar attribute already. Instead this data structure references the claim. 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 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.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
|
||||
|
||||
4
go.sum
4
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=
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
require_Error(t, err)
|
||||
defer nc1.Close()
|
||||
|
||||
nc2, err := nats.Connect(s.ClientURL(), uOpt2)
|
||||
require_NoError(t, err)
|
||||
defer nc2.Close()
|
||||
}
|
||||
|
||||
func TestJWTAccountProtectedImport(t *testing.T) {
|
||||
srvFmt := `
|
||||
port: -1
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user