diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 47761bf5..00000000 --- a/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:1.12.9 - -MAINTAINER Ivan Kozlovic - -COPY . /go/src/github.com/nats-io/nats-server -WORKDIR /go/src/github.com/nats-io/nats-server - -RUN CGO_ENABLED=0 GO111MODULE=off go install -v -a -tags netgo -installsuffix netgo -ldflags "-s -w -X github.com/nats-io/nats-server/server.gitCommit=`git rev-parse --short HEAD`" - -EXPOSE 4222 8222 -ENTRYPOINT ["nats-server"] -CMD ["--help"] diff --git a/Dockerfile.all b/Dockerfile.all deleted file mode 100644 index 8357d8d2..00000000 --- a/Dockerfile.all +++ /dev/null @@ -1,15 +0,0 @@ -FROM golang:1.12.9 - -MAINTAINER Ivan Kozlovic - -COPY . /go/src/github.com/nats-io/nats-server -WORKDIR /go/src/github.com/nats-io/nats-server - -RUN CGO_ENABLED=0 GO111MODULE=off GOOS=linux GOARCH=amd64 go build -v -a -tags netgo -installsuffix netgo -ldflags "-s -w -X github.com/nats-io/nats-server/server.gitCommit=`git rev-parse --short HEAD`" -o pkg/linux-amd64/nats-server -RUN CGO_ENABLED=0 GO111MODULE=off GOOS=linux GOARCH=arm GOARM=6 go build -v -a -tags netgo -installsuffix netgo -ldflags "-s -w -X github.com/nats-io/nats-server/server.gitCommit=`git rev-parse --short HEAD`" -o pkg/linux-arm6/nats-server -RUN CGO_ENABLED=0 GO111MODULE=off GOOS=linux GOARCH=arm GOARM=7 go build -v -a -tags netgo -installsuffix netgo -ldflags "-s -w -X github.com/nats-io/nats-server/server.gitCommit=`git rev-parse --short HEAD`" -o pkg/linux-arm7/nats-server -RUN CGO_ENABLED=0 GO111MODULE=off GOOS=linux GOARCH=arm64 go build -v -a -tags netgo -installsuffix netgo -ldflags "-s -w -X github.com/nats-io/nats-server/server.gitCommit=`git rev-parse --short HEAD`" -o pkg/linux-arm64/nats-server -RUN CGO_ENABLED=0 GO111MODULE=off GOOS=windows GOARCH=amd64 go build -v -a -tags netgo -installsuffix netgo -ldflags "-s -w -X github.com/nats-io/nats-server/server.gitCommit=`git rev-parse --short HEAD`" -o pkg/win-amd64/nats-server.exe - -ENTRYPOINT ["go"] -CMD ["version"] diff --git a/README.md b/README.md index b89e0d15..b76a5463 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ If you are interested in contributing to NATS, read about our... [Fossa-Image]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fgnatsd.svg?type=shield [Build-Status-Url]: https://travis-ci.org/nats-io/nats-server [Build-Status-Image]: https://travis-ci.org/nats-io/nats-server.svg?branch=master -[Release-Url]: https://github.com/nats-io/nats-server/releases/tag/v2.1.0 -[Release-image]: https://img.shields.io/badge/release-v2.1.0-1eb0fc.svg +[Release-Url]: https://github.com/nats-io/nats-server/releases/tag/v2.1.2 +[Release-image]: https://img.shields.io/badge/release-v2.1.2-1eb0fc.svg [Coverage-Url]: https://coveralls.io/r/nats-io/nats-server?branch=master [Coverage-image]: https://coveralls.io/repos/github/nats-io/nats-server/badge.svg?branch=master [ReportCard-Url]: https://goreportcard.com/report/nats-io/nats-server diff --git a/go.mod b/go.mod index 25330218..5b8a4cc3 100644 --- a/go.mod +++ b/go.mod @@ -2,9 +2,9 @@ module github.com/nats-io/nats-server/v2 require ( github.com/golang/protobuf v1.3.2 // indirect - github.com/nats-io/jwt v0.3.0 - github.com/nats-io/nats.go v1.8.1 - github.com/nats-io/nkeys v0.1.0 + github.com/nats-io/jwt v0.3.2 + github.com/nats-io/nats.go v1.9.1 + github.com/nats-io/nkeys v0.1.3 github.com/nats-io/nuid v1.0.1 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e diff --git a/go.sum b/go.sum index 15cb39be..4a5794dc 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,15 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= -github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= +github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 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-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/server/const.go b/server/const.go index f58fb56e..50cbd31a 100644 --- a/server/const.go +++ b/server/const.go @@ -40,7 +40,7 @@ var ( const ( // VERSION is the current version for the server. - VERSION = "2.1.1-RC6" + VERSION = "2.1.2" // PROTO is the currently supported protocol. // 0 was the original diff --git a/vendor/github.com/nats-io/jwt/.travis.yml b/vendor/github.com/nats-io/jwt/.travis.yml index f7dc9242..50e27a6b 100644 --- a/vendor/github.com/nats-io/jwt/.travis.yml +++ b/vendor/github.com/nats-io/jwt/.travis.yml @@ -1,8 +1,8 @@ language: go sudo: false go: +- 1.13.x - 1.12.x -- 1.11.x install: - go get -t ./... @@ -18,4 +18,5 @@ before_script: - staticcheck ./... script: -- if [[ "$TRAVIS_GO_VERSION" == 1.10.* ]] ; then ./scripts/cov.sh TRAVIS; else go test -v -race ./...; fi +- go test -v -race ./... +- if [[ "$TRAVIS_GO_VERSION" =~ 1.12 ]]; then ./scripts/cov.sh TRAVIS; fi diff --git a/vendor/github.com/nats-io/jwt/account_claims.go b/vendor/github.com/nats-io/jwt/account_claims.go index ec7132d0..945bd987 100644 --- a/vendor/github.com/nats-io/jwt/account_claims.go +++ b/vendor/github.com/nats-io/jwt/account_claims.go @@ -17,6 +17,7 @@ package jwt import ( "errors" + "sort" "time" "github.com/nats-io/nkeys" @@ -127,7 +128,8 @@ func (a *AccountClaims) Encode(pair nkeys.KeyPair) (string, error) { if !nkeys.IsValidPublicAccountKey(a.Subject) { return "", errors.New("expected subject to be account public key") } - + sort.Sort(a.Exports) + sort.Sort(a.Imports) a.ClaimsData.Type = AccountClaim return a.ClaimsData.Encode(pair, a) } diff --git a/vendor/github.com/nats-io/jwt/creds_utils.go b/vendor/github.com/nats-io/jwt/creds_utils.go index debc2bc7..bb913dc1 100644 --- a/vendor/github.com/nats-io/jwt/creds_utils.go +++ b/vendor/github.com/nats-io/jwt/creds_utils.go @@ -23,6 +23,7 @@ func formatJwt(kind string, jwtString string) ([]byte, error) { templ := `-----BEGIN NATS %s JWT----- %s ------END NATS %s JWT------ + ` w := bytes.NewBuffer(nil) kind = strings.ToUpper(kind) @@ -60,6 +61,7 @@ func DecorateSeed(seed []byte) ([]byte, error) { header := `************************* IMPORTANT ************************* NKEY Seed printed below can be used to sign and prove identity. NKEYs are sensitive and should be treated as secrets. + -----BEGIN %s NKEY SEED----- ` _, err := fmt.Fprintf(w, header, kind) @@ -70,6 +72,7 @@ NKEYs are sensitive and should be treated as secrets. footer := ` ------END %s NKEY SEED------ + ************************************************************* ` _, err = fmt.Fprintf(w, footer, kind) diff --git a/vendor/github.com/nats-io/jwt/exports.go b/vendor/github.com/nats-io/jwt/exports.go index c0993365..5578f988 100644 --- a/vendor/github.com/nats-io/jwt/exports.go +++ b/vendor/github.com/nats-io/jwt/exports.go @@ -158,7 +158,7 @@ func (e *Export) IsRevoked(pubKey string) bool { return e.Revocations.IsRevoked(pubKey, time.Now()) } -// Exports is an array of exports +// Exports is a slice of exports type Exports []*Export // Add appends exports to the list @@ -222,3 +222,15 @@ func (e *Exports) HasExportContainingSubject(subject Subject) bool { } return false } + +func (e Exports) Len() int { + return len(e) +} + +func (e Exports) Swap(i, j int) { + e[i], e[j] = e[j], e[i] +} + +func (e Exports) Less(i, j int) bool { + return e[i].Subject < e[j].Subject +} diff --git a/vendor/github.com/nats-io/jwt/go.mod b/vendor/github.com/nats-io/jwt/go.mod index b1ad180a..a780dde9 100644 --- a/vendor/github.com/nats-io/jwt/go.mod +++ b/vendor/github.com/nats-io/jwt/go.mod @@ -1,3 +1,3 @@ module github.com/nats-io/jwt -require github.com/nats-io/nkeys v0.1.0 +require github.com/nats-io/nkeys v0.1.3 diff --git a/vendor/github.com/nats-io/jwt/go.sum b/vendor/github.com/nats-io/jwt/go.sum index ea493eca..9baf67f5 100644 --- a/vendor/github.com/nats-io/jwt/go.sum +++ b/vendor/github.com/nats-io/jwt/go.sum @@ -1,5 +1,5 @@ -github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/vendor/github.com/nats-io/jwt/header.go b/vendor/github.com/nats-io/jwt/header.go index 468f30f7..27c65811 100644 --- a/vendor/github.com/nats-io/jwt/header.go +++ b/vendor/github.com/nats-io/jwt/header.go @@ -23,7 +23,7 @@ import ( const ( // Version is semantic version. - Version = "0.3.0" + Version = "0.3.2" // TokenTypeJwt is the JWT token type supported JWT tokens // encoded and decoded by this library diff --git a/vendor/github.com/nats-io/jwt/imports.go b/vendor/github.com/nats-io/jwt/imports.go index 8a0f0c98..8cd97479 100644 --- a/vendor/github.com/nats-io/jwt/imports.go +++ b/vendor/github.com/nats-io/jwt/imports.go @@ -63,10 +63,11 @@ func (i *Import) Validate(actPubKey string, vr *ValidationResults) { i.Subject.Validate(vr) - if i.IsService() { - if i.Subject.HasWildCards() { - vr.AddWarning("services cannot have wildcard subject: %q", i.Subject) - } + if i.IsService() && i.Subject.HasWildCards() { + vr.AddError("services cannot have wildcard subject: %q", i.Subject) + } + if i.IsStream() && i.To.HasWildCards() { + vr.AddError("streams cannot have wildcard to subject: %q", i.Subject) } var act *ActivationClaims @@ -136,3 +137,15 @@ func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) { func (i *Imports) Add(a ...*Import) { *i = append(*i, a...) } + +func (i Imports) Len() int { + return len(i) +} + +func (i Imports) Swap(j, k int) { + i[j], i[k] = i[k], i[j] +} + +func (i Imports) Less(j, k int) bool { + return i[j].Subject < i[k].Subject +} diff --git a/vendor/github.com/nats-io/nats.go/.travis.yml b/vendor/github.com/nats-io/nats.go/.travis.yml index 9e73bb2d..2594b74e 100644 --- a/vendor/github.com/nats-io/nats.go/.travis.yml +++ b/vendor/github.com/nats-io/nats.go/.travis.yml @@ -16,8 +16,8 @@ install: before_script: - $(exit $(go fmt ./... | wc -l)) - go vet ./... -- misspell -error -locale US . +- find . -type f -name "*.go" | xargs misspell -error -locale US - staticcheck ./... script: - go test -i -race ./... -- if [[ "$TRAVIS_GO_VERSION" =~ 1.12 ]]; then ./scripts/cov.sh TRAVIS; else go test -race ./...; fi +- if [[ "$TRAVIS_GO_VERSION" =~ 1.12 ]]; then ./scripts/cov.sh TRAVIS; else go test -race -v -p=1 ./... --failfast; fi diff --git a/vendor/github.com/nats-io/nats.go/README.md b/vendor/github.com/nats-io/nats.go/README.md index f9b1b644..83cbbd27 100644 --- a/vendor/github.com/nats-io/nats.go/README.md +++ b/vendor/github.com/nats-io/nats.go/README.md @@ -15,6 +15,20 @@ go get github.com/nats-io/nats.go/ go get github.com/nats-io/nats-server ``` +When using or transitioning to Go modules support: + +```bash +# Go client latest or explicit version +go get github.com/nats-io/nats.go/@latest +go get github.com/nats-io/nats.go/@v1.9.1 + +# For latest NATS Server, add /v2 at the end +go get github.com/nats-io/nats-server/v2 + +# NATS Server v1 is installed otherwise +# go get github.com/nats-io/nats-server +``` + ## Basic Usage ```go @@ -33,7 +47,7 @@ nc.Subscribe("foo", func(m *nats.Msg) { // Responding to a request message nc.Subscribe("request", func(m *nats.Msg) { - m.Respond([]byte("answer is 42") + m.Respond([]byte("answer is 42")) }) // Simple Sync Subscriber @@ -55,7 +69,7 @@ sub.Drain() msg, err := nc.Request("help", []byte("help me"), 10*time.Millisecond) // Replies -nc.Subscribe("help", func(m *Msg) { +nc.Subscribe("help", func(m *nats.Msg) { nc.Publish(m.Reply, []byte("I can help!")) }) @@ -102,12 +116,12 @@ c.Publish("hello", me) // Unsubscribe sub, err := c.Subscribe("foo", nil) -... +// ... sub.Unsubscribe() // Requests var response string -err := c.Request("help", "help me", &response, 10*time.Millisecond) +err = c.Request("help", "help me", &response, 10*time.Millisecond) if err != nil { fmt.Printf("Request failed: %v\n", err) } @@ -127,7 +141,7 @@ This requires server with version >= 2.0.0 NATS servers have a new security and authentication mechanism to authenticate with user credentials and Nkeys. The simplest form is to use the helper method UserCredentials(credsFilepath). ```go -nc, err := nats.Connect(url, UserCredentials("user.creds")) +nc, err := nats.Connect(url, nats.UserCredentials("user.creds")) ``` The helper methods creates two callback handlers to present the user JWT and sign the nonce challenge from the server. @@ -136,12 +150,12 @@ The helper will load and wipe and erase memory it uses for each connect or recon The helper also can take two entries, one for the JWT and one for the NKey seed file. ```go -nc, err := nats.Connect(url, UserCredentials("user.jwt", "user.nk")) +nc, err := nats.Connect(url, nats.UserCredentials("user.jwt", "user.nk")) ``` You can also set the callback handlers directly and manage challenge signing directly. ```go -nc, err := nats.Connect(url, UserJWT(jwtCB, sigCB)) +nc, err := nats.Connect(url, nats.UserJWT(jwtCB, sigCB)) ``` Bare Nkeys are also supported. The nkey seed should be in a read only file, e.g. seed.txt @@ -160,7 +174,7 @@ opt, err := nats.NkeyOptionFromSeed("seed.txt") nc, err := nats.Connect(serverUrl, opt) // Direct -nc, err := nats.Connect(serverUrl, Nkey(pubNkey, sigCB)) +nc, err := nats.Connect(serverUrl, nats.Nkey(pubNkey, sigCB)) ``` ## TLS diff --git a/vendor/github.com/nats-io/nats.go/context.go b/vendor/github.com/nats-io/nats.go/context.go index 14aa355e..c921d6be 100644 --- a/vendor/github.com/nats-io/nats.go/context.go +++ b/vendor/github.com/nats-io/nats.go/context.go @@ -43,29 +43,7 @@ func (nc *Conn) RequestWithContext(ctx context.Context, subj string, data []byte return nc.oldRequestWithContext(ctx, subj, data) } - // Do setup for the new style. - if nc.respMap == nil { - nc.initNewResp() - } - // Create literal Inbox and map to a chan msg. - mch := make(chan *Msg, RequestChanLen) - respInbox := nc.newRespInbox() - token := respToken(respInbox) - nc.respMap[token] = mch - createSub := nc.respMux == nil - ginbox := nc.respSub - nc.mu.Unlock() - - if createSub { - // Make sure scoped subscription is setup only once. - var err error - nc.respSetup.Do(func() { err = nc.createRespMux(ginbox) }) - if err != nil { - return nil, err - } - } - - err := nc.PublishRequest(subj, respInbox, data) + mch, token, err := nc.createNewRequestAndSend(subj, data) if err != nil { return nil, err } @@ -140,7 +118,7 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) { select { case msg, ok = <-mch: if !ok { - return nil, ErrConnectionClosed + return nil, s.getNextMsgErr() } if err := s.processNextMsgDelivered(msg); err != nil { return nil, err @@ -153,7 +131,7 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) { select { case msg, ok = <-mch: if !ok { - return nil, ErrConnectionClosed + return nil, s.getNextMsgErr() } if err := s.processNextMsgDelivered(msg); err != nil { return nil, err diff --git a/vendor/github.com/nats-io/nats.go/enc.go b/vendor/github.com/nats-io/nats.go/enc.go index 6d5c2790..0ed71a2c 100644 --- a/vendor/github.com/nats-io/nats.go/enc.go +++ b/vendor/github.com/nats-io/nats.go/enc.go @@ -33,7 +33,7 @@ type Encoder interface { var encMap map[string]Encoder var encLock sync.Mutex -// Indexe names into the Registered Encoders. +// Indexed names into the Registered Encoders. const ( JSON_ENCODER = "json" GOB_ENCODER = "gob" @@ -109,7 +109,7 @@ func (c *EncodedConn) PublishRequest(subject, reply string, v interface{}) error // Request will create an Inbox and perform a Request() call // with the Inbox reply for the data v. A response will be -// decoded into the vPtrResponse. +// decoded into the vPtr Response. func (c *EncodedConn) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error { b, err := c.Enc.Encode(subject, v) if err != nil { diff --git a/vendor/github.com/nats-io/nats.go/go.mod b/vendor/github.com/nats-io/nats.go/go.mod index 7cfd5639..f82ceee6 100644 --- a/vendor/github.com/nats-io/nats.go/go.mod +++ b/vendor/github.com/nats-io/nats.go/go.mod @@ -1,6 +1,7 @@ module github.com/nats-io/nats.go require ( - github.com/nats-io/nkeys v0.0.2 + github.com/nats-io/jwt v0.3.0 + github.com/nats-io/nkeys v0.1.0 github.com/nats-io/nuid v1.0.1 ) diff --git a/vendor/github.com/nats-io/nats.go/go.sum b/vendor/github.com/nats-io/nats.go/go.sum index 7f95527f..0cd4f648 100644 --- a/vendor/github.com/nats-io/nats.go/go.sum +++ b/vendor/github.com/nats-io/nats.go/go.sum @@ -1,6 +1,13 @@ -github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 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-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/nats-io/nats.go/nats.go b/vendor/github.com/nats-io/nats.go/nats.go index 8785d6da..911df590 100644 --- a/vendor/github.com/nats-io/nats.go/nats.go +++ b/vendor/github.com/nats-io/nats.go/nats.go @@ -28,7 +28,8 @@ import ( "math/rand" "net" "net/url" - "regexp" + "os" + "path/filepath" "runtime" "strconv" "strings" @@ -36,6 +37,7 @@ import ( "sync/atomic" "time" + "github.com/nats-io/jwt" "github.com/nats-io/nats.go/util" "github.com/nats-io/nkeys" "github.com/nats-io/nuid" @@ -43,7 +45,7 @@ import ( // Default Constants const ( - Version = "1.8.1" + Version = "1.9.1" DefaultURL = "nats://127.0.0.1:4222" DefaultPort = 4222 DefaultMaxReconnect = 60 @@ -67,6 +69,9 @@ const ( // AUTHORIZATION_ERR is for when nats server user authorization has failed. AUTHORIZATION_ERR = "authorization violation" + + // AUTHENTICATION_EXPIRED_ERR is for when nats server user authorization has expired. + AUTHENTICATION_EXPIRED_ERR = "user authentication expired" ) // Errors @@ -80,10 +85,12 @@ var ( ErrBadSubscription = errors.New("nats: invalid subscription") ErrTypeSubscription = errors.New("nats: invalid subscription type") ErrBadSubject = errors.New("nats: invalid subject") + ErrBadQueueName = errors.New("nats: invalid queue name") ErrSlowConsumer = errors.New("nats: slow consumer, messages dropped") ErrTimeout = errors.New("nats: timeout") ErrBadTimeout = errors.New("nats: timeout invalid") ErrAuthorization = errors.New("nats: authorization violation") + ErrAuthExpired = errors.New("nats: authentication expired") ErrNoServers = errors.New("nats: no servers available for connection") ErrJsonParse = errors.New("nats: connect message, json parse error") ErrChanArg = errors.New("nats: argument needs to be a channel type") @@ -166,7 +173,8 @@ type UserJWTHandler func() (string, error) // SignatureHandler is used to sign a nonce from the server while // authenticating with nkeys. The user should sign the nonce and -// return the base64 encoded signature. +// return the raw signature. The client will base64 encode this to +// send to the server. type SignatureHandler func([]byte) ([]byte, error) // AuthTokenHandler is used to generate a new token. @@ -339,6 +347,11 @@ type Options struct { // UseOldRequestStyle forces the old method of Requests that utilize // a new Inbox and a new Subscription for each request. UseOldRequestStyle bool + + // NoCallbacksAfterClientClose allows preventing the invocation of + // callbacks after Close() is called. Client won't receive notifications + // when Close is invoked by user code. Default is to invoke the callbacks. + NoCallbacksAfterClientClose bool } const ( @@ -363,6 +376,7 @@ const ( // A Conn represents a bare connection to a nats-server. // It can send and receive []byte payloads. +// The connection is safe to use in multiple Go routines concurrently. type Conn struct { // Keep all members for which we use atomic at the beginning of the // struct and make sure they are all 64bits (or use padding if necessary). @@ -394,13 +408,15 @@ type Conn struct { ps *parseState ptmr *time.Timer pout int + ar bool // abort reconnect // New style response handler respSub string // The wildcard subject + respScanf string // The scanf template to extract mux token respMux *Subscription // A single response subscription respMap map[string]chan *Msg // Request map for the response msg channels respSetup sync.Once // Ensures response subscription occurs once - respRand *rand.Rand // Used for generating suffix. + respRand *rand.Rand // Used for generating suffix } // A Subscription represents interest in a given subject. @@ -475,6 +491,7 @@ type srv struct { didConnect bool reconnects int lastAttempt time.Time + lastErr error isImplicit bool tlsName string } @@ -872,6 +889,16 @@ func UseOldRequestStyle() Option { } } +// NoCallbacksAfterClientClose is an Option to disable callbacks when user code +// calls Close(). If close is initiated by any other condition, callbacks +// if any will be invoked. +func NoCallbacksAfterClientClose() Option { + return func(o *Options) error { + o.NoCallbacksAfterClientClose = true + return nil + } +} + // Handler processing // SetDisconnectHandler will set the disconnect event handler. @@ -1445,6 +1472,7 @@ func (nc *Conn) connect() error { if err == nil { nc.srvPool[i].didConnect = true nc.srvPool[i].reconnects = 0 + nc.current.lastErr = nil returnedErr = nil break } else { @@ -1614,7 +1642,6 @@ func normalizeErr(line string) string { // applicable. Will wait for a flush to return from the server for error // processing. func (nc *Conn) sendConnect() error { - // Construct the CONNECT protocol string cProto, err := nc.connectProto() if err != nil { @@ -1668,6 +1695,17 @@ func (nc *Conn) sendConnect() error { if strings.HasPrefix(proto, _ERR_OP_) { // Remove -ERR, trim spaces and quotes, and convert to lower case. proto = normalizeErr(proto) + + // Check if this is an auth error + if authErr := checkAuthError(strings.ToLower(proto)); authErr != nil { + // This will schedule an async error if we are in reconnect, + // and keep track of the auth error for the current server. + // If we have got the same error twice, this sets nc.ar to true to + // indicate that the reconnect should be aborted (will be checked + // in doReconnect()). + nc.processAuthError(authErr) + } + return errors.New("nats: " + proto) } @@ -1841,6 +1879,11 @@ func (nc *Conn) doReconnect(err error) { // Process connect logic if nc.err = nc.processConnectInit(); nc.err != nil { + // Check if we should abort reconnect. If so, break out + // of the loop and connection will be closed. + if nc.ar { + break + } nc.status = RECONNECTING // Reset the buffered writer to the pending buffer // (was set to a buffered writer on nc.conn in createConn) @@ -1848,6 +1891,10 @@ func (nc *Conn) doReconnect(err error) { continue } + // Clear possible lastErr under the connection lock after + // a successful processConnectInit(). + nc.current.lastErr = nil + // Clear out server stats for the server we connected to.. cur.didConnect = true cur.reconnects = 0 @@ -1883,6 +1930,7 @@ func (nc *Conn) doReconnect(err error) { if nc.Opts.ReconnectedCB != nil { nc.ach.push(func() { nc.Opts.ReconnectedCB(nc) }) } + // Release lock here, we will return below. nc.mu.Unlock() @@ -1897,7 +1945,7 @@ func (nc *Conn) doReconnect(err error) { nc.err = ErrNoServers } nc.mu.Unlock() - nc.Close() + nc.close(CLOSED, true, nil) } // processOpErr handles errors from reading or parsing the protocol. @@ -1932,7 +1980,7 @@ func (nc *Conn) processOpErr(err error) { nc.status = DISCONNECTED nc.err = err nc.mu.Unlock() - nc.Close() + nc.close(CLOSED, true, nil) } // dispatch is responsible for calling any async callbacks @@ -2132,8 +2180,8 @@ func (nc *Conn) processMsg(data []byte) { nc.subsMu.RLock() // Stats - nc.InMsgs++ - nc.InBytes += uint64(len(data)) + atomic.AddUint64(&nc.InMsgs, 1) + atomic.AddUint64(&nc.InBytes, uint64(len(data))) sub := nc.subs[nc.ps.ma.sid] if sub == nil { @@ -2239,15 +2287,24 @@ func (nc *Conn) processPermissionsViolation(err string) { nc.mu.Unlock() } -// processAuthorizationViolation is called when the server signals a user -// authorization violation. -func (nc *Conn) processAuthorizationViolation(err string) { - nc.mu.Lock() - nc.err = ErrAuthorization - if nc.Opts.AsyncErrorCB != nil { - nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, ErrAuthorization) }) +// processAuthError generally processing for auth errors. We want to do retries +// unless we get the same error again. This allows us for instance to swap credentials +// and have the app reconnect, but if nothing is changing we should bail. +// This function will return true if the connection should be closed, false otherwise. +// Connection lock is held on entry +func (nc *Conn) processAuthError(err error) bool { + nc.err = err + if !nc.initc && nc.Opts.AsyncErrorCB != nil { + nc.ach.push(func() { nc.Opts.AsyncErrorCB(nc, nil, err) }) } - nc.mu.Unlock() + // We should give up if we tried twice on this server and got the + // same error. + if nc.current.lastErr == err { + nc.ar = true + } else { + nc.current.lastErr = err + } + return nc.ar } // flusher is a separate Go routine that will process flush requests for the write @@ -2417,6 +2474,18 @@ func (nc *Conn) LastError() error { return err } +// Check if the given error string is an auth error, and if so returns +// the corresponding ErrXXX error, nil otherwise +func checkAuthError(e string) error { + if strings.HasPrefix(e, AUTHORIZATION_ERR) { + return ErrAuthorization + } + if strings.HasPrefix(e, AUTHENTICATION_EXPIRED_ERR) { + return ErrAuthExpired + } + return nil +} + // processErr processes any error messages from the server and // sets the connection's lastError. func (nc *Conn) processErr(ie string) { @@ -2425,18 +2494,25 @@ func (nc *Conn) processErr(ie string) { // convert to lower case. e := strings.ToLower(ne) + close := false + // FIXME(dlc) - process Slow Consumer signals special. if e == STALE_CONNECTION { nc.processOpErr(ErrStaleConnection) } else if strings.HasPrefix(e, PERMISSIONS_ERR) { nc.processPermissionsViolation(ne) - } else if strings.HasPrefix(e, AUTHORIZATION_ERR) { - nc.processAuthorizationViolation(ne) + } else if authErr := checkAuthError(e); authErr != nil { + nc.mu.Lock() + close = nc.processAuthError(authErr) + nc.mu.Unlock() } else { + close = true nc.mu.Lock() nc.err = errors.New("nats: " + ne) nc.mu.Unlock() - nc.Close() + } + if close { + nc.close(CLOSED, true, nil) } } @@ -2572,21 +2648,32 @@ func (nc *Conn) publish(subj, reply string, data []byte) error { // the appropriate channel based on the last token and place // the message on the channel if possible. func (nc *Conn) respHandler(m *Msg) { - rt := respToken(m.Subject) - nc.mu.Lock() + // Just return if closed. if nc.isClosed() { nc.mu.Unlock() return } + var mch chan *Msg + // Grab mch - mch := nc.respMap[rt] - // Delete the key regardless, one response only. - // FIXME(dlc) - should we track responses past 1 - // just statistics wise? - delete(nc.respMap, rt) + rt := nc.respToken(m.Subject) + if rt != _EMPTY_ { + mch = nc.respMap[rt] + // Delete the key regardless, one response only. + delete(nc.respMap, rt) + } else if len(nc.respMap) == 1 { + // If the server has rewritten the subject, the response token (rt) + // will not match (could be the case with JetStream). If that is the + // case and there is a single entry, use that. + for k, v := range nc.respMap { + mch = v + delete(nc.respMap, k) + break + } + } nc.mu.Unlock() // Don't block, let Request timeout instead, mch is @@ -2610,11 +2697,43 @@ func (nc *Conn) createRespMux(respSub string) error { return err } nc.mu.Lock() + nc.respScanf = strings.Replace(respSub, "*", "%s", -1) nc.respMux = s nc.mu.Unlock() return nil } +// Helper to setup and send new request style requests. Return the chan to receive the response. +func (nc *Conn) createNewRequestAndSend(subj string, data []byte) (chan *Msg, string, error) { + // Do setup for the new style if needed. + if nc.respMap == nil { + nc.initNewResp() + } + // Create new literal Inbox and map to a chan msg. + mch := make(chan *Msg, RequestChanLen) + respInbox := nc.newRespInbox() + token := respInbox[respInboxPrefixLen:] + nc.respMap[token] = mch + createSub := nc.respMux == nil + ginbox := nc.respSub + nc.mu.Unlock() + + if createSub { + // Make sure scoped subscription is setup only once. + var err error + nc.respSetup.Do(func() { err = nc.createRespMux(ginbox) }) + if err != nil { + return nil, token, err + } + } + + if err := nc.PublishRequest(subj, respInbox, data); err != nil { + return nil, token, err + } + + return mch, token, nil +} + // Request will send a request payload and deliver the response message, // or an error, including a timeout if no message was received properly. func (nc *Conn) Request(subj string, data []byte, timeout time.Duration) (*Msg, error) { @@ -2629,29 +2748,8 @@ func (nc *Conn) Request(subj string, data []byte, timeout time.Duration) (*Msg, return nc.oldRequest(subj, data, timeout) } - // Do setup for the new style. - if nc.respMap == nil { - nc.initNewResp() - } - // Create literal Inbox and map to a chan msg. - mch := make(chan *Msg, RequestChanLen) - respInbox := nc.newRespInbox() - token := respToken(respInbox) - nc.respMap[token] = mch - createSub := nc.respMux == nil - ginbox := nc.respSub - nc.mu.Unlock() - - if createSub { - // Make sure scoped subscription is setup only once. - var err error - nc.respSetup.Do(func() { err = nc.createRespMux(ginbox) }) - if err != nil { - return nil, err - } - } - - if err := nc.PublishRequest(subj, respInbox, data); err != nil { + mch, token, err := nc.createNewRequestAndSend(subj, data) + if err != nil { return nil, err } @@ -2754,9 +2852,16 @@ func (nc *Conn) NewRespInbox() string { } // respToken will return the last token of a literal response inbox -// which we use for the message channel lookup. -func respToken(respInbox string) string { - return respInbox[respInboxPrefixLen:] +// which we use for the message channel lookup. This needs to do a +// scan to protect itself against the server changing the subject. +// Lock should be held. +func (nc *Conn) respToken(respInbox string) string { + var token string + n, err := fmt.Sscanf(respInbox, nc.respScanf, &token) + if err != nil || n != 1 { + return "" + } + return token } // Subscribe will express interest in the given subject. The subject @@ -2822,11 +2927,37 @@ func (nc *Conn) QueueSubscribeSyncWithChan(subj, queue string, ch chan *Msg) (*S return nc.subscribe(subj, queue, nil, ch, false) } +// badSubject will do quick test on whether a subject is acceptable. +// Spaces are not allowed and all tokens should be > 0 in len. +func badSubject(subj string) bool { + if strings.ContainsAny(subj, " \t\r\n") { + return true + } + tokens := strings.Split(subj, ".") + for _, t := range tokens { + if len(t) == 0 { + return true + } + } + return false +} + +// badQueue will check a queue name for whitespace. +func badQueue(qname string) bool { + return strings.ContainsAny(qname, " \t\r\n") +} + // subscribe is the internal subscribe function that indicates interest in a subject. func (nc *Conn) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg, isSync bool) (*Subscription, error) { if nc == nil { return nil, ErrInvalidConnection } + if badSubject(subj) { + return nil, ErrBadSubject + } + if queue != "" && badQueue(queue) { + return nil, ErrBadQueueName + } nc.mu.Lock() // ok here, but defer is generally expensive defer nc.mu.Unlock() @@ -2902,7 +3033,6 @@ func (nc *Conn) removeSub(s *Subscription) { s.mch = nil // Mark as invalid - s.conn = nil s.closed = true if s.pCond != nil { s.pCond.Broadcast() @@ -2939,7 +3069,7 @@ func (s *Subscription) IsValid() bool { } s.mu.Lock() defer s.mu.Unlock() - return s.conn != nil + return s.conn != nil && !s.closed } // Drain will remove interest but continue callbacks until all messages @@ -2964,8 +3094,12 @@ func (s *Subscription) Unsubscribe() error { } s.mu.Lock() conn := s.conn + closed := s.closed s.mu.Unlock() - if conn == nil { + if conn == nil || conn.IsClosed() { + return ErrConnectionClosed + } + if closed { return ErrBadSubscription } if conn.IsDraining() { @@ -3021,8 +3155,9 @@ func (s *Subscription) AutoUnsubscribe(max int) error { } s.mu.Lock() conn := s.conn + closed := s.closed s.mu.Unlock() - if conn == nil { + if conn == nil || closed { return ErrBadSubscription } return conn.unsubscribe(s, max, false) @@ -3069,8 +3204,8 @@ func (nc *Conn) unsubscribe(sub *Subscription, max int, drainMode bool) error { } // NextMsg will return the next message available to a synchronous subscriber -// or block until one is available. A timeout can be used to return when no -// message has been delivered. +// or block until one is available. An error is returned if the subscription is invalid (ErrBadSubscription), +// the connection is closed (ErrConnectionClosed), or the timeout is reached (ErrTimeout). func (s *Subscription) NextMsg(timeout time.Duration) (*Msg, error) { if s == nil { return nil, ErrBadSubscription @@ -3094,7 +3229,7 @@ func (s *Subscription) NextMsg(timeout time.Duration) (*Msg, error) { select { case msg, ok = <-mch: if !ok { - return nil, ErrConnectionClosed + return nil, s.getNextMsgErr() } if err := s.processNextMsgDelivered(msg); err != nil { return nil, err @@ -3113,7 +3248,7 @@ func (s *Subscription) NextMsg(timeout time.Duration) (*Msg, error) { select { case msg, ok = <-mch: if !ok { - return nil, ErrConnectionClosed + return nil, s.getNextMsgErr() } if err := s.processNextMsgDelivered(msg); err != nil { return nil, err @@ -3150,6 +3285,18 @@ func (s *Subscription) validateNextMsgState() error { return nil } +// This is called when the sync channel has been closed. +// The error returned will be either connection or subscription +// closed depending on what caused NextMsg() to fail. +func (s *Subscription) getNextMsgErr() error { + s.mu.Lock() + defer s.mu.Unlock() + if s.connClosed { + return ErrConnectionClosed + } + return ErrBadSubscription +} + // processNextMsgDelivered takes a message and applies the needed // accounting to the stats from the subscription, returning an // error in case we have the maximum number of messages have been @@ -3197,7 +3344,7 @@ func (s *Subscription) Pending() (int, int, error) { } s.mu.Lock() defer s.mu.Unlock() - if s.conn == nil { + if s.conn == nil || s.closed { return -1, -1, ErrBadSubscription } if s.typ == ChanSubscription { @@ -3213,7 +3360,7 @@ func (s *Subscription) MaxPending() (int, int, error) { } s.mu.Lock() defer s.mu.Unlock() - if s.conn == nil { + if s.conn == nil || s.closed { return -1, -1, ErrBadSubscription } if s.typ == ChanSubscription { @@ -3229,7 +3376,7 @@ func (s *Subscription) ClearMaxPending() error { } s.mu.Lock() defer s.mu.Unlock() - if s.conn == nil { + if s.conn == nil || s.closed { return ErrBadSubscription } if s.typ == ChanSubscription { @@ -3254,7 +3401,7 @@ func (s *Subscription) PendingLimits() (int, int, error) { } s.mu.Lock() defer s.mu.Unlock() - if s.conn == nil { + if s.conn == nil || s.closed { return -1, -1, ErrBadSubscription } if s.typ == ChanSubscription { @@ -3271,7 +3418,7 @@ func (s *Subscription) SetPendingLimits(msgLimit, bytesLimit int) error { } s.mu.Lock() defer s.mu.Unlock() - if s.conn == nil { + if s.conn == nil || s.closed { return ErrBadSubscription } if s.typ == ChanSubscription { @@ -3291,7 +3438,7 @@ func (s *Subscription) Delivered() (int64, error) { } s.mu.Lock() defer s.mu.Unlock() - if s.conn == nil { + if s.conn == nil || s.closed { return -1, ErrBadSubscription } return int64(s.delivered), nil @@ -3307,7 +3454,7 @@ func (s *Subscription) Dropped() (int, error) { } s.mu.Lock() defer s.mu.Unlock() - if s.conn == nil { + if s.conn == nil || s.closed { return -1, ErrBadSubscription } return s.dropped, nil @@ -3529,8 +3676,14 @@ func (nc *Conn) close(status Status, doCBs bool, err error) { nc.stopPingTimer() nc.ptmr = nil - // Go ahead and make sure we have flushed the outbound - if nc.conn != nil { + // Need to close and set tcp conn to nil if reconnect loop has stopped, + // otherwise we would incorrectly invoke Disconnect handler (if set) + // down below. + if nc.ar && nc.conn != nil { + nc.conn.Close() + nc.conn = nil + } else if nc.conn != nil { + // Go ahead and make sure we have flushed the outbound nc.bw.Flush() defer nc.conn.Close() } @@ -3583,7 +3736,7 @@ func (nc *Conn) close(status Status, doCBs bool, err error) { // all blocking calls, such as Flush() and NextMsg() func (nc *Conn) Close() { if nc != nil { - nc.close(CLOSED, true, nil) + nc.close(CLOSED, !nc.Opts.NoCallbacksAfterClientClose, nil) } } @@ -3662,12 +3815,12 @@ func (nc *Conn) drainConnection() { err := nc.Flush() if err != nil { pushErr(err) - nc.Close() + nc.close(CLOSED, true, nil) return } // Move to closed state. - nc.Close() + nc.close(CLOSED, true, nil) } // Drain will put a connection into a drain state. All subscriptions will @@ -3773,18 +3926,16 @@ func (nc *Conn) isDrainingPubs() bool { // Stats will return a race safe copy of the Statistics section for the connection. func (nc *Conn) Stats() Statistics { - // Stats are updated either under connection's mu or subsMu mutexes. - // Lock both to safely get them. + // Stats are updated either under connection's mu or with atomic operations + // for inbound stats in processMsg(). nc.mu.Lock() - nc.subsMu.RLock() stats := Statistics{ - InMsgs: nc.InMsgs, - InBytes: nc.InBytes, + InMsgs: atomic.LoadUint64(&nc.InMsgs), + InBytes: atomic.LoadUint64(&nc.InBytes), OutMsgs: nc.OutMsgs, OutBytes: nc.OutBytes, Reconnects: nc.Reconnects, } - nc.subsMu.RUnlock() nc.mu.Unlock() return stats } @@ -3902,70 +4053,74 @@ func NkeyOptionFromSeed(seedFile string) (Option, error) { return Nkey(string(pub), sigCB), nil } -// This is a regex to match decorated jwts in keys/seeds. -// .e.g. -// -----BEGIN NATS USER JWT----- -// eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5... -// ------END NATS USER JWT------ -// -// ************************* IMPORTANT ************************* -// NKEY Seed printed below can be used sign and prove identity. -// NKEYs are sensitive and should be treated as secrets. -// -// -----BEGIN USER NKEY SEED----- -// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM -// ------END USER NKEY SEED------ - -var nscDecoratedRe = regexp.MustCompile(`\s*(?:(?:[-]{3,}[^\n]*[-]{3,}\n)(.+)(?:\n\s*[-]{3,}[^\n]*[-]{3,}\n))`) +// Just wipe slice with 'x', for clearing contents of creds or nkey seed file. +func wipeSlice(buf []byte) { + for i := range buf { + buf[i] = 'x' + } +} func userFromFile(userFile string) (string, error) { - contents, err := ioutil.ReadFile(userFile) + path, err := expandPath(userFile) + if err != nil { + return _EMPTY_, fmt.Errorf("nats: %v", err) + } + + contents, err := ioutil.ReadFile(path) if err != nil { return _EMPTY_, fmt.Errorf("nats: %v", err) } defer wipeSlice(contents) + return jwt.ParseDecoratedJWT(contents) +} - items := nscDecoratedRe.FindAllSubmatch(contents, -1) - if len(items) == 0 { - return string(contents), nil +func homeDir() (string, error) { + if runtime.GOOS == "windows" { + homeDrive, homePath := os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH") + userProfile := os.Getenv("USERPROFILE") + + var home string + if homeDrive == "" || homePath == "" { + if userProfile == "" { + return _EMPTY_, errors.New("nats: failed to get home dir, require %HOMEDRIVE% and %HOMEPATH% or %USERPROFILE%") + } + home = userProfile + } else { + home = filepath.Join(homeDrive, homePath) + } + + return home, nil } - // First result should be the user JWT. - // We copy here so that if the file contained a seed file too we wipe appropriately. - raw := items[0][1] - tmp := make([]byte, len(raw)) - copy(tmp, raw) - return string(tmp), nil + + home := os.Getenv("HOME") + if home == "" { + return _EMPTY_, errors.New("nats: failed to get home dir, require $HOME") + } + return home, nil +} + +func expandPath(p string) (string, error) { + p = os.ExpandEnv(p) + + if !strings.HasPrefix(p, "~") { + return p, nil + } + + home, err := homeDir() + if err != nil { + return _EMPTY_, err + } + + return filepath.Join(home, p[1:]), nil } func nkeyPairFromSeedFile(seedFile string) (nkeys.KeyPair, error) { - var seed []byte contents, err := ioutil.ReadFile(seedFile) if err != nil { return nil, fmt.Errorf("nats: %v", err) } defer wipeSlice(contents) - - items := nscDecoratedRe.FindAllSubmatch(contents, -1) - if len(items) > 1 { - seed = items[1][1] - } else { - lines := bytes.Split(contents, []byte("\n")) - for _, line := range lines { - if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) { - seed = line - break - } - } - } - - if seed == nil { - return nil, fmt.Errorf("nats: No nkey user seed found in %q", seedFile) - } - kp, err := nkeys.FromSeed(seed) - if err != nil { - return nil, err - } - return kp, nil + return jwt.ParseDecoratedNKey(contents) } // Sign authentication challenges from the server. @@ -3982,13 +4137,6 @@ func sigHandler(nonce []byte, seedFile string) ([]byte, error) { return sig, nil } -// Just wipe slice with 'x', for clearing contents of nkey seed file. -func wipeSlice(buf []byte) { - for i := range buf { - buf[i] = 'x' - } -} - type timeoutWriter struct { timeout time.Duration conn net.Conn diff --git a/vendor/github.com/nats-io/nkeys/README.md b/vendor/github.com/nats-io/nkeys/README.md index 5cb87861..8b787cc3 100644 --- a/vendor/github.com/nats-io/nkeys/README.md +++ b/vendor/github.com/nats-io/nkeys/README.md @@ -17,7 +17,7 @@ Ed25519 is fast and resistant to side channel attacks. Generation of a seed key The NATS system will utilize Ed25519 keys, meaning that NATS systems will never store or even have access to any private keys. Authentication will utilize a random challenge response mechanism. -Dealing with 32 byte and 64 byte raw keys can be challenging. NKEYS is designed to formulate keys in a much friendlier fashion and references work done in cryptocurrencies, specifically [Stellar](https://www.stellar.org/). Bitcoin and others used a form of Base58 (or Base58Check) to endode raw keys. Stellar utilized a more traditonal Base32 with a CRC16 and a version or prefix byte. NKEYS utilizes a similar format where the prefix will be 1 byte for public and private keys and will be 2 bytes for seeds. The base32 encoding of these prefixes will yield friendly human readbable prefixes, e.g. '**N**' = server, '**C**' = cluster, '**O**' = operator, '**A**' = account, and '**U**' = user. '**P**' is used for private keys. For seeds, the first encoded prefix is '**S**', and the second character will be the type for the public key, e.g. "**SU**" is a seed for a user key pair, "**SA**" is a seed for an account key pair. +Dealing with 32 byte and 64 byte raw keys can be challenging. NKEYS is designed to formulate keys in a much friendlier fashion and references work done in cryptocurrencies, specifically [Stellar](https://www.stellar.org/). Bitcoin and others used a form of Base58 (or Base58Check) to encode raw keys. Stellar utilized a more traditional Base32 with a CRC16 and a version or prefix byte. NKEYS utilizes a similar format where the prefix will be 1 byte for public and private keys and will be 2 bytes for seeds. The base32 encoding of these prefixes will yield friendly human readable prefixes, e.g. '**N**' = server, '**C**' = cluster, '**O**' = operator, '**A**' = account, and '**U**' = user. '**P**' is used for private keys. For seeds, the first encoded prefix is '**S**', and the second character will be the type for the public key, e.g. "**SU**" is a seed for a user key pair, "**SA**" is a seed for an account key pair. ## Installation @@ -69,4 +69,4 @@ Unless otherwise noted, the NATS source files are distributed under the Apache Version 2.0 license found in the LICENSE file. -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys?ref=badge_large) \ No newline at end of file +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fnkeys?ref=badge_large) diff --git a/vendor/github.com/nats-io/nkeys/main.go b/vendor/github.com/nats-io/nkeys/main.go index 71b56336..2ea3f904 100644 --- a/vendor/github.com/nats-io/nkeys/main.go +++ b/vendor/github.com/nats-io/nkeys/main.go @@ -19,8 +19,8 @@ import ( "errors" ) -// Version -const Version = "0.1.0" +// Version is our current version +const Version = "0.1.3" // Errors var ( @@ -33,6 +33,7 @@ var ( ErrInvalidSignature = errors.New("nkeys: signature verification failed") ErrCannotSign = errors.New("nkeys: can not sign, no private key available") ErrPublicKeyOnly = errors.New("nkeys: no seed or private key available") + ErrIncompatibleKey = errors.New("nkeys: incompatible key") ) // KeyPair provides the central interface to nkeys. @@ -93,7 +94,7 @@ func FromSeed(seed []byte) (KeyPair, error) { return &kp{copy}, nil } -// Create a KeyPair from the raw 32 byte seed for a given type. +// FromRawSeed will create a KeyPair from the raw 32 byte seed for a given type. func FromRawSeed(prefix PrefixByte, rawSeed []byte) (KeyPair, error) { seed, err := EncodeSeed(prefix, rawSeed) if err != nil { diff --git a/vendor/github.com/nats-io/nkeys/strkey.go b/vendor/github.com/nats-io/nkeys/strkey.go index 36fd0536..324ea638 100644 --- a/vendor/github.com/nats-io/nkeys/strkey.go +++ b/vendor/github.com/nats-io/nkeys/strkey.go @@ -17,7 +17,6 @@ import ( "bytes" "encoding/base32" "encoding/binary" - "golang.org/x/crypto/ed25519" ) @@ -47,7 +46,7 @@ const ( PrefixByteUser PrefixByte = 20 << 3 // Base32-encodes to 'U...' // PrefixByteUnknown is for unknown prefixes. - PrefixByteUknown PrefixByte = 23 << 3 // Base32-encodes to 'X...' + PrefixByteUnknown PrefixByte = 23 << 3 // Base32-encodes to 'X...' ) // Set our encoding to not include padding '==' @@ -188,10 +187,11 @@ func DecodeSeed(src []byte) (PrefixByte, []byte, error) { return PrefixByte(b2), raw[2:], nil } +// Prefix returns PrefixBytes of its input func Prefix(src string) PrefixByte { b, err := decode([]byte(src)) if err != nil { - return PrefixByteUknown + return PrefixByteUnknown } prefix := PrefixByte(b[0]) err = checkValidPrefixByte(prefix) @@ -203,7 +203,7 @@ func Prefix(src string) PrefixByte { if PrefixByte(b1) == PrefixByteSeed { return PrefixByteSeed } - return PrefixByteUknown + return PrefixByteUnknown } // IsValidPublicKey will decode and verify that the string is a valid encoded public key. @@ -288,3 +288,19 @@ func (p PrefixByte) String() string { } return "unknown" } + +// CompatibleKeyPair returns an error if the KeyPair doesn't match expected PrefixByte(s) +func CompatibleKeyPair(kp KeyPair, expected ...PrefixByte) error { + pk, err := kp.PublicKey() + if err != nil { + return err + } + pkType := Prefix(pk) + for _, k := range expected { + if pkType == k { + return nil + } + } + + return ErrIncompatibleKey +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6730441a..256cd290 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,10 +1,10 @@ -# github.com/nats-io/jwt v0.3.0 +# github.com/nats-io/jwt v0.3.2 github.com/nats-io/jwt -# github.com/nats-io/nats.go v1.8.1 +# github.com/nats-io/nats.go v1.9.1 github.com/nats-io/nats.go github.com/nats-io/nats.go/encoders/builtin github.com/nats-io/nats.go/util -# github.com/nats-io/nkeys v0.1.0 +# github.com/nats-io/nkeys v0.1.3 github.com/nats-io/nkeys # github.com/nats-io/nuid v1.0.1 github.com/nats-io/nuid