Skip to content

Commit

Permalink
Merge pull request #2004 from nats-io/jwt-issue
Browse files Browse the repository at this point in the history
[FIXED] private import issue by pulling in up to date jwt library
  • Loading branch information
kozlovic authored Mar 15, 2021
2 parents 4f8931e + eb1a91d commit 423b794
Show file tree
Hide file tree
Showing 178 changed files with 14,062 additions and 6,337 deletions.
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
module github.com/nats-io/nats-server/v2

go 1.15
go 1.16

require (
github.com/klauspost/compress v1.11.7
github.com/minio/highwayhash v1.0.0
github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813
github.com/klauspost/compress v1.11.12
github.com/minio/highwayhash v1.0.1
github.com/nats-io/jwt/v2 v2.0.1
github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac
github.com/nats-io/nkeys v0.2.0
github.com/nats-io/nkeys v0.3.0
github.com/nats-io/nuid v1.0.1
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
)
24 changes: 18 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,24 @@ 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.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.12 h1:famVnQVu7QwryBN4jNseQdUKES71ZAOnB6UQQJPZvqk=
github.com/klauspost/compress v1.11.12/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/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0=
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
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.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU=
github.com/nats-io/jwt v1.2.2/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-20210125223648-1c24d462becc/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813 h1:km4lLzT86NyJRhO++VqfP/vn5cbfm+E05i2bGdqDbrY=
github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
github.com/nats-io/jwt/v2 v2.0.1 h1:SycklijeduR742i/1Y3nRhURYM7imDzZZ3+tuAQqhQA=
github.com/nats-io/jwt/v2 v2.0.1/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY=
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=
Expand All @@ -32,33 +39,38 @@ github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5H
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/go.mod h1:8eAIv96Mo9QW6Or40jUHejS7e4VwZ3VRYD6Sf0BTDp4=
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.20210127212649-5b4924938a9a h1:EjwBk6T/arS7o0ZGdMgdzYrQHeUITT1GHf3cFQFtr3I=
github.com/nats-io/nats.go v1.10.1-0.20210127212649-5b4924938a9a/go.mod h1:Sa3kLIonafChP5IF0b55i9uvGR10I3hPETFbi4+9kOI=
github.com/nats-io/nats.go v1.10.1-0.20210211000709-75ded9c77585 h1:xbs6PNOyQcxNFXII9qcFvodqBtQKec8hP7WzGHOdsz0=
github.com/nats-io/nats.go v1.10.1-0.20210211000709-75ded9c77585/go.mod h1:uBWnCKg9luW1g7hgzPxUjHFRI40EuTSX7RCzgnc74Jk=
github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac h1:/cF7DEtxQBcwRDhpFZ3J0XU4TFpJa9KQF/xDirRNNI0=
github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac/go.mod h1:hxFvLNbNmT6UppX5B5Tr/r3g+XSwGjJzFn6mxPNJEHc=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
github.com/nats-io/nkeys v0.2.0 h1:WXKF7diOaPU9cJdLD7nuzwasQy9vT1tBqzXZZf3AMJM=
github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
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/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=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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/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/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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=
Expand Down
19 changes: 18 additions & 1 deletion server/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3577,6 +3577,15 @@ func getOperator(s *Server) (string, bool, error) {
return op, strict, nil
}

func claimValidate(claim *jwt.AccountClaims) error {
vr := &jwt.ValidationResults{}
claim.Validate(vr)
if vr.IsBlocking(false) {
return fmt.Errorf("validation errors: %v", vr.Errors())
}
return nil
}

