Remove unused code #12
@ -82,26 +82,6 @@ func ActionServe(c *cli.Context) error {
|
|||||||
} else {
|
} else {
|
||||||
s = pgStore
|
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:
|
default:
|
||||||
return fmt.Errorf("Invalid store configured")
|
return fmt.Errorf("Invalid store configured")
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,12 @@ type StoreConfig struct {
|
|||||||
Type string `toml:"Type"`
|
Type string `toml:"Type"`
|
||||||
EnableCache bool `toml:"EnableCache"`
|
EnableCache bool `toml:"EnableCache"`
|
||||||
Postgres PostgresStoreConfig `toml:"Postgres"`
|
Postgres PostgresStoreConfig `toml:"Postgres"`
|
||||||
Bolt BoltStoreConfig `toml:"Bolt"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostgresStoreConfig struct {
|
type PostgresStoreConfig struct {
|
||||||
DSN string `toml:"DSN"`
|
DSN string `toml:"DSN"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoltStoreConfig struct {
|
|
||||||
DBPath string `toml:"DBPath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HoneypotConfig struct {
|
type HoneypotConfig struct {
|
||||||
ListenAddr string `toml:"ListenAddr"`
|
ListenAddr string `toml:"ListenAddr"`
|
||||||
LogLevel string `toml:"LogLevel"`
|
LogLevel string `toml:"LogLevel"`
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
mkdir -p web/frontend/dist
|
mkdir -p web/frontend/dist
|
||||||
cp -r ${frontend}/* 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}" ];
|
ldflags = [ "-X git.t-juice.club/torjus/apiary.Build=${rev}" ];
|
||||||
tags = [
|
tags = [
|
||||||
"embed"
|
"embed"
|
||||||
|
1
go.mod
1
go.mod
@ -15,7 +15,6 @@ require (
|
|||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
github.com/prometheus/client_golang v1.21.1
|
github.com/prometheus/client_golang v1.21.1
|
||||||
github.com/urfave/cli/v2 v2.27.6
|
github.com/urfave/cli/v2 v2.27.6
|
||||||
go.etcd.io/bbolt v1.4.0
|
|
||||||
golang.org/x/crypto v0.36.0
|
golang.org/x/crypto v0.36.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
4
go.sum
4
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/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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
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.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.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
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-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.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.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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package ssh
|
|
||||||
|
|
||||||
type ActionType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
ActionTypeLogPassword ActionType = iota
|
|
||||||
ActionTypeLogPasswordSlow
|
|
||||||
ActionTypeLogCommandAndExit
|
|
||||||
ActionTypeSendGarbage
|
|
||||||
)
|
|
||||||
const ActionTypeDefault ActionType = ActionTypeLogPassword
|
|
@ -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
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version = "v0.2.3"
|
Version = "v0.2.4"
|
||||||
Build string
|
Build string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ func (s *Server) serveIndex(frontendDir string) http.HandlerFunc {
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(w, f); err != nil {
|
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
|
return fn
|
||||||
|
Loading…
x
Reference in New Issue
Block a user