// Package cerberus provides lightweight security checks (WAF, ACL, CrowdSec) with notification support. package cerberus import ( "context" "net/http" "strings" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" "github.com/Wikid82/charon/backend/internal/config" "github.com/Wikid82/charon/backend/internal/logger" "github.com/Wikid82/charon/backend/internal/metrics" "github.com/Wikid82/charon/backend/internal/models" "github.com/Wikid82/charon/backend/internal/services" ) // Cerberus provides a lightweight facade for security checks (WAF, CrowdSec, ACL). type Cerberus struct { cfg config.SecurityConfig db *gorm.DB accessSvc *services.AccessListService securityNotifySvc *services.SecurityNotificationService } // New creates a new Cerberus instance func New(cfg config.SecurityConfig, db *gorm.DB) *Cerberus { return &Cerberus{ cfg: cfg, db: db, accessSvc: services.NewAccessListService(db), securityNotifySvc: services.NewSecurityNotificationService(db), } } // IsEnabled returns whether Cerberus features are enabled via config or settings. func (c *Cerberus) IsEnabled() bool { if c.cfg.CerberusEnabled { return true } // If any of the security modes are explicitly enabled, consider Cerberus enabled. // Treat empty values as disabled to avoid treating zero-values ("") as enabled. if c.cfg.CrowdSecMode == "local" { return true } if (c.cfg.WAFMode != "" && c.cfg.WAFMode != "disabled") || c.cfg.RateLimitMode == "enabled" || c.cfg.ACLMode == "enabled" { return true } // Check database setting (runtime toggle) only if db is provided if c.db != nil { var s models.Setting // Check feature flag if err := c.db.Where("key = ?", "feature.cerberus.enabled").First(&s).Error; err == nil { return strings.EqualFold(s.Value, "true") } // Fallback to legacy setting for backward compatibility if err := c.db.Where("key = ?", "security.cerberus.enabled").First(&s).Error; err == nil { return strings.EqualFold(s.Value, "true") } } // Default to true (Optional Features spec) return true } // Middleware returns a Gin middleware that enforces Cerberus checks when enabled. func (c *Cerberus) Middleware() gin.HandlerFunc { return func(ctx *gin.Context) { if !c.IsEnabled() { ctx.Next() return } // WAF: The actual WAF protection is handled by the Coraza plugin at the Caddy layer. // This middleware just tracks metrics for requests when WAF is enabled. // The naive