From dab03f52dcc3a5b62727806d12565633abb61674 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Mon, 5 Jun 2017 22:07:13 -0400 Subject: [PATCH 1/2] storage: share prometheus models Running of the binary actually caused a panic due to multiple calling of MustRegister(). This fixes that by sharing models in the storage package. --- storage/memory/peer_store.go | 46 +++++----------------------- storage/memorybysubnet/peer_store.go | 46 +++++----------------------- storage/prometheus.go | 44 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 76 deletions(-) create mode 100644 storage/prometheus.go diff --git a/storage/memory/peer_store.go b/storage/memory/peer_store.go index 19a6c20..ecbf95a 100644 --- a/storage/memory/peer_store.go +++ b/storage/memory/peer_store.go @@ -12,7 +12,6 @@ import ( "time" log "github.com/Sirupsen/logrus" - "github.com/prometheus/client_golang/prometheus" "gopkg.in/yaml.v2" "github.com/chihaya/chihaya/bittorrent" @@ -23,44 +22,10 @@ import ( const Name = "memory" func init() { - // Register Prometheus metrics. - prometheus.MustRegister( - promGCDurationMilliseconds, - promInfohashesCount, - promSeedersCount, - promLeechersCount, - ) - // Register the storage driver. storage.RegisterDriver(Name, driver{}) } -var promGCDurationMilliseconds = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "chihaya_storage_gc_duration_milliseconds", - Help: "The time it takes to perform storage garbage collection", - Buckets: prometheus.ExponentialBuckets(9.375, 2, 10), -}) - -var promInfohashesCount = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "chihaya_storage_infohashes_count", - Help: "The number of Infohashes tracked", -}) - -var promSeedersCount = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "chihaya_storage_seeders_count", - Help: "The number of seeders tracked", -}) - -var promLeechersCount = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "chihaya_storage_leechers_count", - Help: "The number of leechers tracked", -}) - -// recordGCDuration records the duration of a GC sweep. -func recordGCDuration(duration time.Duration) { - promGCDurationMilliseconds.Observe(float64(duration.Nanoseconds()) / float64(time.Millisecond)) -} - type driver struct{} func (d driver) NewPeerStore(icfg interface{}) (storage.PeerStore, error) { @@ -272,9 +237,14 @@ func (ps *peerStore) populateProm() { s.RUnlock() } - promInfohashesCount.Set(float64(numInfohashes)) - promSeedersCount.Set(float64(numSeeders)) - promLeechersCount.Set(float64(numLeechers)) + storage.PromInfohashesCount.Set(float64(numInfohashes)) + storage.PromSeedersCount.Set(float64(numSeeders)) + storage.PromLeechersCount.Set(float64(numLeechers)) +} + +// recordGCDuration records the duration of a GC sweep. +func recordGCDuration(duration time.Duration) { + storage.PromGCDurationMilliseconds.Observe(float64(duration.Nanoseconds()) / float64(time.Millisecond)) } func (ps *peerStore) getClock() int64 { diff --git a/storage/memorybysubnet/peer_store.go b/storage/memorybysubnet/peer_store.go index 08921b8..5234297 100644 --- a/storage/memorybysubnet/peer_store.go +++ b/storage/memorybysubnet/peer_store.go @@ -13,7 +13,6 @@ import ( "time" log "github.com/Sirupsen/logrus" - "github.com/prometheus/client_golang/prometheus" "gopkg.in/yaml.v2" "github.com/chihaya/chihaya/bittorrent" @@ -24,44 +23,10 @@ import ( const Name = "memorybysubnet" func init() { - // Register Prometheus metrics. - prometheus.MustRegister( - promGCDurationMilliseconds, - promInfohashesCount, - promSeedersCount, - promLeechersCount, - ) - // Register the storage driver. storage.RegisterDriver(Name, driver{}) } -var promGCDurationMilliseconds = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "chihaya_storage_gc_duration_milliseconds", - Help: "The time it takes to perform storage garbage collection", - Buckets: prometheus.ExponentialBuckets(9.375, 2, 10), -}) - -var promInfohashesCount = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "chihaya_storage_infohashes_count", - Help: "The number of Infohashes tracked", -}) - -var promSeedersCount = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "chihaya_storage_seeders_count", - Help: "The number of seeders tracked", -}) - -var promLeechersCount = prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "chihaya_storage_leechers_count", - Help: "The number of leechers tracked", -}) - -// recordGCDuration records the duration of a GC sweep. -func recordGCDuration(duration time.Duration) { - promGCDurationMilliseconds.Observe(float64(duration.Nanoseconds()) / float64(time.Millisecond)) -} - type driver struct{} func (d driver) NewPeerStore(icfg interface{}) (storage.PeerStore, error) { @@ -311,9 +276,14 @@ func (ps *peerStore) populateProm() { s.RUnlock() } - promInfohashesCount.Set(float64(numInfohashes)) - promSeedersCount.Set(float64(numSeeders)) - promLeechersCount.Set(float64(numLeechers)) + storage.PromInfohashesCount.Set(float64(numInfohashes)) + storage.PromSeedersCount.Set(float64(numSeeders)) + storage.PromLeechersCount.Set(float64(numLeechers)) +} + +// recordGCDuration records the duration of a GC sweep. +func recordGCDuration(duration time.Duration) { + storage.PromGCDurationMilliseconds.Observe(float64(duration.Nanoseconds()) / float64(time.Millisecond)) } func (ps *peerStore) getClock() int64 { diff --git a/storage/prometheus.go b/storage/prometheus.go new file mode 100644 index 0000000..3eb8899 --- /dev/null +++ b/storage/prometheus.go @@ -0,0 +1,44 @@ +package storage + +import "github.com/prometheus/client_golang/prometheus" + +func init() { + // Register the metrics. + prometheus.MustRegister( + PromGCDurationMilliseconds, + PromInfohashesCount, + PromSeedersCount, + PromLeechersCount, + ) +} + +var ( + // PromGCDurationMilliseconds is a histogram used by storage to record the + // durations of execution time required for removing expired peers. + PromGCDurationMilliseconds = prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "chihaya_storage_gc_duration_milliseconds", + Help: "The time it takes to perform storage garbage collection", + Buckets: prometheus.ExponentialBuckets(9.375, 2, 10), + }) + + // PromInfohashesCount is a gauge used to hold the current total amount of + // unique swarms being tracked by a storage. + PromInfohashesCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "chihaya_storage_infohashes_count", + Help: "The number of Infohashes tracked", + }) + + // PromSeedersCount is a gauge used to hold the current total amount of + // unique seeders per swarm. + PromSeedersCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "chihaya_storage_seeders_count", + Help: "The number of seeders tracked", + }) + + // PromLeechersCount is a gauge used to hold the current total amount of + // unique leechers per swarm. + PromLeechersCount = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "chihaya_storage_leechers_count", + Help: "The number of leechers tracked", + }) +) From 0d9a2309fc90698f82451c2f38959f66f5cdd090 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Mon, 5 Jun 2017 22:09:34 -0400 Subject: [PATCH 2/2] middleware/jwt: add debug logs for JWT failures --- middleware/jwt/jwt.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/middleware/jwt/jwt.go b/middleware/jwt/jwt.go index fa0ee41..5ea5c81 100644 --- a/middleware/jwt/jwt.go +++ b/middleware/jwt/jwt.go @@ -155,28 +155,58 @@ func validateJWT(ih bittorrent.InfoHash, jwtBytes []byte, cfgIss, cfgAud string, claims := parsedJWT.Claims() if iss, ok := claims.Issuer(); !ok || iss != cfgIss { + log.WithFields(log.Fields{ + "exists": ok, + "claim": iss, + "config": cfgIss, + }).Debugln("unequal or missing issuer when validating JWT") return jwt.ErrInvalidISSClaim } if aud, ok := claims.Audience(); !ok || !validAudience(aud, cfgAud) { + log.WithFields(log.Fields{ + "exists": ok, + "claim": aud, + "config": cfgAud, + }).Debugln("unequal or missing audience when validating JWT") return jwt.ErrInvalidAUDClaim } if ihClaim, ok := claims.Get("infohash").(string); !ok || !validInfoHash(ihClaim, ih) { + log.WithFields(log.Fields{ + "exists": ok, + "request": ih, + "claim": ihClaim, + }).Debugln("unequal or missing infohash when validating JWT") return errors.New("claim \"infohash\" is invalid") } parsedJWS := parsedJWT.(jws.JWS) kid, ok := parsedJWS.Protected().Get("kid").(string) if !ok { + log.WithFields(log.Fields{ + "exists": ok, + "claim": kid, + }).Debugln("missing kid when validating JWT") return errors.New("invalid kid") } publicKey, ok := publicKeys[kid] if !ok { + log.WithFields(log.Fields{ + "kid": kid, + }).Debugln("missing public key for kid when validating JWT") return errors.New("signed by unknown kid") } - return parsedJWS.Verify(publicKey, jc.SigningMethodRS256) + err = parsedJWS.Verify(publicKey, jc.SigningMethodRS256) + if err != nil { + log.WithFields(log.Fields{ + "err": err, + }).Debugln("failed to verify signature of JWT") + return err + } + + return nil } func validAudience(aud []string, cfgAud string) bool { @@ -188,6 +218,8 @@ func validAudience(aud []string, cfgAud string) bool { return false } +// validInfoHash attempts to match the claim for the Infohash field of a JWT by +// checking both the raw and unescaped forms of the contents of the field. func validInfoHash(claim string, ih bittorrent.InfoHash) bool { if len(claim) == 20 && bittorrent.InfoHashFromString(claim) == ih { return true