Cybersecurity-Projects/PROJECTS/advanced/honeypot-network/internal/httpd/middleware.go

174 lines
3.8 KiB
Go

/*
©AngelaMos | 2026
middleware.go
Request capture middleware for the HTTP honeypot
Wraps every incoming HTTP request to capture the full method, path,
headers, and body. Publishes request events to the event bus and
runs user-agent scanner detection. Each request gets its own
session for tracking in the event pipeline.
*/
package httpd
import (
"bytes"
"encoding/json"
"io"
"net"
"net/http"
"strconv"
"strings"
"time"
"github.com/google/uuid"
"github.com/CarterPerez-dev/hive/internal/config"
"github.com/CarterPerez-dev/hive/internal/event"
"github.com/CarterPerez-dev/hive/internal/ratelimit"
"github.com/CarterPerez-dev/hive/internal/session"
"github.com/CarterPerez-dev/hive/pkg/types"
)
const maxBodyCapture = 64 * 1024
type statusWriter struct {
http.ResponseWriter
status int
size int
}
func (w *statusWriter) WriteHeader(code int) {
w.status = code
w.ResponseWriter.WriteHeader(code)
}
func (w *statusWriter) Write(b []byte) (int, error) {
n, err := w.ResponseWriter.Write(b)
w.size += n
return n, err
}
func requestLogger(
bus *event.Bus,
tracker *session.Tracker,
limiter *ratelimit.IPLimiter,
cfg *config.Config,
next http.Handler,
) http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
srcIP, srcPort := parseHTTPAddr(r)
if !limiter.Allow(srcIP) {
http.Error(
w,
"429 Too Many Requests",
http.StatusTooManyRequests,
)
return
}
sess := tracker.Start(
cfg.Sensor.ID, types.ServiceHTTP,
srcIP, srcPort, cfg.HTTP.Port,
)
defer tracker.End(sess.ID)
var body []byte
if r.Body != nil {
body, _ = io.ReadAll(
io.LimitReader(r.Body, maxBodyCapture),
)
r.Body = io.NopCloser(bytes.NewReader(body))
}
w.Header().Set("Server", cfg.HTTP.ServerHeader)
sw := &statusWriter{
ResponseWriter: w,
status: http.StatusOK,
}
next.ServeHTTP(sw, r)
headers := make(
map[string]string, len(r.Header),
)
for k, v := range r.Header {
headers[k] = strings.Join(v, "; ")
}
serviceData, _ := json.Marshal(
map[string]interface{}{
"method": r.Method,
"path": r.URL.Path,
"query": r.URL.RawQuery,
"headers": headers,
"body": string(body),
"status": sw.status,
"user_agent": r.UserAgent(),
},
)
bus.Publish(config.TopicCommand, &types.Event{
ID: uuid.Must(uuid.NewV7()).String(),
SessionID: sess.ID,
SensorID: cfg.Sensor.ID,
Timestamp: time.Now().UTC(),
ServiceType: types.ServiceHTTP,
EventType: types.EventRequest,
SourceIP: srcIP,
SourcePort: srcPort,
DestPort: cfg.HTTP.Port,
Protocol: types.ProtocolTCP,
SchemaVersion: config.SchemaVersion,
ServiceData: serviceData,
})
scanner := DetectScanner(r.UserAgent())
if scanner != "" {
scanData, _ := json.Marshal(
map[string]string{
"scanner": scanner,
"user_agent": r.UserAgent(),
"path": r.URL.Path,
},
)
bus.Publish(
config.TopicScan,
&types.Event{
ID: uuid.Must(uuid.NewV7()).String(),
SessionID: sess.ID,
SensorID: cfg.Sensor.ID,
Timestamp: time.Now().UTC(),
ServiceType: types.ServiceHTTP,
EventType: types.EventScan,
SourceIP: srcIP,
Protocol: types.ProtocolTCP,
SchemaVersion: config.SchemaVersion,
Tags: []string{
"scanner:" + scanner,
"mitre:T1595",
},
ServiceData: scanData,
},
)
}
},
)
}
func parseHTTPAddr(
r *http.Request,
) (string, int) {
host, portStr, err := net.SplitHostPort(
r.RemoteAddr,
)
if err != nil {
return r.RemoteAddr, 0
}
port, _ := strconv.Atoi(portStr)
return host, port
}