Add query to api
This commit is contained in:
parent
6c20033bcc
commit
946bdd2a6a
@ -25,7 +25,7 @@ ListenAddr = ":2222"
|
||||
# Throttle incoming and outgoing data per connection
|
||||
# Values are in bytes per second. Empty means no unlimited
|
||||
# Default: ""
|
||||
ThrottleSpeed = 10240
|
||||
ThrottleSpeed = 10240.0
|
||||
|
||||
[Frontend]
|
||||
# Log level for SSH Honeypot
|
||||
|
@ -3,6 +3,7 @@ package store
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.uio.no/torjus/apiary/models"
|
||||
@ -116,6 +117,31 @@ func (ms *MemoryStore) statTotals() ([]StatsResult, error) {
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (ms *MemoryStore) Query(query AttemptQuery) ([]models.LoginAttempt, error) {
|
||||
var results []models.LoginAttempt
|
||||
ms.lock.Lock()
|
||||
defer ms.lock.Unlock()
|
||||
|
||||
for _, la := range ms.attempts {
|
||||
switch query.QueryType {
|
||||
case AttemptQueryTypeIP:
|
||||
if la.RemoteIP.String() == query.Query {
|
||||
results = append(results, la)
|
||||
}
|
||||
case AttemptQueryTypePassword:
|
||||
if strings.Contains(la.Password, query.Query) {
|
||||
results = append(results, la)
|
||||
}
|
||||
case AttemptQueryTypeUsername:
|
||||
if strings.Contains(la.Username, query.Query) {
|
||||
results = append(results, la)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func toResults(m map[string]int) []StatsResult {
|
||||
var results []StatsResult
|
||||
|
||||
|
@ -158,3 +158,39 @@ func (s *PostgresStore) statsTotal(limit int) ([]StatsResult, error) {
|
||||
{Name: "TotalLoginAttempts", Count: attemptsCount},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) Query(query AttemptQuery) ([]models.LoginAttempt, error) {
|
||||
var stmt string
|
||||
|
||||
switch query.QueryType {
|
||||
case AttemptQueryTypeIP:
|
||||
stmt = `SELECT id, date, remote_ip, username, password, client_version, connection_uuid, country
|
||||
FROM login_attempts WHERE remote_ip = $1`
|
||||
case AttemptQueryTypePassword:
|
||||
stmt = `SELECT id, date, remote_ip, username, password, client_version, connection_uuid, country
|
||||
FROM login_attempts WHERE password like '%$1%'`
|
||||
case AttemptQueryTypeUsername:
|
||||
stmt = `SELECT id, date, remote_ip, username, password, client_version, connection_uuid, country
|
||||
FROM login_attempts WHERE username like '%$1%'`
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid query type")
|
||||
}
|
||||
|
||||
rows, err := s.db.Query(stmt, query.Query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to query database: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []models.LoginAttempt
|
||||
for rows.Next() {
|
||||
var la models.LoginAttempt
|
||||
if err := rows.Scan(&la.ID, &la.Date, &la.RemoteIP, &la.Username, &la.Password, &la.SSHClientVersion, &la.ConnectionUUID, &la.Country); err != nil {
|
||||
return nil, fmt.Errorf("Unable to unmarshal data from database: %w", err)
|
||||
}
|
||||
results = append(results, la)
|
||||
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
@ -13,13 +13,26 @@ const (
|
||||
LoginStatsTotals LoginStats = "total"
|
||||
)
|
||||
|
||||
type AttemptQueryType string
|
||||
|
||||
const (
|
||||
AttemptQueryTypeUsername AttemptQueryType = "username"
|
||||
AttemptQueryTypePassword AttemptQueryType = "password"
|
||||
AttemptQueryTypeIP AttemptQueryType = "ip"
|
||||
)
|
||||
|
||||
type StatsResult struct {
|
||||
Name string `json:"name"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type AttemptQuery struct {
|
||||
QueryType AttemptQueryType
|
||||
Query string
|
||||
}
|
||||
type LoginAttemptStore interface {
|
||||
AddAttempt(l *models.LoginAttempt) error
|
||||
All() ([]models.LoginAttempt, error)
|
||||
Stats(statType LoginStats, limit int) ([]StatsResult, error)
|
||||
Query(query AttemptQuery) ([]models.LoginAttempt, error)
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ func NewServer(cfg config.FrontendConfig, hs *honeypot.HoneypotServer, store sto
|
||||
r.Use(middleware.SetHeader("Content-Type", "application/json"))
|
||||
r.Get("/stats", s.HandlerStats)
|
||||
r.Get("/stream", s.HandlerAttemptStream)
|
||||
r.Get("/query", s.HandlerQuery)
|
||||
})
|
||||
})
|
||||
s.Handler = r
|
||||
@ -185,6 +186,7 @@ func (s *Server) HandlerAttemptStream(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) HandlerStats(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
statType := store.LoginStats(r.URL.Query().Get("type"))
|
||||
if statType == store.LoginStatsUndefined {
|
||||
statType = store.LoginStatsPasswords
|
||||
@ -209,6 +211,36 @@ func (s *Server) HandlerStats(w http.ResponseWriter, r *http.Request) {
|
||||
s.ServerLogger.Debugf("Error encoding or writing response", "remote_ip", r.RemoteAddr, "error", err)
|
||||
}
|
||||
}
|
||||
func (s *Server) HandlerQuery(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
queryType := r.URL.Query().Get("type")
|
||||
query := r.URL.Query().Get("query")
|
||||
|
||||
if query == "" || queryType == "" {
|
||||
s.WriteAPIError(w, r, http.StatusBadRequest, "Invalid query or query type")
|
||||
return
|
||||
}
|
||||
|
||||
aq := store.AttemptQuery{
|
||||
QueryType: store.AttemptQueryType(queryType),
|
||||
Query: query,
|
||||
}
|
||||
|
||||
results, err := s.store.Query(aq)
|
||||
if err != nil {
|
||||
s.WriteAPIError(w, r, http.StatusInternalServerError, "Unable to perform query")
|
||||
s.ServerLogger.Warnw("Error performing query", "error", err)
|
||||
return
|
||||
}
|
||||
if results == nil {
|
||||
results = []models.LoginAttempt{}
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(w)
|
||||
if err := encoder.Encode(&results); err != nil {
|
||||
s.ServerLogger.Warnw("Error writing query results", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
type APIErrorResponse struct {
|
||||
Error string `json:"error"`
|
||||
|
Loading…
Reference in New Issue
Block a user