func (dr *DirAccResolver) Start(s *Server) error {
op, strict, err := getOperator(s)
if err != nil {
Expand Down Expand Up @@ -3609,7 +3618,9 @@ func (dr *DirAccResolver) Start(s *Server) error {
return
}
if claim, err := jwt.DecodeAccountClaims(string(msg)); err != nil {
respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
respondToUpdate(s, resp, "n/a", "jwt update resulted in error", err)
} else if err := claimValidate(claim); err != nil {
respondToUpdate(s, resp, claim.Subject, "jwt validation failed", err)
} else if claim.Subject != pubKey {
err := errors.New("subject does not match jwt content")
respondToUpdate(s, resp, pubKey, "jwt update resulted in error", err)
Expand All @@ -3631,6 +3642,8 @@ func (dr *DirAccResolver) Start(s *Server) error {
} 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 := claimValidate(claim); err != nil {
respondToUpdate(s, resp, claim.Subject, "jwt validation failed", err)
} else if err := dr.save(claim.Subject, string(msg)); err != nil {
respondToUpdate(s, resp, claim.Subject, "jwt update resulted in error", err)
} else {
Expand Down Expand Up @@ -3865,6 +3878,8 @@ func (dr *CacheDirAccResolver) Start(s *Server) error {
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 := claimValidate(claim); err != nil {
respondToUpdate(s, resp, claim.Subject, "jwt update cache validation failed", err)
} else if err := dr.save(pubKey, string(msg)); err != nil {
respondToUpdate(s, resp, pubKey, "jwt update cache resulted in error", err)
} else {
Expand All @@ -3882,6 +3897,8 @@ func (dr *CacheDirAccResolver) Start(s *Server) error {
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 := claimValidate(claim); err != nil {
respondToUpdate(s, resp, claim.Subject, "jwt update cache validation failed", err)
} else if err := dr.save(claim.Subject, string(msg)); err != nil {
respondToUpdate(s, resp, claim.Subject, "jwt update cache resulted in error", err)
} else {
Expand Down
134 changes: 134 additions & 0 deletions server/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4829,6 +4829,140 @@ func TestJWTAccountImportsWithWildcardSupport(t *testing.T) {
})
}

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

aExpKp, aExpPub := createKey(t)
aExpClaim := jwt.NewAccountClaims(aExpPub)
aExpClaim.Name = "Export"
aExpClaim.Exports.Add(&jwt.Export{
Subject: "$events.*.$in.*.>",
Type: jwt.Stream,
TokenReq: true,
}, &jwt.Export{
Subject: "foo",
Type: jwt.Stream,
TokenReq: true,
})
aExpJwt := encodeClaim(t, aExpClaim, aExpPub)
aExpCreds := newUser(t, aExpKp)
defer os.Remove(aExpCreds)

createImportingAccountClaim := func(aImpKp nkeys.KeyPair, aExpPub string, ac *jwt.ActivationClaims) (string, string) {
t.Helper()
token, err := ac.Encode(aExpKp)
require_NoError(t, err)

aImpPub, err := aImpKp.PublicKey()
require_NoError(t, err)
aImpClaim := jwt.NewAccountClaims(aImpPub)
aImpClaim.Name = "Import"
aImpClaim.Imports.Add(&jwt.Import{
Subject: "$events.*.$in.*.>",
Type: jwt.Stream,
Account: aExpPub,
Token: token,
})
aImpJwt := encodeClaim(t, aImpClaim, aImpPub)
aImpCreds := newUser(t, aImpKp)
return aImpJwt, aImpCreds
}

testConnect := func(aExpPub, aExpJwt, aExpCreds, aImpPub, aImpJwt, aImpCreds string) {
t.Helper()
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/A/" {
// Server startup
w.Write(nil)
} else if r.URL.Path == "/A/"+aExpPub {
w.Write([]byte(aExpJwt))
} else if r.URL.Path == "/A/"+aImpPub {
w.Write([]byte(aImpJwt))
} else {
t.Fatal("not expected")
}
}))
defer ts.Close()
cf := createConfFile(t, []byte(fmt.Sprintf(`
listen: -1
operator: %s
resolver: URL("%s/A/")
`, ojwt, ts.URL)))
defer os.Remove(cf)

s, opts := RunServerWithConfig(cf)
defer s.Shutdown()

ncImp, err := nats.Connect(fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port), nats.UserCredentials(aImpCreds))
require_Error(t, err) // misuse needs to result in an error
defer ncImp.Close()
}

testNatsResolver := func(aImpJwt string) {
t.Helper()
dirSrv := createDir(t, "srv")
defer os.RemoveAll(dirSrv)
cf := createConfFile(t, []byte(fmt.Sprintf(`
listen: -1
operator: %s
system_account: %s
resolver: {
type: full
dir: %s
}
`, ojwt, syspub, dirSrv)))

s, _ := RunServerWithConfig(cf)
defer s.Shutdown()

require_True(t, updateJwt(t, s.ClientURL(), sysCreds, sysJwt, 1) == 1)
require_True(t, updateJwt(t, s.ClientURL(), sysCreds, aExpJwt, 1) == 1)
require_True(t, updateJwt(t, s.ClientURL(), sysCreds, aImpJwt, 1) == 0) // assure this did not succeed
}

t.Run("wrong-account", func(t *testing.T) {
aImpKp, aImpPub := createKey(t)
ac := &jwt.ActivationClaims{}
_, ac.Subject = createKey(t) // on purpose issue this token for another account
ac.ImportSubject = "$events.*.$in.*.>"
ac.ImportType = jwt.Stream

aImpJwt, aImpCreds := createImportingAccountClaim(aImpKp, aExpPub, ac)
defer os.Remove(aImpCreds)
testConnect(aExpPub, aExpJwt, aExpCreds, aImpPub, aImpJwt, aImpCreds)
testNatsResolver(aImpJwt)
})

t.Run("different-subject", func(t *testing.T) {
aImpKp, aImpPub := createKey(t)
ac := &jwt.ActivationClaims{}
ac.Subject = aImpPub
ac.ImportSubject = "foo" // on purpose use a subject from another export
ac.ImportType = jwt.Stream

aImpJwt, aImpCreds := createImportingAccountClaim(aImpKp, aExpPub, ac)
defer os.Remove(aImpCreds)
testConnect(aExpPub, aExpJwt, aExpCreds, aImpPub, aImpJwt, aImpCreds)
testNatsResolver(aImpJwt)
})

t.Run("non-existing-subject", func(t *testing.T) {
aImpKp, aImpPub := createKey(t)
ac := &jwt.ActivationClaims{}
ac.Subject = aImpPub
ac.ImportSubject = "does-not-exist-or-from-different-export" // on purpose use a non exported subject
ac.ImportType = jwt.Stream

aImpJwt, aImpCreds := createImportingAccountClaim(aImpKp, aExpPub, ac)
defer os.Remove(aImpCreds)
testConnect(aExpPub, aExpJwt, aExpCreds, aImpPub, aImpJwt, aImpCreds)
testNatsResolver(aImpJwt)
})
}

func TestJWTResponseThreshold(t *testing.T) {
respThresh := 20 * time.Millisecond
aExpKp, aExpPub := createKey(t)
Expand Down
Loading

0 comments on commit 423b794

Please sign in to comment.