diff --git a/cmd/apiary/apiary.go b/cmd/apiary/apiary.go index e5c8ab3..02df6b8 100644 --- a/cmd/apiary/apiary.go +++ b/cmd/apiary/apiary.go @@ -82,26 +82,6 @@ func ActionServe(c *cli.Context) error { } else { s = pgStore } - case "bolt", "BOLT": - boltStartTime := time.Now() - loggers.rootLogger.Debug("Initializing store.", "store_type", "bolt") - boltStore, err := store.NewBBoltStore(cfg.Store.Bolt.DBPath) - if err != nil { - return err - } - defer boltStore.Close() - - loggers.rootLogger.Info("Initialized store.", "store_type", "bolt", "init_time", time.Since(boltStartTime)) - - if cfg.Store.EnableCache { - loggers.rootLogger.Debug("Initializing store.", "store_type", "cache-bolt") - startTime := time.Now() - cachingStore := store.NewCachingStore(boltStore) - s = cachingStore - loggers.rootLogger.Info("Initialized store.", "store_type", "cache-bolt", "init_time", time.Since(startTime)) - } else { - s = boltStore - } default: return fmt.Errorf("Invalid store configured") } diff --git a/config/config.go b/config/config.go index 8fbae3f..2273f5e 100644 --- a/config/config.go +++ b/config/config.go @@ -18,17 +18,12 @@ type StoreConfig struct { Type string `toml:"Type"` EnableCache bool `toml:"EnableCache"` Postgres PostgresStoreConfig `toml:"Postgres"` - Bolt BoltStoreConfig `toml:"Bolt"` } type PostgresStoreConfig struct { DSN string `toml:"DSN"` } -type BoltStoreConfig struct { - DBPath string `toml:"DBPath"` -} - type HoneypotConfig struct { ListenAddr string `toml:"ListenAddr"` LogLevel string `toml:"LogLevel"` diff --git a/flake.nix b/flake.nix index 044ac3d..7b2c8df 100644 --- a/flake.nix +++ b/flake.nix @@ -75,7 +75,7 @@ mkdir -p web/frontend/dist cp -r ${frontend}/* web/frontend/dist ''; - vendorHash = "sha256-fJnln143V5ajZgEEgVZN2y3dIz9/L9w6ZBLZk/eX61M="; + vendorHash = "sha256-RtYKwUx5m8pL6MUinmK7yNFNxybnN+Xs0k6Cv1CITEU="; ldflags = [ "-X git.t-juice.club/torjus/apiary.Build=${rev}" ]; tags = [ "embed" diff --git a/go.mod b/go.mod index 71b29c6..9a9650f 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/pelletier/go-toml v1.9.5 github.com/prometheus/client_golang v1.21.1 github.com/urfave/cli/v2 v2.27.6 - go.etcd.io/bbolt v1.4.0 golang.org/x/crypto v0.36.0 ) diff --git a/go.sum b/go.sum index 3467883..1758837 100644 --- a/go.sum +++ b/go.sum @@ -160,8 +160,6 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -206,8 +204,6 @@ golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/honeypot/ssh/actions.go b/honeypot/ssh/actions.go deleted file mode 100644 index eae4d06..0000000 --- a/honeypot/ssh/actions.go +++ /dev/null @@ -1,11 +0,0 @@ -package ssh - -type ActionType int - -const ( - ActionTypeLogPassword ActionType = iota - ActionTypeLogPasswordSlow - ActionTypeLogCommandAndExit - ActionTypeSendGarbage -) -const ActionTypeDefault ActionType = ActionTypeLogPassword diff --git a/honeypot/ssh/store/bbolt.go b/honeypot/ssh/store/bbolt.go deleted file mode 100644 index 45e4e59..0000000 --- a/honeypot/ssh/store/bbolt.go +++ /dev/null @@ -1,235 +0,0 @@ -package store - -import ( - "encoding/binary" - "encoding/json" - "fmt" - "sort" - "strings" - - "git.t-juice.club/torjus/apiary/models" - bolt "go.etcd.io/bbolt" -) - -// var _ LoginAttemptStore = &BBoltStore{} - -var bktKeyLogins []byte = []byte("logins") - -type BBoltStore struct { - db *bolt.DB -} - -func NewBBoltStore(path string) (*BBoltStore, error) { - db, err := bolt.Open(path, 0o666, nil) - if err != nil { - return nil, fmt.Errorf("error opening database: %w", err) - } - - var store BBoltStore - store.db = db - - err = db.Update(func(tx *bolt.Tx) error { - _, err := tx.CreateBucketIfNotExists(bktKeyLogins) - return err - }) - if err != nil { - return nil, fmt.Errorf("error creating database bucket: %w", err) - } - - return &store, nil -} - -func (s *BBoltStore) Close() error { - return s.db.Close() -} - -func (s *BBoltStore) AddAttempt(l *models.LoginAttempt) error { - data, err := json.Marshal(l) - if err != nil { - return err - } - - return s.db.Update(func(tx *bolt.Tx) error { - bkt := tx.Bucket(bktKeyLogins) - seq, err := bkt.NextSequence() - if err != nil { - return err - } - key := itob(seq) - return bkt.Put(key, data) - }) -} - -func (s *BBoltStore) All() (<-chan models.LoginAttempt, error) { - ch := make(chan models.LoginAttempt) - - go func() { - _ = s.db.View(func(tx *bolt.Tx) error { - bkt := tx.Bucket(bktKeyLogins) - - c := bkt.Cursor() - - for k, v := c.First(); k != nil; k, v = c.Next() { - var l models.LoginAttempt - if err := json.Unmarshal(v, &l); err != nil { - close(ch) - panic(err) - } - ch <- l - } - - close(ch) - - return nil - }) - }() - - return ch, nil -} - -func (s *BBoltStore) Stats(statType LoginStats, limit int) ([]StatsResult, error) { - if statType == LoginStatsTotals { - return s.statTotals() - } - - counts := make(map[string]int) - err := s.db.View(func(tx *bolt.Tx) error { - bkt := tx.Bucket(bktKeyLogins) - - c := bkt.Cursor() - - for k, v := c.First(); k != nil; k, v = c.Next() { - var l models.LoginAttempt - if err := json.Unmarshal(v, &l); err != nil { - return err - } - - switch statType { - case LoginStatsPasswords: - counts[l.Password]++ - case LoginStatsCountry: - counts[l.Country]++ - case LoginStatsIP: - counts[l.RemoteIP.String()]++ - case LoginStatsUsername: - counts[l.Username]++ - default: - return fmt.Errorf("invalid stat type") - } - } - - return nil - }) - if err != nil { - return nil, fmt.Errorf("error generating stats: %w", err) - } - - if limit < 1 { - return toResults(counts), nil - } - if limit >= len(counts) { - return toResults(counts), nil - } - - var si StatItems - for key := range counts { - si = append(si, StatItem{Key: key, Count: counts[key]}) - } - sort.Sort(si) - - output := make(map[string]int) - for i := len(si) - 1; i > len(si)-limit-1; i-- { - output[si[i].Key] = si[i].Count - } - - return toResults(output), nil -} - -func (s *BBoltStore) statTotals() ([]StatsResult, error) { - passwords := make(map[string]int) - usernames := make(map[string]int) - ips := make(map[string]int) - countries := make(map[string]int) - var count int - - err := s.db.View(func(tx *bolt.Tx) error { - bkt := tx.Bucket(bktKeyLogins) - - c := bkt.Cursor() - - for k, v := c.First(); k != nil; k, v = c.Next() { - var l models.LoginAttempt - if err := json.Unmarshal(v, &l); err != nil { - return err - } - passwords[l.Password] += 1 - usernames[l.Username] += 1 - ips[l.RemoteIP.String()] += 1 - countries[l.Country] += 1 - count++ - } - - return nil - }) - if err != nil { - return nil, err - } - - stats := []StatsResult{ - {Name: "UniquePasswords", Count: len(passwords)}, - {Name: "UniqueUsernames", Count: len(usernames)}, - {Name: "UniqueIPs", Count: len(ips)}, - {Name: "UniqueCountries", Count: len(countries)}, - {Name: "TotalLoginAttempts", Count: count}, - } - return stats, nil -} - -func (s *BBoltStore) Query(query AttemptQuery) ([]models.LoginAttempt, error) { - var results []models.LoginAttempt - err := s.db.View(func(tx *bolt.Tx) error { - bkt := tx.Bucket(bktKeyLogins) - - c := bkt.Cursor() - - for k, v := c.First(); k != nil; k, v = c.Next() { - var l models.LoginAttempt - if err := json.Unmarshal(v, &l); err != nil { - return err - } - - switch query.QueryType { - case AttemptQueryTypeIP: - if l.RemoteIP.String() == query.Query { - results = append(results, l) - } - case AttemptQueryTypePassword: - if strings.Contains(l.Password, query.Query) { - results = append(results, l) - } - case AttemptQueryTypeUsername: - if strings.Contains(l.Username, query.Query) { - results = append(results, l) - } - } - - } - - return nil - }) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *BBoltStore) IsHealthy() error { - // TODO: Do actual healthcheck - return nil -} - -func itob(v uint64) []byte { - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, v) - return b -} diff --git a/honeypot/ssh/store/bbolt_test.go b/honeypot/ssh/store/bbolt_test.go deleted file mode 100644 index 791f849..0000000 --- a/honeypot/ssh/store/bbolt_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package store_test - -import ( - "os" - "testing" - - "git.t-juice.club/torjus/apiary/honeypot/ssh/store" -) - -func TestBBoltStore(t *testing.T) { - dir := t.TempDir() - f, err := os.CreateTemp(dir, "apiary-test-bbolt") - if err != nil { - t.Fatal(err) - } - fname := f.Name() - f.Close() - s, err := store.NewBBoltStore(fname) - if err != nil { - t.Fatal(err) - } - testLoginAttemptStore(s, t) -} - -func FuzzBBoltStore(f *testing.F) { - dir := f.TempDir() - file, err := os.CreateTemp(dir, "apiary-test-bbolt") - if err != nil { - f.Fatal(err) - } - fname := file.Name() - file.Close() - s, err := store.NewBBoltStore(fname) - if err != nil { - f.Fatal(err) - } - fuzzLoginAttemptStore(s, f) -} diff --git a/models/user.go b/models/user.go deleted file mode 100644 index f15781e..0000000 --- a/models/user.go +++ /dev/null @@ -1,32 +0,0 @@ -package models - -import ( - "fmt" - - "golang.org/x/crypto/bcrypt" -) - -var ErrInvalidPassword = fmt.Errorf("invalid password") - -type User struct { - Username string `json:"username"` - PasswordHash []byte `json:"passwordHash"` -} - -func (u *User) SetPassword(password string) error { - newHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - return fmt.Errorf("error hashing new password: %w", err) - } - - u.PasswordHash = newHash - return nil -} - -func (u *User) VerifyPassword(password string) error { - err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password)) - if err != nil { - return ErrInvalidPassword - } - return nil -} diff --git a/version.go b/version.go index ed37e1f..0a72d3b 100644 --- a/version.go +++ b/version.go @@ -6,7 +6,7 @@ import ( ) var ( - Version = "v0.2.3" + Version = "v0.2.4" Build string ) diff --git a/web/frontend.go b/web/frontend.go index 02056e0..ab3e022 100644 --- a/web/frontend.go +++ b/web/frontend.go @@ -40,7 +40,7 @@ func (s *Server) serveIndex(frontendDir string) http.HandlerFunc { defer f.Close() if _, err := io.Copy(w, f); err != nil { - s.ServerLogger.Warnw("Error writing frontend to client.", "err", err) + s.ServerLogger.Warn("Error writing frontend to client.", "err", err) } } return fn