mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
[added] support for StrictSigningKeyUsage and updated jwt library (#1845)
This will cause the server to not trust accounts/user signed by an identity key The boot strapping system account will assume the account is issued by the operator. If this is not desirable, the system account can be provided right away as resolver_preload. [fixes] crash when the system account uses signing keys and an update changes that key set. 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.15
|
||||
require (
|
||||
github.com/klauspost/compress v1.11.7
|
||||
github.com/minio/highwayhash v1.0.0
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6
|
||||
github.com/nats-io/nkeys v0.2.0
|
||||
github.com/nats-io/nuid v1.0.1
|
||||
|
||||
27
go.sum
27
go.sum
@@ -3,49 +3,30 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU=
|
||||
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA=
|
||||
github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7 h1:RnGotxlghqR5D2KDAu4TyuLqyjuylOsJiAFhXvMvQIc=
|
||||
github.com/nats-io/jwt v0.3.3-0.20200519195258-f2bf5ce574c7/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=
|
||||
github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM=
|
||||
github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=
|
||||
github.com/nats-io/jwt v1.2.3-0.20210107222814-18c5cc45d263 h1:x3J+0KMQhbQE8iHxChJdJHNk7rJnCwXFX+WI0hfvYtE=
|
||||
github.com/nats-io/jwt v1.2.3-0.20210107222814-18c5cc45d263/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q=
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20201015190852-e11ce317263c/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263 h1:M2bkT/arzYFYPtRqQWN2m+LSLfcqFmPxlxvTxdB4aVE=
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc h1:pu+s4XC+bYnI0iD2vDtOl83zjCYUau/q6c83pEvsGZc=
|
||||
github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
|
||||
github.com/nats-io/nats-server/v2 v2.1.8-0.20200524125952-51ebd92a9093/go.mod h1:rQnBf2Rv4P9adtAs/Ti6LfFmVtFG6HLhl/H7cVshcJU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.8-0.20200601203034-f8d6dd992b71/go.mod h1:Nan/1L5Sa1JRW+Thm4HNYcIDcVRFc5zK9OpSZeI2kk4=
|
||||
github.com/nats-io/nats-server/v2 v2.1.8-0.20200929001935-7f44d075f7ad/go.mod h1:TkHpUIDETmTI7mrHN40D1pzxfzHZuGmtMbtb83TGVQw=
|
||||
github.com/nats-io/nats-server/v2 v2.1.8-0.20201129161730-ebe63db3e3ed/go.mod h1:XD0zHR/jTXdZvWaQfS5mQgsXj6x12kMjKLyAk/cOGgY=
|
||||
github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20200531124210-96f2130e4d55/go.mod h1:ARiFsjW9DVxk48WJbO3OSZ2DG8fjkMi7ecLmXoY/n9I=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a h1:gzSKZOBlu/DpbuPbt34paXCOvA6+E+lVfU2BmomQ9HA=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20200606002146-fc6fed82929a/go.mod h1:8eAIv96Mo9QW6Or40jUHejS7e4VwZ3VRYD6Sf0BTDp4=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20201021145452-94be476ad6e0 h1:s//xxvMgNRVJNTRXTRNvLlES0zvJoDLXfcmbK0YgN0E=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20201021145452-94be476ad6e0/go.mod h1:VU2zERjp8xmF+Lw2NH4u2t5qWZxwc7jB3+7HVMWQXPI=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20201218032324-b4450fb04511 h1:VMkjinILzhvMPbG+MwRC9q5IElHdsOaB5aHIiCT8vd4=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20201218032324-b4450fb04511/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210113000055-be60b8378d4f h1:HjROEVuamG+e0lCwjKs3yfTzDbbaogriW4HQQ2OQcr0=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210113000055-be60b8378d4f/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210113025029-acca6d69b4e3 h1:M7a1IwTIUnZ/ucEz+/oyOoQ11CJ/sDfybZYjdGMGjdE=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210113025029-acca6d69b4e3/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210114001154-0a6b5f686ab3 h1:2uYi4zZJ6zni7jwix8l1bA/srT3hjFwGN2iUvx0CWpA=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210114001154-0a6b5f686ab3/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210115180731-7fb8bacca613 h1:Zio4IMHHsFjtTeksjF4PySxFNcKwSH9urNIiIW7A/FQ=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210115180731-7fb8bacca613/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6 h1:cpS+9uyfHXvRG/Q+WcDd3KXRgPa9fo9tDbIeDHCxYAg=
|
||||
github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
@@ -56,7 +37,6 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -64,19 +44,16 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
|
||||
@@ -2746,10 +2746,6 @@ func (a *Account) hasIssuer(issuer string) (jwt.Scope, bool) {
|
||||
|
||||
// hasIssuerNoLock is the unlocked version of hasIssuer
|
||||
func (a *Account) hasIssuerNoLock(issuer string) (jwt.Scope, bool) {
|
||||
// same issuer -- keep this for safety on the calling code
|
||||
if a.Name == issuer {
|
||||
return nil, true
|
||||
}
|
||||
scope, ok := a.signingKeys[issuer]
|
||||
return scope, ok
|
||||
}
|
||||
@@ -2844,13 +2840,17 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
|
||||
|
||||
// update account signing keys
|
||||
a.signingKeys = nil
|
||||
if len(ac.SigningKeys) > 0 {
|
||||
a.signingKeys = make(map[string]jwt.Scope, len(ac.SigningKeys))
|
||||
_, strict := s.strictSigningKeyUsage[a.Issuer]
|
||||
if len(ac.SigningKeys) > 0 || !strict {
|
||||
a.signingKeys = make(map[string]jwt.Scope)
|
||||
}
|
||||
signersChanged := false
|
||||
for k, scope := range ac.SigningKeys {
|
||||
a.signingKeys[k] = scope
|
||||
}
|
||||
if !strict {
|
||||
a.signingKeys[a.Name] = nil
|
||||
}
|
||||
if len(a.signingKeys) != len(old.signingKeys) {
|
||||
signersChanged = true
|
||||
}
|
||||
@@ -3150,6 +3150,10 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
|
||||
if signersChanged {
|
||||
for _, c := range clients {
|
||||
c.mu.Lock()
|
||||
if c.user == nil {
|
||||
c.mu.Unlock()
|
||||
continue
|
||||
}
|
||||
sk := c.user.SigningKey
|
||||
c.mu.Unlock()
|
||||
if sk == _EMPTY_ {
|
||||
@@ -3506,19 +3510,21 @@ func handleDeleteRequest(store *DirJWTStore, s *Server, msg []byte, reply string
|
||||
}
|
||||
}
|
||||
|
||||
func getOperator(s *Server) (string, error) {
|
||||
func getOperator(s *Server) (string, bool, error) {
|
||||
var op string
|
||||
var strict bool
|
||||
if opts := s.getOpts(); opts != nil && len(opts.TrustedOperators) > 0 {
|
||||
op = opts.TrustedOperators[0].Subject
|
||||
strict = opts.TrustedOperators[0].StrictSigningKeyUsage
|
||||
}
|
||||
if op == "" {
|
||||
return "", fmt.Errorf("no operator found")
|
||||
return "", false, fmt.Errorf("no operator found")
|
||||
}
|
||||
return op, nil
|
||||
return op, strict, nil
|
||||
}
|
||||
|
||||
func (dr *DirAccResolver) Start(s *Server) error {
|
||||
op, err := getOperator(s)
|
||||
op, strict, err := getOperator(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -3553,6 +3559,9 @@ func (dr *DirAccResolver) Start(s *Server) error {
|
||||
} else if claim.Subject != pubKey {
|
||||
err := errors.New("subject does not match jwt content")
|
||||
respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
|
||||
} else if claim.Issuer == op && strict {
|
||||
err := errors.New("operator requires issuer to be a signing key")
|
||||
respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
|
||||
} else if err := dr.save(pubKey, string(msg)); err != nil {
|
||||
respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
|
||||
} else {
|
||||
@@ -3565,6 +3574,9 @@ func (dr *DirAccResolver) Start(s *Server) error {
|
||||
if _, err := s.sysSubscribe(accClaimsReqSubj, func(_ *subscription, _ *client, subj, resp string, msg []byte) {
|
||||
if claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {
|
||||
respondToUpdate(s, resp, "n/a", "jwt update resulted in error", err)
|
||||
} else if claim.Issuer == op && strict {
|
||||
err := errors.New("operator requires issuer to be a signing key")
|
||||
respondToUpdate(s, resp, claim.Subject, "jwt update resulted in error", err)
|
||||
} else if err := dr.save(claim.Subject, string(msg)); err != nil {
|
||||
respondToUpdate(s, resp, claim.Subject, "jwt update resulted in error", err)
|
||||
} else {
|
||||
@@ -3760,7 +3772,7 @@ func NewCacheDirAccResolver(path string, limit int64, ttl time.Duration, _ ...di
|
||||
}
|
||||
|
||||
func (dr *CacheDirAccResolver) Start(s *Server) error {
|
||||
op, err := getOperator(s)
|
||||
op, strict, err := getOperator(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -3794,6 +3806,9 @@ func (dr *CacheDirAccResolver) Start(s *Server) error {
|
||||
} else if claim.Subject != pubKey {
|
||||
err := errors.New("subject does not match jwt content")
|
||||
respondToUpdate(s, resp, pubKey, "jwt update cache resulted in error", err)
|
||||
} else if claim.Issuer == op && strict {
|
||||
err := errors.New("operator requires issuer to be a signing key")
|
||||
respondToUpdate(s, resp, pubKey, "jwt update cache resulted in error", err)
|
||||
} else if _, ok := s.accounts.Load(pubKey); !ok {
|
||||
respondToUpdate(s, resp, pubKey, "jwt update cache skipped", nil)
|
||||
} else if err := dr.save(pubKey, string(msg)); err != nil {
|
||||
@@ -3808,6 +3823,9 @@ func (dr *CacheDirAccResolver) Start(s *Server) error {
|
||||
if _, err := s.sysSubscribe(accClaimsReqSubj, func(_ *subscription, _ *client, subj, resp string, msg []byte) {
|
||||
if claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {
|
||||
respondToUpdate(s, resp, "n/a", "jwt update cache resulted in error", err)
|
||||
} else if claim.Issuer == op && strict {
|
||||
err := errors.New("operator requires issuer to be a signing key")
|
||||
respondToUpdate(s, resp, claim.Subject, "jwt update cache resulted in error", err)
|
||||
} else if _, ok := s.accounts.Load(claim.Subject); !ok {
|
||||
respondToUpdate(s, resp, claim.Subject, "jwt update cache skipped", nil)
|
||||
} else if err := dr.save(claim.Subject, string(msg)); err != nil {
|
||||
|
||||
@@ -238,7 +238,7 @@ func (s *Server) configureAuthorization() {
|
||||
// This just checks and sets up the user map if we have multiple users.
|
||||
if opts.CustomClientAuthentication != nil {
|
||||
s.info.AuthRequired = true
|
||||
} else if len(s.trustedKeys) > 0 {
|
||||
} else if s.trustedKeys != nil {
|
||||
s.info.AuthRequired = true
|
||||
} else if opts.Nkeys != nil || opts.Users != nil {
|
||||
s.nkeys, s.users = s.buildNkeysAndUsersFromOptions(opts.Nkeys, opts.Users)
|
||||
@@ -560,21 +560,18 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) boo
|
||||
c.Debugf("Account JWT not signed by trusted operator")
|
||||
return false
|
||||
}
|
||||
// this only executes IF there's an issuer on the Juc - otherwise the account is already vetted
|
||||
if juc.IssuerAccount != _EMPTY_ {
|
||||
if scope, ok := acc.hasIssuer(juc.Issuer); !ok {
|
||||
c.Debugf("User JWT issuer is not known")
|
||||
if scope, ok := acc.hasIssuer(juc.Issuer); !ok {
|
||||
c.Debugf("User JWT issuer is not known")
|
||||
return false
|
||||
} else if scope != nil {
|
||||
if err := scope.ValidateScopedSigner(juc); err != nil {
|
||||
c.Debugf("User JWT is not valid: %v", err)
|
||||
return false
|
||||
} else if scope != nil {
|
||||
if err := scope.ValidateScopedSigner(juc); err != nil {
|
||||
c.Debugf("User JWT is not valid: %v", err)
|
||||
return false
|
||||
} else if uSc, ok := scope.(*jwt.UserScope); !ok {
|
||||
c.Debugf("User JWT is not valid")
|
||||
return false
|
||||
} else {
|
||||
juc.UserPermissionLimits = uSc.Template
|
||||
}
|
||||
} else if uSc, ok := scope.(*jwt.UserScope); !ok {
|
||||
c.Debugf("User JWT is not valid")
|
||||
return false
|
||||
} else {
|
||||
juc.UserPermissionLimits = uSc.Template
|
||||
}
|
||||
}
|
||||
if acc.IsExpired() {
|
||||
|
||||
@@ -1787,7 +1787,7 @@ func (c *client) authViolation() {
|
||||
var hasTrustedNkeys, hasNkeys, hasUsers bool
|
||||
if s = c.srv; s != nil {
|
||||
s.mu.Lock()
|
||||
hasTrustedNkeys = len(s.trustedKeys) > 0
|
||||
hasTrustedNkeys = s.trustedKeys != nil
|
||||
hasNkeys = s.nkeys != nil
|
||||
hasUsers = s.users != nil
|
||||
s.mu.Unlock()
|
||||
|
||||
@@ -132,7 +132,9 @@ func validateTrustedOperators(o *Options) error {
|
||||
if o.TrustedKeys == nil {
|
||||
o.TrustedKeys = make([]string, 0, 4)
|
||||
}
|
||||
o.TrustedKeys = append(o.TrustedKeys, opc.Issuer)
|
||||
if !opc.StrictSigningKeyUsage {
|
||||
o.TrustedKeys = append(o.TrustedKeys, opc.Subject)
|
||||
}
|
||||
o.TrustedKeys = append(o.TrustedKeys, opc.SigningKeys...)
|
||||
}
|
||||
for _, key := range o.TrustedKeys {
|
||||
|
||||
@@ -5144,3 +5144,121 @@ func TestJWScopedSigningKeys(t *testing.T) {
|
||||
})
|
||||
require_Len(t, len(errChan), 0)
|
||||
}
|
||||
|
||||
func TestJWTStrictSigningKeys(t *testing.T) {
|
||||
newAccount := func(opKp nkeys.KeyPair) (nkeys.KeyPair, nkeys.KeyPair, string, *jwt.AccountClaims, string) {
|
||||
accId, err := nkeys.CreateAccount()
|
||||
require_NoError(t, err)
|
||||
accIdPub, err := accId.PublicKey()
|
||||
require_NoError(t, err)
|
||||
|
||||
accSig, err := nkeys.CreateAccount()
|
||||
require_NoError(t, err)
|
||||
accSigPub, err := accSig.PublicKey()
|
||||
require_NoError(t, err)
|
||||
|
||||
aClaim := jwt.NewAccountClaims(accIdPub)
|
||||
aClaim.SigningKeys.Add(accSigPub)
|
||||
theJwt, err := aClaim.Encode(opKp)
|
||||
require_NoError(t, err)
|
||||
return accId, accSig, accIdPub, aClaim, theJwt
|
||||
}
|
||||
|
||||
opId, err := nkeys.CreateOperator()
|
||||
require_NoError(t, err)
|
||||
opIdPub, err := opId.PublicKey()
|
||||
require_NoError(t, err)
|
||||
|
||||
opSig, err := nkeys.CreateOperator()
|
||||
require_NoError(t, err)
|
||||
opSigPub, err := opSig.PublicKey()
|
||||
require_NoError(t, err)
|
||||
|
||||
aBadBadKp, aBadGoodKp, aBadPub, _, aBadJwt := newAccount(opId)
|
||||
aGoodBadKp, aGoodGoodKp, aGoodPub, _, aGoodJwt := newAccount(opSig)
|
||||
_, aSysKp, aSysPub, _, aSysJwt := newAccount(opSig)
|
||||
|
||||
oClaim := jwt.NewOperatorClaims(opIdPub)
|
||||
oClaim.StrictSigningKeyUsage = true
|
||||
oClaim.SigningKeys.Add(opSigPub)
|
||||
oClaim.SystemAccount = aSysPub
|
||||
oJwt, err := oClaim.Encode(opId)
|
||||
require_NoError(t, err)
|
||||
|
||||
uBadBadCreds := newUserEx(t, aBadBadKp, false, aBadPub)
|
||||
defer os.Remove(uBadBadCreds)
|
||||
uBadGoodCreds := newUserEx(t, aBadGoodKp, false, aBadPub)
|
||||
defer os.Remove(uBadGoodCreds)
|
||||
uGoodBadCreds := newUserEx(t, aGoodBadKp, false, aGoodPub)
|
||||
defer os.Remove(uGoodBadCreds)
|
||||
uGoodGoodCreds := newUserEx(t, aGoodGoodKp, false, aGoodPub)
|
||||
defer os.Remove(uGoodGoodCreds)
|
||||
uSysCreds := newUserEx(t, aSysKp, false, aSysPub)
|
||||
defer os.Remove(uSysCreds)
|
||||
|
||||
connectTest := func(url string) {
|
||||
for _, test := range []struct {
|
||||
creds string
|
||||
fail bool
|
||||
}{
|
||||
{uBadBadCreds, true},
|
||||
{uBadGoodCreds, true},
|
||||
{uGoodBadCreds, true},
|
||||
{uGoodGoodCreds, false},
|
||||
} {
|
||||
nc, err := nats.Connect(url, nats.UserCredentials(test.creds))
|
||||
nc.Close()
|
||||
if test.fail {
|
||||
require_Error(t, err)
|
||||
} else {
|
||||
require_NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("resolver", func(t *testing.T) {
|
||||
dirSrv := createDir(t, "srv")
|
||||
defer os.RemoveAll(dirSrv)
|
||||
cf := createConfFile(t, []byte(fmt.Sprintf(`
|
||||
port: -1
|
||||
operator = %s
|
||||
resolver: {
|
||||
type: full
|
||||
dir: %s
|
||||
}
|
||||
resolver_preload = {
|
||||
%s : "%s"
|
||||
}
|
||||
`, oJwt, dirSrv, aSysPub, aSysJwt)))
|
||||
defer os.Remove(cf)
|
||||
s, _ := RunServerWithConfig(cf)
|
||||
defer s.Shutdown()
|
||||
url := s.ClientURL()
|
||||
if updateJwt(t, url, uSysCreds, aBadJwt, 1) != 0 {
|
||||
t.Fatal("Expected negative response")
|
||||
}
|
||||
if updateJwt(t, url, uSysCreds, aGoodJwt, 1) != 1 {
|
||||
t.Fatal("Expected positive response")
|
||||
}
|
||||
connectTest(url)
|
||||
})
|
||||
|
||||
t.Run("mem-resolver", func(t *testing.T) {
|
||||
dirSrv := createDir(t, "srv")
|
||||
defer os.RemoveAll(dirSrv)
|
||||
cf := createConfFile(t, []byte(fmt.Sprintf(`
|
||||
port: -1
|
||||
operator = %s
|
||||
resolver: MEMORY
|
||||
resolver_preload = {
|
||||
%s : "%s"
|
||||
%s : "%s"
|
||||
%s : "%s"
|
||||
}
|
||||
`, oJwt, aSysPub, aSysJwt, aBadPub, aBadJwt, aGoodPub, aGoodJwt)))
|
||||
defer os.Remove(cf)
|
||||
s, _ := RunServerWithConfig(cf)
|
||||
defer s.Shutdown()
|
||||
connectTest(s.ClientURL())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (s *Server) NonceRequired() bool {
|
||||
// nonceRequired tells us if we should send a nonce.
|
||||
// Lock should be held on entry.
|
||||
func (s *Server) nonceRequired() bool {
|
||||
return len(s.nkeys) > 0 || len(s.trustedKeys) > 0
|
||||
return len(s.nkeys) > 0 || s.trustedKeys != nil
|
||||
}
|
||||
|
||||
// Generate a nonce for INFO challenge.
|
||||
|
||||
@@ -194,6 +194,8 @@ type Server struct {
|
||||
|
||||
// Trusted public operator keys.
|
||||
trustedKeys []string
|
||||
// map of trusted keys to operator setting StrictSigningKeyUsage
|
||||
strictSigningKeyUsage map[string]struct{}
|
||||
|
||||
// We use this to minimize mem copies for requests to monitoring
|
||||
// endpoint /varz (when it comes from http).
|
||||
@@ -419,6 +421,8 @@ func NewServer(opts *Options) (*Server, error) {
|
||||
if a == nil {
|
||||
sac := NewAccount(s.opts.SystemAccount)
|
||||
sac.Issuer = opts.TrustedOperators[0].Issuer
|
||||
sac.signingKeys = map[string]jwt.Scope{}
|
||||
sac.signingKeys[s.opts.SystemAccount] = nil
|
||||
s.registerAccountNoLock(sac)
|
||||
}
|
||||
}
|
||||
@@ -748,7 +752,7 @@ func (s *Server) generateRouteInfoJSON() {
|
||||
func (s *Server) globalAccountOnly() bool {
|
||||
var hasOthers bool
|
||||
|
||||
if len(s.trustedKeys) > 0 {
|
||||
if s.trustedKeys != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -798,7 +802,7 @@ func (s *Server) isTrustedIssuer(issuer string) 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 len(s.trustedKeys) == 0 && issuer == "" {
|
||||
if s.trustedKeys == nil && issuer == "" {
|
||||
return true
|
||||
}
|
||||
for _, tk := range s.trustedKeys {
|
||||
@@ -812,6 +816,7 @@ func (s *Server) isTrustedIssuer(issuer string) bool {
|
||||
// processTrustedKeys will process binary stamped and
|
||||
// options-based trusted nkeys. Returns success.
|
||||
func (s *Server) processTrustedKeys() bool {
|
||||
s.strictSigningKeyUsage = map[string]struct{}{}
|
||||
if trustedKeys != "" && !s.initStampedTrustedKeys() {
|
||||
return false
|
||||
} else if s.opts.TrustedKeys != nil {
|
||||
@@ -820,7 +825,15 @@ func (s *Server) processTrustedKeys() bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
s.trustedKeys = s.opts.TrustedKeys
|
||||
s.trustedKeys = append([]string(nil), s.opts.TrustedKeys...)
|
||||
for _, claim := range s.opts.TrustedOperators {
|
||||
if !claim.StrictSigningKeyUsage {
|
||||
continue
|
||||
}
|
||||
for _, key := range claim.SigningKeys {
|
||||
s.strictSigningKeyUsage[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
2
vendor/github.com/nats-io/jwt/v2/account_claims.go
generated
vendored
2
vendor/github.com/nats-io/jwt/v2/account_claims.go
generated
vendored
@@ -148,7 +148,7 @@ func NewAccountClaims(subject string) *AccountClaims {
|
||||
c.Limits = OperatorLimits{
|
||||
NatsLimits{NoLimit, NoLimit, NoLimit},
|
||||
AccountLimits{NoLimit, NoLimit, true, NoLimit, NoLimit},
|
||||
JetStreamLimits{NoLimit, NoLimit, NoLimit, NoLimit}}
|
||||
JetStreamLimits{0, 0, 0, 0}}
|
||||
c.Subject = subject
|
||||
return c
|
||||
}
|
||||
|
||||
4
vendor/github.com/nats-io/jwt/v2/operator_claims.go
generated
vendored
4
vendor/github.com/nats-io/jwt/v2/operator_claims.go
generated
vendored
@@ -43,6 +43,8 @@ type Operator struct {
|
||||
SystemAccount string `json:"system_account,omitempty"`
|
||||
// Min Server version
|
||||
AssertServerVersion string `json:"assert_server_version,omitempty"`
|
||||
// Signing of subordinate objects will require signing keys
|
||||
StrictSigningKeyUsage bool `json:"strict_signing_key_usage,omitempty"`
|
||||
GenericFields
|
||||
}
|
||||
|
||||
@@ -174,7 +176,7 @@ func (oc *OperatorClaims) DidSign(op Claims) bool {
|
||||
}
|
||||
issuer := op.Claims().Issuer
|
||||
if issuer == oc.Subject {
|
||||
return true
|
||||
return !oc.StrictSigningKeyUsage
|
||||
}
|
||||
return oc.SigningKeys.Contains(issuer)
|
||||
}
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -4,7 +4,7 @@ github.com/klauspost/compress/s2
|
||||
# github.com/minio/highwayhash v1.0.0
|
||||
## explicit
|
||||
github.com/minio/highwayhash
|
||||
# github.com/nats-io/jwt/v2 v2.0.0-20210107222814-18c5cc45d263
|
||||
# github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc
|
||||
## explicit
|
||||
github.com/nats-io/jwt/v2
|
||||
# github.com/nats-io/nats.go v1.10.1-0.20210122204956-b8ea7fc17ea6
|
||||
|
||||
Reference in New Issue
Block a user