2

Build initial caddy-opnsense-blocker daemon

This commit is contained in:
2026-03-12 00:51:06 +01:00
commit 4e87d84237
21 changed files with 4354 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
package engine
import (
"fmt"
"net"
"strings"
"git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/config"
"git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/model"
)
type Evaluator struct{}
func NewEvaluator() *Evaluator {
return &Evaluator{}
}
func (e *Evaluator) Evaluate(record model.AccessLogRecord, profile config.ProfileConfig, override model.ManualOverride) model.Decision {
switch override {
case model.ManualOverrideForceAllow:
return model.Decision{Action: model.DecisionActionAllow, Reasons: []string{"manual_override_force_allow"}}
case model.ManualOverrideForceBlock:
return model.Decision{Action: model.DecisionActionBlock, Reasons: []string{"manual_override_force_block"}}
}
if record.Status < profile.MinStatus || record.Status > profile.MaxStatus {
return model.Decision{Action: model.DecisionActionNone}
}
ip := net.ParseIP(record.ClientIP)
if ip == nil {
return model.Decision{Action: model.DecisionActionReview, Reasons: []string{"invalid_client_ip"}}
}
if profile.IsExcluded(ip) {
return model.Decision{Action: model.DecisionActionAllow, Reasons: []string{"excluded_cidr"}}
}
if rule, ok := profile.MatchKnownAgent(ip, record.UserAgent); ok {
if rule.Decision == "allow" {
return model.Decision{Action: model.DecisionActionAllow, Reasons: []string{fmt.Sprintf("known_agent_allow:%s", rule.Name)}}
}
return blockDecision(profile.AutoBlock, []string{fmt.Sprintf("known_agent_deny:%s", rule.Name)})
}
blockReasons := make([]string, 0, 3)
for _, prefix := range profile.SuspiciousPrefixes() {
if strings.HasPrefix(record.Path, prefix) {
blockReasons = append(blockReasons, fmt.Sprintf("suspicious_path_prefix:%s", prefix))
break
}
}
if profile.BlockUnexpectedPosts && strings.EqualFold(record.Method, "POST") && !profile.IsAllowedPostPath(record.Path) {
blockReasons = append(blockReasons, "unexpected_post")
}
if profile.BlockPHPPaths && strings.HasSuffix(record.Path, ".php") {
blockReasons = append(blockReasons, "php_path")
}
return blockDecision(profile.AutoBlock, blockReasons)
}
func blockDecision(autoBlock bool, reasons []string) model.Decision {
if len(reasons) == 0 {
return model.Decision{Action: model.DecisionActionNone}
}
if autoBlock {
return model.Decision{Action: model.DecisionActionBlock, Reasons: reasons}
}
return model.Decision{Action: model.DecisionActionReview, Reasons: reasons}
}