# Caddy SIP Guardian
[](https://go.dev/)
[](https://caddyserver.com/)
[](LICENSE)
[](https://git.supported.systems/rsp2k/caddy-sip-guardian)
> **A comprehensive Caddy module providing SIP-aware security at Layer 4.**
> Protects your VoIP infrastructure with intelligent rate limiting, attack detection, message validation, and topology hiding.
---
## Why SIP Guardian?
Traditional SIP security (like fail2ban) parses logs *after* attacks reach your PBX. SIP Guardian operates at **Layer 4**, blocking threats *before* they touch your infrastructure:
| Traditional Approach | SIP Guardian |
|---------------------|--------------|
| Log parsing delay | Real-time blocking |
| Regex-based detection | Protocol-aware analysis |
| Separate fail2ban config | Single Caddyfile |
| No topology protection | Full B2BUA-lite hiding |
| Manual IP management | Auto-ban with API control |
---
## Features
### 🛡️ Core Protection
- **Layer 4 SIP Proxying** — Handle SIP traffic (UDP/TCP/TLS) before it reaches your PBX
- **Intelligent Rate Limiting** — Per-method token bucket rate limiting with burst support
- **Automatic Banning** — Ban IPs that exceed failure thresholds
- **Attack Detection** — Detect common SIP scanning tools (SIPVicious, friendly-scanner, etc.)
- **CIDR Whitelisting** — Whitelist trusted networks
- **GeoIP Blocking** — Block traffic by country using MaxMind databases
### 🔍 Extension Enumeration Detection
- **Count-based Detection** — Block IPs probing too many unique extensions
- **Sequential Pattern Detection** — Detect numeric extension scanning (100, 101, 102...)
- **Rapid-fire Detection** — Catch high-speed enumeration attempts
- **Configurable Exemptions** — Whitelist common extensions like voicemail
### ✅ SIP Message Validation
- **RFC 3261 Compliance** — Enforce required headers and message structure
- **Injection Prevention** — Block NULL bytes and binary injection attacks
- **Content-Length Validation** — Detect body/header mismatches
- **Multiple Modes** — Permissive, strict, or paranoid validation
### 🔒 Topology Hiding (B2BUA-lite)
- **Via Header Rewriting** — Hide internal proxy chain
- **Contact Header Rewriting** — Mask internal IP addresses
- **Sensitive Header Stripping** — Remove P-Asserted-Identity, Server, etc.
- **Call-ID Anonymization** — Prevent dialog correlation attacks
- **Private IP Masking** — Automatically hide RFC 1918 addresses
### 📊 Observability
- **Prometheus Metrics** — Comprehensive metrics for monitoring
- **Webhook Notifications** — Real-time alerts for security events
- **SQLite Persistence** — Durable ban storage across restarts
- **Admin API** — RESTful API for management and stats
---
## Architecture
```
Internet
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Caddy SIP Guardian (Layer 4) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 1. SIP Matcher ─ Identifies SIP traffic │ │
│ │ 2. Ban Check ─ Reject banned IPs │ │
│ │ 3. Whitelist Check ─ Skip checks for trusted IPs │ │
│ │ 4. GeoIP Check ─ Block by country │ │
│ │ 5. Validation ─ RFC 3261 compliance │ │
│ │ 6. Pattern Detection ─ Scanner fingerprinting │ │
│ │ 7. Enumeration Check ─ Extension scanning detection │ │
│ │ 8. Rate Limiting ─ Per-method token buckets │ │
│ │ 9. Topology Hiding ─ Header rewriting (optional) │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ FreePBX / Asterisk / Kamailio │
│ (Protected from scanners, enumeration, topology leaks) │
└──────────────────────────────────────────────────────────────────┘
```
---
## Quick Start
```bash
# Clone the repository
git clone https://git.supported.systems/rsp2k/caddy-sip-guardian.git
cd caddy-sip-guardian
# Build the custom Caddy image
make build
# Start the stack
make run
# View logs
make logs
# Run tests (60 tests)
make test
```
---
## Configuration
### Minimal Configuration
```caddyfile
{
layer4 {
udp/:5060 {
@sip sip
route @sip {
sip_guardian {
max_failures 5
ban_time 1h
whitelist 10.0.0.0/8 192.168.0.0/16
}
proxy udp/asterisk:5060
}
}
}
}
```
### Full Configuration
Click to expand complete Caddyfile example
```caddyfile
{
layer4 {
# UDP SIP (standard port)
udp/:5060 {
@sip sip {
methods REGISTER INVITE OPTIONS ACK BYE CANCEL INFO NOTIFY SUBSCRIBE MESSAGE
}
route @sip {
sip_guardian {
# Core settings
max_failures 5
find_time 10m
ban_time 1h
whitelist 10.0.0.0/8 192.168.0.0/16
# GeoIP blocking (optional)
geoip_db /etc/caddy/GeoLite2-Country.mmdb
blocked_countries CN RU
# Per-method rate limiting
rate_limit {
register 10/s burst 20
invite 5/s burst 10
options 20/s burst 50
}
# Extension enumeration detection
enumeration {
max_extensions 20
extension_window 5m
sequential_threshold 5
rapid_fire_count 10
rapid_fire_window 30s
ban_time 2h
exempt_extensions 100 200 9999
}
# SIP message validation
validation {
enabled true
mode strict
max_message_size 65535
ban_on_null_bytes true
ban_on_binary_injection true
}
# Prometheus metrics
metrics {
enabled true
}
# Webhook notifications
webhooks {
url https://hooks.example.com/sip-alerts
events ban unban attack enumeration
}
# SQLite persistence
storage {
path /var/lib/caddy/sip_guardian.db
}
}
# Optional: Topology hiding
sip_topology_hider {
proxy_host 203.0.113.1
proxy_port 5060
upstream udp/192.168.1.100:5060
rewrite_via
rewrite_contact
strip_headers P-Preferred-Identity P-Asserted-Identity Server User-Agent
hide_private_ips
# anonymize_call_id # Optional: randomize Call-IDs
}
proxy udp/freepbx:5060
}
}
# TCP SIP
tcp/:5060 {
@sip sip
route @sip {
sip_guardian { ... }
proxy tcp/freepbx:5060
}
}
# SIP over TLS
tcp/:5061 {
@sip tls sni sip.example.com
route @sip {
sip_guardian { ... }
tls
proxy tcp/freepbx:5060
}
}
}
}
# Admin API
:2020 {
handle /api/sip-guardian/* {
sip_guardian_admin
}
# Prometheus metrics endpoint
handle /metrics {
metrics
}
}
```
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `SIP_UPSTREAM_HOST` | `freepbx` | Upstream SIP server hostname |
| `SIP_UPSTREAM_PORT` | `5060` | Upstream SIP port |
| `SIP_GUARDIAN_MAX_FAILURES` | `5` | Failures before ban |
| `SIP_GUARDIAN_FIND_TIME` | `10m` | Time window for counting failures |
| `SIP_GUARDIAN_BAN_TIME` | `1h` | Ban duration |
---
## Feature Details
### Extension Enumeration Detection
Protects against tools like SIPVicious `svwar` that scan for valid extensions:
```caddyfile
enumeration {
max_extensions 20 # Ban after 20 unique extensions probed
extension_window 5m # Within this time window
sequential_threshold 5 # Ban if 5+ consecutive extensions (100,101,102...)
rapid_fire_count 10 # Ban if 10+ extensions in rapid_fire_window
rapid_fire_window 30s
ban_time 2h # Enumeration bans last longer
exempt_extensions 100 200 9999 # Don't count these (voicemail, etc.)
}
```
**Detection Methods:**
| Method | Trigger | Use Case |
|--------|---------|----------|
| **Count Threshold** | 20+ unique extensions | Catches slow scanners |
| **Sequential Pattern** | 5+ consecutive numbers | Detects `svwar` immediately |
| **Rapid Fire** | 10+ in 30 seconds | Catches fast automated scans |
---
### SIP Message Validation
Enforces RFC 3261 compliance and blocks malformed/malicious packets:
```caddyfile
validation {
enabled true
mode strict # permissive, strict, or paranoid
max_message_size 65535 # Reject oversized messages
ban_on_null_bytes true # Immediate ban for NULL byte injection
ban_on_binary_injection true
disabled_rules via_invalid_branch # Skip specific rules
}
```
**Validation Modes:**
| Mode | Behavior |
|------|----------|
| **Permissive** | Log violations, only block critical attacks |
| **Strict** | Enforce RFC 3261 required headers |
| **Paranoid** | Additional heuristics for edge cases |
**Validation Rules:**
| Rule | Severity | Action |
|------|----------|--------|
| `null_bytes` | 🔴 CRITICAL | Immediate ban |
| `binary_injection` | 🔴 CRITICAL | Immediate ban |
| `missing_via` | 🟠 HIGH | Count toward ban |
| `missing_from` | 🟠 HIGH | Count toward ban |
| `missing_to` | 🟠 HIGH | Count toward ban |
| `missing_call_id` | 🟠 HIGH | Count toward ban |
| `missing_cseq` | 🟠 HIGH | Count toward ban |
| `content_length_mismatch` | 🟠 HIGH | Count toward ban |
| `oversized_message` | 🟠 HIGH | Count toward ban |
| `invalid_request_uri` | 🟡 MEDIUM | Reject only |
---
### Topology Hiding
Hide your internal infrastructure from external attackers:
```caddyfile
sip_topology_hider {
# Your public-facing address
proxy_host 203.0.113.1
proxy_port 5060
# Internal PBX (never exposed)
upstream udp/192.168.1.100:5060
rewrite_via # Add proxy Via, remove on response
rewrite_contact # Replace internal Contact URIs
strip_headers P-Preferred-Identity P-Asserted-Identity Server User-Agent
hide_private_ips # Replace RFC 1918 addresses
# anonymize_call_id # Optional: randomize Call-IDs
}
```
**What It Hides:**
| Header/Field | Before | After |
|--------------|--------|-------|
| Via | `192.168.1.100:5060` | `proxy.example.com:5060` |
| Contact | `` | `` |
| Server | `Asterisk PBX 18.x` | *(removed)* |
| P-Asserted-Identity | `` | *(removed)* |
**Request Flow:**
```
External UA ──────► Caddy ──────► Asterisk
│
├─ Adds Via: SIP/2.0/UDP proxy.example.com;branch=z9hG4bK...
├─ Rewrites Contact:
└─ Strips: Server, P-Asserted-Identity
```
**Response Flow:**
```
Asterisk ──────► Caddy ──────► External UA
│
├─ Removes top Via (proxy's)
└─ Dialog state routes response correctly
```
---
## Admin API
### Endpoints
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/sip-guardian/bans` | List all banned IPs |
| `GET` | `/api/sip-guardian/stats` | View statistics |
| `POST` | `/api/sip-guardian/ban/{ip}` | Manually ban an IP |
| `DELETE` | `/api/sip-guardian/unban/{ip}` | Remove IP from ban list |
| `GET` | `/api/sip-guardian/enumeration/stats` | Enumeration statistics |
| `GET` | `/api/sip-guardian/validation/stats` | Validation statistics |
### Examples
List Banned IPs
```bash
curl http://localhost:2020/api/sip-guardian/bans
```
```json
{
"bans": [
{
"ip": "185.224.128.0",
"reason": "scanner_detected",
"banned_at": "2024-01-15T10:30:00Z",
"expires_at": "2024-01-15T11:30:00Z"
}
],
"total": 1
}
```
View Stats
```bash
curl http://localhost:2020/api/sip-guardian/stats
```
```json
{
"total_requests": 15234,
"blocked_requests": 423,
"active_bans": 12,
"enumeration_detections": 5,
"validation_failures": 89,
"rate_limited": 156
}
```
Manually Ban IP
```bash
curl -X POST http://localhost:2020/api/sip-guardian/ban/192.168.1.100 \
-H "Content-Type: application/json" \
-d '{"reason": "manual_ban", "duration": "24h"}'
```
Unban IP
```bash
curl -X DELETE http://localhost:2020/api/sip-guardian/unban/192.168.1.100
```
---
## Prometheus Metrics
```prometheus
# Core metrics
sip_guardian_requests_total{method="REGISTER"}
sip_guardian_blocked_total{reason="banned"}
sip_guardian_active_bans
# Rate limiting
sip_guardian_rate_limited_total{method="INVITE"}
# Enumeration detection
sip_guardian_enumeration_detections_total{reason="sequential_pattern"}
sip_guardian_enumeration_tracked_ips
# Validation
sip_guardian_validation_violations_total{rule="missing_via"}
sip_guardian_validation_results_total{result="valid"}
sip_guardian_message_size_bytes
# GeoIP
sip_guardian_geoip_blocked_total{country="CN"}
```
---
## Integration Examples
FreePBX / Asterisk
```caddyfile
{
layer4 {
udp/:5060 {
@sip sip
route @sip {
sip_guardian {
max_failures 3
ban_time 24h
whitelist 10.0.0.0/8
}
proxy udp/freepbx:5060
}
}
}
}
```
Kamailio (as SBC)
```caddyfile
{
layer4 {
udp/:5060 {
@sip sip
route @sip {
sip_guardian {
max_failures 5
ban_time 1h
}
sip_topology_hider {
proxy_host sbc.example.com
proxy_port 5060
upstream udp/kamailio:5060
rewrite_via
rewrite_contact
}
proxy udp/kamailio:5060
}
}
}
}
```
High Availability Setup
```caddyfile
{
layer4 {
udp/:5060 {
@sip sip
route @sip {
sip_guardian {
storage {
# Shared storage for HA
path /shared/sip_guardian.db
}
}
# Load balance across PBX cluster
proxy udp/pbx1:5060 udp/pbx2:5060 udp/pbx3:5060 {
lb_policy round_robin
health_check interval=30s
}
}
}
}
}
```
---
## Troubleshooting
### Common Issues
Legitimate users getting banned
**Symptoms:** Users report being unable to connect; they appear in ban list.
**Solutions:**
1. Add their network to the whitelist:
```caddyfile
whitelist 10.0.0.0/8 192.168.0.0/16 YOUR.NETWORK.0.0/16
```
2. Increase `max_failures` threshold
3. Check if their client sends malformed packets (validation logs)
4. Temporarily unban: `curl -X DELETE http://localhost:2020/api/sip-guardian/unban/IP`
SIP traffic not being matched
**Symptoms:** Traffic passes through but SIP Guardian doesn't process it.
**Solutions:**
1. Verify the matcher syntax:
```caddyfile
@sip sip {
methods REGISTER INVITE OPTIONS
}
```
2. Check Caddy logs for matcher errors
3. Ensure traffic is arriving on the correct port (UDP vs TCP)
4. Test with `tcpdump -i any port 5060` to verify traffic flow
Topology hiding breaks calls
**Symptoms:** Calls connect but audio is one-way or missing.
**Solutions:**
1. This is usually an RTP/media issue, not SIP signaling
2. Ensure RTP ports are forwarded through your firewall
3. Check if `hide_private_ips` is replacing IPs in SDP body
4. Verify NAT traversal settings on your PBX
High memory usage
**Symptoms:** Caddy process memory grows over time.
**Solutions:**
1. Dialog/transaction state cleanup runs automatically, but check TTL settings
2. Reduce `extension_window` to clean up enumeration tracking faster
3. Check for memory leaks with `go tool pprof`
4. Ensure cleanup goroutines are running (check logs)
GeoIP database not loading
**Symptoms:** Country blocking not working; errors about mmdb file.
**Solutions:**
1. Download the database:
```bash
wget https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&suffix=tar.gz
```
2. Verify path in config matches actual file location
3. Check file permissions (Caddy needs read access)
4. Ensure you're using GeoLite2-Country, not City database
### Debug Mode
Enable verbose logging:
```caddyfile
{
debug
layer4 {
# ... your config
}
}
```
Check logs:
```bash
docker logs caddy-sip-guardian 2>&1 | grep -E "(sip_guardian|topology)"
```
---
## Building from Source
```bash
# Using xcaddy
xcaddy build \
--with github.com/mholt/caddy-l4 \
--with git.supported.systems/rsp2k/caddy-sip-guardian
# Or with local development
xcaddy build \
--with github.com/mholt/caddy-l4 \
--with git.supported.systems/rsp2k/caddy-sip-guardian=/path/to/local/module
```
### Running Tests
```bash
# Run all unit tests (60 tests)
make test
# Run with verbose output
docker run --rm -v $(pwd):/app -w /app golang:1.25 go test -v ./...
# Test specific feature
go test -v -run TestEnumeration ./...
go test -v -run TestValidation ./...
go test -v -run TestTopology ./...
```
---
## Changelog
### v0.3.0 (2024-12)
- ✨ **Topology Hiding** — B2BUA-lite functionality for hiding internal infrastructure
- ✨ **SIP Message Parsing** — Full RFC 3261 compliant parser with header manipulation
- ✨ **Dialog State Management** — Stateful response routing for topology hiding
- 🐛 Fixed PRNG overflow in branch generation
### v0.2.0 (2024-12)
- ✨ **SIP Message Validation** — RFC 3261 compliance checking
- ✨ **Injection Detection** — NULL byte and binary injection blocking
- ✨ **Validation Modes** — Permissive, strict, and paranoid modes
### v0.1.0 (2024-12)
- ✨ **Extension Enumeration Detection** — Sequential pattern and rapid-fire detection
- ✨ **Per-method Rate Limiting** — Token bucket with configurable burst
- ✨ **GeoIP Blocking** — Country-based blocking with MaxMind
- ✨ **Prometheus Metrics** — Comprehensive observability
- ✨ **Webhook Notifications** — Real-time security alerts
- ✨ **SQLite Persistence** — Durable ban storage
---
## Security Considerations
| Consideration | Recommendation |
|---------------|----------------|
| **Whitelist internal networks** | Always add your LAN to prevent self-blocking |
| **Start permissive** | Begin with permissive validation, tighten after monitoring |
| **Monitor false positives** | Watch metrics for legitimate traffic being blocked |
| **Keep GeoIP updated** | Refresh MaxMind databases monthly |
| **Use webhooks** | Configure alerts for immediate security notification |
| **Protect admin API** | Don't expose `:2020` to the internet |
---
## Detected Attack Patterns
SIP Guardian automatically detects:
| Pattern | Detection Method |
|---------|------------------|
| **SIPVicious** | User-Agent fingerprinting |
| **friendly-scanner** | User-Agent fingerprinting |
| **sipcli / sip-scan** | User-Agent fingerprinting |
| **Sequential scanning** | Extension pattern analysis |
| **NULL byte injection** | Binary content inspection |
| **Malformed packets** | RFC 3261 validation |
---
## License
MIT — see [LICENSE](LICENSE) for details.
## Contributing
Contributions welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Submit a pull request
## Related Projects
- [caddy-l4](https://github.com/mholt/caddy-l4) — Layer 4 proxy for Caddy
- [Caddy](https://caddyserver.com/) — The HTTP/2 web server with automatic HTTPS
- [SIPVicious](https://github.com/EnableSecurity/sipvicious) — SIP security testing tools (for testing your setup)