mirror of
https://github.com/NotAShelf/watchdog.git
synced 2026-04-17 23:58:19 +00:00
various: reduce file I/O & pre-parse CIDRs
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I288c299d35fdc833c802e22682f14b8e6a6a6964
This commit is contained in:
parent
ad50debb62
commit
0f38a062e9
2 changed files with 66 additions and 52 deletions
|
|
@ -25,18 +25,23 @@ type versionInfo struct {
|
||||||
BuildDate string `json:"buildDate"`
|
BuildDate string `json:"buildDate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getVersionInfo() versionInfo {
|
||||||
|
data, err := os.ReadFile("version.json")
|
||||||
|
if err != nil {
|
||||||
|
return versionInfo{}
|
||||||
|
}
|
||||||
|
var v versionInfo
|
||||||
|
if err := json.Unmarshal(data, &v); err != nil {
|
||||||
|
return versionInfo{}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func getVersion() string {
|
func getVersion() string {
|
||||||
if version != "" {
|
if version != "" {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
data, err := os.ReadFile("version.json")
|
v := getVersionInfo()
|
||||||
if err != nil {
|
|
||||||
return "dev"
|
|
||||||
}
|
|
||||||
var v versionInfo
|
|
||||||
if err := json.Unmarshal(data, &v); err != nil {
|
|
||||||
return "dev"
|
|
||||||
}
|
|
||||||
if v.Version != "" {
|
if v.Version != "" {
|
||||||
return v.Version
|
return v.Version
|
||||||
}
|
}
|
||||||
|
|
@ -47,14 +52,7 @@ func getCommit() string {
|
||||||
if commit != "" {
|
if commit != "" {
|
||||||
return commit
|
return commit
|
||||||
}
|
}
|
||||||
data, err := os.ReadFile("version.json")
|
v := getVersionInfo()
|
||||||
if err != nil {
|
|
||||||
return "none"
|
|
||||||
}
|
|
||||||
var v versionInfo
|
|
||||||
if err := json.Unmarshal(data, &v); err != nil {
|
|
||||||
return "none"
|
|
||||||
}
|
|
||||||
if v.Commit != "" {
|
if v.Commit != "" {
|
||||||
return v.Commit
|
return v.Commit
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"notashelf.dev/watchdog/internal/aggregate"
|
"notashelf.dev/watchdog/internal/aggregate"
|
||||||
"notashelf.dev/watchdog/internal/config"
|
"notashelf.dev/watchdog/internal/config"
|
||||||
|
|
@ -16,12 +17,14 @@ import (
|
||||||
type IngestionHandler struct {
|
type IngestionHandler struct {
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
domainMap map[string]bool // O(1) domain validation
|
domainMap map[string]bool // O(1) domain validation
|
||||||
|
corsOriginMap map[string]bool // O(1) CORS origin validation
|
||||||
pathNorm *normalize.PathNormalizer
|
pathNorm *normalize.PathNormalizer
|
||||||
pathRegistry *aggregate.PathRegistry
|
pathRegistry *aggregate.PathRegistry
|
||||||
refRegistry *normalize.ReferrerRegistry
|
refRegistry *normalize.ReferrerRegistry
|
||||||
metricsAgg *aggregate.MetricsAggregator
|
metricsAgg *aggregate.MetricsAggregator
|
||||||
rateLimiter *ratelimit.TokenBucket
|
rateLimiter *ratelimit.TokenBucket
|
||||||
rng *rand.Rand
|
rng *rand.Rand
|
||||||
|
trustedNetworks []*net.IPNet // Pre-parsed CIDR networks
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new ingestion handler
|
// Creates a new ingestion handler
|
||||||
|
|
@ -47,15 +50,40 @@ func NewIngestionHandler(
|
||||||
domainMap[domain] = true
|
domainMap[domain] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build CORS origin map for O(1) lookup
|
||||||
|
corsOriginMap := make(map[string]bool, len(cfg.Security.CORS.AllowedOrigins))
|
||||||
|
for _, origin := range cfg.Security.CORS.AllowedOrigins {
|
||||||
|
corsOriginMap[origin] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-parse trusted proxy CIDRs to avoid re-parsing on each request
|
||||||
|
trustedNetworks := make([]*net.IPNet, 0, len(cfg.Security.TrustedProxies))
|
||||||
|
for _, cidr := range cfg.Security.TrustedProxies {
|
||||||
|
if _, network, err := net.ParseCIDR(cidr); err == nil {
|
||||||
|
trustedNetworks = append(trustedNetworks, network)
|
||||||
|
} else if ip := net.ParseIP(cidr); ip != nil {
|
||||||
|
// Single IP - create a /32 or /128 network
|
||||||
|
var mask net.IPMask
|
||||||
|
if ip.To4() != nil {
|
||||||
|
mask = net.CIDRMask(32, 32)
|
||||||
|
} else {
|
||||||
|
mask = net.CIDRMask(128, 128)
|
||||||
|
}
|
||||||
|
trustedNetworks = append(trustedNetworks, &net.IPNet{IP: ip, Mask: mask})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &IngestionHandler{
|
return &IngestionHandler{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
domainMap: domainMap,
|
domainMap: domainMap,
|
||||||
|
corsOriginMap: corsOriginMap,
|
||||||
pathNorm: pathNorm,
|
pathNorm: pathNorm,
|
||||||
pathRegistry: pathRegistry,
|
pathRegistry: pathRegistry,
|
||||||
refRegistry: refRegistry,
|
refRegistry: refRegistry,
|
||||||
metricsAgg: metricsAgg,
|
metricsAgg: metricsAgg,
|
||||||
rateLimiter: limiter,
|
rateLimiter: limiter,
|
||||||
rng: rand.New(rand.NewSource(42)),
|
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||||
|
trustedNetworks: trustedNetworks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,13 +241,8 @@ func (h *IngestionHandler) extractIP(r *http.Request) string {
|
||||||
|
|
||||||
// Check if we should trust proxy headers
|
// Check if we should trust proxy headers
|
||||||
trustProxy := false
|
trustProxy := false
|
||||||
if len(h.cfg.Security.TrustedProxies) > 0 {
|
if len(h.trustedNetworks) > 0 {
|
||||||
for _, trustedCIDR := range h.cfg.Security.TrustedProxies {
|
trustProxy = h.ipInNetworks(remoteIP, h.trustedNetworks)
|
||||||
if h.ipInCIDR(remoteIP, trustedCIDR) {
|
|
||||||
trustProxy = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not trusting proxy, return direct IP
|
// If not trusting proxy, return direct IP
|
||||||
|
|
@ -233,12 +256,11 @@ func (h *IngestionHandler) extractIP(r *http.Request) string {
|
||||||
ips := strings.Split(xff, ",")
|
ips := strings.Split(xff, ",")
|
||||||
for i := len(ips) - 1; i >= 0; i-- {
|
for i := len(ips) - 1; i >= 0; i-- {
|
||||||
ip := strings.TrimSpace(ips[i])
|
ip := strings.TrimSpace(ips[i])
|
||||||
if !h.ipInCIDR(ip, "0.0.0.0/0") {
|
if testIP := net.ParseIP(ip); testIP != nil {
|
||||||
continue
|
|
||||||
}
|
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check X-Real-IP header
|
// Check X-Real-IP header
|
||||||
if xri := r.Header.Get("X-Real-IP"); xri != "" {
|
if xri := r.Header.Get("X-Real-IP"); xri != "" {
|
||||||
|
|
@ -249,26 +271,20 @@ func (h *IngestionHandler) extractIP(r *http.Request) string {
|
||||||
return remoteIP
|
return remoteIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if an IP address is within a CIDR range
|
// Checks if an IP address is within any of the trusted networks
|
||||||
func (h *IngestionHandler) ipInCIDR(ip, cidr string) bool {
|
func (h *IngestionHandler) ipInNetworks(ip string, networks []*net.IPNet) bool {
|
||||||
// Parse the IP address
|
|
||||||
testIP := net.ParseIP(ip)
|
testIP := net.ParseIP(ip)
|
||||||
if testIP == nil {
|
if testIP == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the CIDR
|
for _, network := range networks {
|
||||||
_, network, err := net.ParseCIDR(cidr)
|
if network.Contains(testIP) {
|
||||||
if err != nil {
|
return true
|
||||||
// If it's not a CIDR, try as a single IP
|
|
||||||
cidrIP := net.ParseIP(cidr)
|
|
||||||
if cidrIP == nil {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return testIP.Equal(cidrIP)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return network.Contains(testIP)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Classifies device using both screen width and User-Agent parsing
|
// Classifies device using both screen width and User-Agent parsing
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue