Add SIP message validation feature

Implements RFC 3261 compliance checking and security validation:

- Three validation modes: permissive (default), strict, paranoid
- Critical checks: null bytes, binary injection (immediate ban)
- RFC compliance: required headers (Via, From, To, Call-ID, CSeq, Max-Forwards)
- Format validation: CSeq range, Content-Length, Via branch format
- Paranoid mode: SQL injection patterns, excessive headers, long values
- Compact header form support (v, f, t, i, l, etc.)

Caddyfile configuration:
  validation {
      enabled true
      mode permissive
      max_message_size 65535
      ban_on_null_bytes true
      ban_on_binary_injection true
      disabled_rules via_invalid_branch
  }

New Prometheus metrics:
- sip_guardian_validation_violations_total{rule}
- sip_guardian_validation_results_total{result}
- sip_guardian_message_size_bytes (histogram)

Includes comprehensive unit tests covering all validation scenarios.
This commit is contained in:
Ryan Malloy 2025-12-07 15:57:26 -07:00
parent 95a794ba69
commit 976fdf53a5
5 changed files with 1485 additions and 1 deletions

View file

@ -55,6 +55,9 @@ type SIPGuardian struct {
// Enumeration detection configuration
Enumeration *EnumerationConfig `json:"enumeration,omitempty"`
// Validation configuration
Validation *ValidationConfig `json:"validation,omitempty"`
// Runtime state
logger *zap.Logger
bannedIPs map[string]*BanEntry
@ -162,6 +165,16 @@ func (g *SIPGuardian) Provision(ctx caddy.Context) error {
)
}
// Initialize validation with config if specified
if g.Validation != nil {
SetValidationConfig(*g.Validation)
g.logger.Info("SIP validation configured",
zap.String("mode", string(g.Validation.Mode)),
zap.Bool("enabled", g.Validation.Enabled),
zap.Int("max_message_size", g.Validation.MaxMessageSize),
)
}
// Start cleanup goroutine
go g.cleanupLoop(ctx)
@ -174,6 +187,7 @@ func (g *SIPGuardian) Provision(ctx caddy.Context) error {
zap.Bool("geoip_enabled", g.geoIP != nil),
zap.Int("webhook_count", len(g.Webhooks)),
zap.Bool("enumeration_enabled", g.Enumeration != nil),
zap.Bool("validation_enabled", g.Validation != nil && g.Validation.Enabled),
)
return nil