2021-10-28 14:35:57 +00:00
|
|
|
package store
|
|
|
|
|
|
|
|
import (
|
2021-11-03 20:45:40 +00:00
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
2022-01-13 08:10:36 +00:00
|
|
|
"git.t-juice.club/torjus/apiary/models"
|
2021-10-28 14:35:57 +00:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
)
|
|
|
|
|
2022-08-26 10:21:52 +00:00
|
|
|
var _ LoginAttemptStore = &MetricsCollectingStore{}
|
|
|
|
|
2021-11-03 20:45:40 +00:00
|
|
|
const tickDuration = 5 * time.Second
|
|
|
|
|
2021-10-28 14:35:57 +00:00
|
|
|
type MetricsCollectingStore struct {
|
2021-11-03 20:45:40 +00:00
|
|
|
store LoginAttemptStore
|
|
|
|
|
2021-10-28 23:45:47 +00:00
|
|
|
attemptsCounter *prometheus.CounterVec
|
|
|
|
uniqueUsernamesCount prometheus.Gauge
|
|
|
|
uniquePasswordsCount prometheus.Gauge
|
|
|
|
uniqueIPsCount prometheus.Gauge
|
2021-10-29 17:15:11 +00:00
|
|
|
totalAttemptsCount prometheus.Gauge
|
2021-11-03 20:45:40 +00:00
|
|
|
|
|
|
|
statsTicker *time.Ticker
|
2021-10-28 14:35:57 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 20:45:40 +00:00
|
|
|
func NewMetricsCollectingStore(ctx context.Context, store LoginAttemptStore) *MetricsCollectingStore {
|
2021-10-28 14:35:57 +00:00
|
|
|
mcs := &MetricsCollectingStore{store: store}
|
|
|
|
|
|
|
|
mcs.attemptsCounter = prometheus.NewCounterVec(
|
|
|
|
prometheus.CounterOpts{
|
|
|
|
Name: "apiary_ssh_attempt_counter",
|
|
|
|
Help: "Total count of login attempts toward SSH",
|
|
|
|
ConstLabels: prometheus.Labels{"service": "honeypot_ssh"},
|
|
|
|
},
|
|
|
|
[]string{"CountryCode"},
|
|
|
|
)
|
2021-10-28 23:45:47 +00:00
|
|
|
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"},
|
|
|
|
})
|
|
|
|
|
2021-10-29 17:15:11 +00:00
|
|
|
mcs.totalAttemptsCount = prometheus.NewGauge(
|
|
|
|
prometheus.GaugeOpts{
|
|
|
|
Name: "apiary_ssh_total_login_attempts",
|
|
|
|
Help: "Total amount of login attempts made.",
|
|
|
|
ConstLabels: prometheus.Labels{"service": "honeypot_ssh"},
|
|
|
|
})
|
|
|
|
|
2021-10-28 14:35:57 +00:00
|
|
|
prometheus.MustRegister(mcs.attemptsCounter)
|
2021-10-28 23:45:47 +00:00
|
|
|
prometheus.MustRegister(mcs.uniqueUsernamesCount)
|
|
|
|
prometheus.MustRegister(mcs.uniquePasswordsCount)
|
|
|
|
prometheus.MustRegister(mcs.uniqueIPsCount)
|
2021-10-29 17:15:11 +00:00
|
|
|
prometheus.MustRegister(mcs.totalAttemptsCount)
|
2021-10-28 14:35:57 +00:00
|
|
|
|
2021-11-03 20:45:40 +00:00
|
|
|
mcs.statsTicker = time.NewTicker(tickDuration)
|
|
|
|
go func() {
|
|
|
|
defer mcs.statsTicker.Stop()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
case <-mcs.statsTicker.C:
|
2022-08-28 00:23:36 +00:00
|
|
|
mcs.Stats(LoginStatsTotals, 0) // nolint: errcheck
|
2021-11-03 20:45:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2021-10-28 14:35:57 +00:00
|
|
|
return mcs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MetricsCollectingStore) AddAttempt(l *models.LoginAttempt) error {
|
|
|
|
err := s.store.AddAttempt(l)
|
|
|
|
s.attemptsCounter.WithLabelValues(l.Country).Inc()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-08-26 10:21:52 +00:00
|
|
|
func (s *MetricsCollectingStore) All() (<-chan models.LoginAttempt, error) {
|
2021-10-28 14:35:57 +00:00
|
|
|
return s.store.All()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MetricsCollectingStore) Stats(statType LoginStats, limit int) ([]StatsResult, error) {
|
2021-10-28 23:45:47 +00:00
|
|
|
stats, err := s.store.Stats(statType, limit)
|
|
|
|
if statType == LoginStatsTotals {
|
2021-11-03 20:45:40 +00:00
|
|
|
// Reset ticker to avoid updating twice within the duration
|
|
|
|
s.statsTicker.Reset(tickDuration)
|
2021-10-28 23:45:47 +00:00
|
|
|
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))
|
2021-10-29 17:15:11 +00:00
|
|
|
case "TotalLoginAttempts":
|
|
|
|
s.totalAttemptsCount.Set(float64(element.Count))
|
2021-10-28 23:45:47 +00:00
|
|
|
default:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stats, err
|
2021-10-28 14:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MetricsCollectingStore) Query(query AttemptQuery) ([]models.LoginAttempt, error) {
|
|
|
|
return s.store.Query(query)
|
|
|
|
}
|
2021-11-05 23:37:28 +00:00
|
|
|
|
2021-11-05 23:41:27 +00:00
|
|
|
func (s *MetricsCollectingStore) IsHealthy() error {
|
|
|
|
return s.store.IsHealthy()
|
2021-11-05 23:37:28 +00:00
|
|
|
}
|