alot more changes
This commit is contained in:
1 parent
23243ddbdc
commit
0ed2935f04
40 files changed
+2889
-115
No files matched your search
@@ -0,0 +1,110 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.tornberg.me/go-otel/internal/features"
|
||||
"git.tornberg.me/go-otel/internal/storage"
|
||||
"git.tornberg.me/go-otel/internal/triage"
|
||||
"git.tornberg.me/go-otel/internal/uaclass"
|
||||
)
|
||||
|
||||
// SetSourceIPQuerier enables the source address endpoints.
|
||||
func (c *Collector) SetSourceIPQuerier(q storage.SourceIPQuerier) { c.sourceIPs = q }
|
||||
|
||||
func (c *Collector) sourceIPsEnabled(w http.ResponseWriter) bool {
|
||||
if c.sourceIPs == nil {
|
||||
statsNotEnabled(w)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// HandleSourceIPs lists every unique source address ever recorded.
|
||||
// Query: q (substring), hours (only addresses seen within), limit.
|
||||
func (c *Collector) HandleSourceIPs(w http.ResponseWriter, r *http.Request) {
|
||||
if !c.sourceIPsEnabled(w) {
|
||||
return
|
||||
}
|
||||
var since time.Time
|
||||
if _, hours := parseStatsCommon(r); r.URL.Query().Get("hours") != "" {
|
||||
since = time.Now().Add(-time.Duration(hours) * time.Hour)
|
||||
}
|
||||
ips, err := c.sourceIPs.SourceIPs(r.Context(), strings.TrimSpace(r.URL.Query().Get("q")), since, parseLimit(r, 200))
|
||||
if err != nil {
|
||||
log.Printf("failed to list source ips: %v", err)
|
||||
http.Error(w, "failed to list source ips", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Judge scanning behaviour over the last week for the whole page in one
|
||||
// query. A failure only drops the hints; the list itself is still useful.
|
||||
addrs := make([]string, len(ips))
|
||||
for i := range ips {
|
||||
addrs[i] = ips[i].IP
|
||||
}
|
||||
if routes, err := c.sourceIPs.SourceIPRoutes(r.Context(), addrs, time.Now().Add(-7*24*time.Hour)); err != nil {
|
||||
log.Printf("failed to assess source ips: %v", err)
|
||||
} else {
|
||||
for i := range ips {
|
||||
ips[i].BadActor, ips[i].ProbePaths = assessSourceIP(ips[i].IP, routes[ips[i].IP])
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ips": ips})
|
||||
}
|
||||
|
||||
// HandleSourceIPActivity lists the recent requests made from one address.
|
||||
// Query: hours (default 168), limit.
|
||||
func (c *Collector) HandleSourceIPActivity(w http.ResponseWriter, r *http.Request) {
|
||||
if !c.sourceIPsEnabled(w) {
|
||||
return
|
||||
}
|
||||
addr, err := netip.ParseAddr(r.PathValue("ip"))
|
||||
if err != nil {
|
||||
http.Error(w, "invalid ip", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_, hours := parseStatsCommon(r)
|
||||
if r.URL.Query().Get("hours") == "" {
|
||||
hours = 24 * 7
|
||||
}
|
||||
events, err := c.sourceIPs.SourceIPActivity(r.Context(), addr.String(), time.Now().Add(-time.Duration(hours)*time.Hour), parseLimit(r, 200))
|
||||
if err != nil {
|
||||
log.Printf("failed to load source ip activity: %v", err)
|
||||
http.Error(w, "failed to load source ip activity", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
reason, probes := assessSourceIP(addr.String(), events)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"ip": addr.String(), "hours": hours, "events": events,
|
||||
// badActor is the first matching features.MarkBadActors rule over the
|
||||
// returned window; probePaths is how many distinct probe paths it hit.
|
||||
"badActor": reason, "probePaths": probes,
|
||||
})
|
||||
}
|
||||
|
||||
// assessSourceIP applies the same bad-actor rules the detector uses to one
|
||||
// address's requests, so the hint in the UI never disagrees with triage.
|
||||
func assessSourceIP(ip string, events []storage.SourceIPEvent) (reason string, probePaths int) {
|
||||
fe := make([]features.Event, len(events))
|
||||
probes := map[string]struct{}{}
|
||||
for i, e := range events {
|
||||
fe[i] = features.Event{Route: e.Route, StatusCode: int(e.StatusCode), ClientIP: ip, UAClass: uaclass.Of(e.UserAgent)}
|
||||
if triage.IsProbePath(e.Route) {
|
||||
probes[e.Route] = struct{}{}
|
||||
}
|
||||
}
|
||||
features.MarkBadActors(fe, features.BadActorConfig{})
|
||||
for _, e := range fe {
|
||||
if e.BadActor != "" {
|
||||
return e.BadActor, len(probes)
|
||||
}
|
||||
}
|
||||
return "", len(probes)
|
||||
}
|
||||
Reference in new issue
Block a user