diff --git a/cmd/apiary.go b/cmd/apiary.go index cc60b39..2b5152e 100644 --- a/cmd/apiary.go +++ b/cmd/apiary.go @@ -82,7 +82,17 @@ func ActionServe(c *cli.Context) error { return fmt.Errorf("Invalid store configured") } - s = store.NewMetricsCollectingStore(s) + // Setup interrupt handling + interruptChan := make(chan os.Signal, 1) + signal.Notify(interruptChan, os.Interrupt) + + rootCtx, rootCancel := context.WithCancel(c.Context) + defer rootCancel() + serversCtx, serversCancel := context.WithCancel(rootCtx) + defer serversCancel() + + // Setup metrics collection + s = store.NewMetricsCollectingStore(rootCtx, s) // Setup honeypot hs, err := ssh.NewHoneypotServer(cfg.Honeypot, s) @@ -109,13 +119,6 @@ func ActionServe(c *cli.Context) error { web.TLSConfig = tlsConfig } - // Setup interrupt handling - interruptChan := make(chan os.Signal, 1) - signal.Notify(interruptChan, os.Interrupt) - - rootCtx, rootCancel := context.WithCancel(c.Context) - serversCtx, serversCancel := context.WithCancel(rootCtx) - // Setup portlistener, if configured if cfg.Ports.Enable { portsCtx, cancel := context.WithCancel(rootCtx) diff --git a/honeypot/ssh/store/metrics.go b/honeypot/ssh/store/metrics.go index 1c8cb4b..c9277b4 100644 --- a/honeypot/ssh/store/metrics.go +++ b/honeypot/ssh/store/metrics.go @@ -1,16 +1,22 @@ package store import ( + "context" + "time" + "github.com/prometheus/client_golang/prometheus" "github.uio.no/torjus/apiary/models" ) type MetricsCollectingStore struct { - store LoginAttemptStore - attemptsCounter *prometheus.CounterVec + store LoginAttemptStore + attemptsCounter *prometheus.CounterVec + uniqueUsernamesCount prometheus.Gauge + uniquePasswordsCount prometheus.Gauge + uniqueIPsCount prometheus.Gauge } -func NewMetricsCollectingStore(store LoginAttemptStore) *MetricsCollectingStore { +func NewMetricsCollectingStore(ctx context.Context, store LoginAttemptStore) *MetricsCollectingStore { mcs := &MetricsCollectingStore{store: store} mcs.attemptsCounter = prometheus.NewCounterVec( @@ -21,7 +27,42 @@ func NewMetricsCollectingStore(store LoginAttemptStore) *MetricsCollectingStore }, []string{"CountryCode"}, ) + mcs.uniqueUsernamesCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "apiary_ssh_unique_usernames_count", + Help: "Counter of unique usernames.", + ConstLabels: prometheus.Labels{"service": "honeypot_ssh"}, + }) + + mcs.uniquePasswordsCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "apiary_ssh_unique_passwords_count", + Help: "Counter of unique passwords.", + ConstLabels: prometheus.Labels{"service": "honeypot_ssh"}, + }) + + mcs.uniqueIPsCount = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "apiary_ssh_unique_ips_count", + Help: "Counter of unique IPs.", + ConstLabels: prometheus.Labels{"service": "honeypot_ssh"}, + }) + prometheus.MustRegister(mcs.attemptsCounter) + prometheus.MustRegister(mcs.uniqueUsernamesCount) + prometheus.MustRegister(mcs.uniquePasswordsCount) + prometheus.MustRegister(mcs.uniqueIPsCount) + + // Kinda jank, we just fetch the stats every 10seconds, but it should be cached most of the time. + go func(ctx context.Context) { + ticker := time.NewTicker(10 * time.Second) + select { + case <-ctx.Done(): + return + case <-ticker.C: + mcs.Stats(LoginStatsTotals, 0) + } + }(ctx) return mcs } @@ -37,7 +78,23 @@ func (s *MetricsCollectingStore) All() ([]models.LoginAttempt, error) { } func (s *MetricsCollectingStore) Stats(statType LoginStats, limit int) ([]StatsResult, error) { - return s.store.Stats(statType, limit) + stats, err := s.store.Stats(statType, limit) + if statType == LoginStatsTotals { + for _, element := range stats { + switch element.Name { + case "UniquePasswords": + s.uniquePasswordsCount.Set(float64(element.Count)) + case "UniqueUsernames": + s.uniqueUsernamesCount.Set(float64(element.Count)) + case "UniqueIPs": + s.uniqueIPsCount.Set(float64(element.Count)) + default: + continue + } + } + } + return stats, err + } func (s *MetricsCollectingStore) Query(query AttemptQuery) ([]models.LoginAttempt, error) { diff --git a/version.go b/version.go index 440effb..b167bb3 100644 --- a/version.go +++ b/version.go @@ -5,7 +5,7 @@ import ( "runtime" ) -var Version = "v0.1.15" +var Version = "v0.1.16" var Build string func FullVersion() string {