Multi-Protocol C2 Failover#
The ProtocolFailover manager monitors the health of every configured listener protocol and automatically switches to the next available one when the current protocol goes down. When a higher-priority protocol recovers, automatic failback restores it.
Protocol Priority#
Protocols are ranked from highest to lowest priority:
| Priority | Protocol | Typical Use |
|---|---|---|
| 10 | HTTPS | Primary C2 channel |
| 20 | DNS | DNS-based C2 (Cobalt Strike DNS, dnscat2) |
| 30 | MQTT | MQTT-based C2 |
| 40 | WebSocket | WebSocket transport (Mythic, Chisel) |
| 50 | TCP Tunnel | Raw TCP tunnels (Ligolo) |
When the active protocol fails, traffic shifts to the next healthy protocol in priority order.
Health Tracking#
Each protocol tracks:
- Consecutive failure count
- Total failures and successes
- Last failure/success timestamps
A protocol is marked down after max_consecutive_failures consecutive failures (default 3). The failover manager then promotes the next healthy protocol.
Failback#
When a down protocol recovers (records a success), failback is evaluated:
- The recovered protocol must outrank the currently active one
- A cooldown period must have elapsed since the last switch (default 60s) to prevent flapping
- If both conditions pass, traffic shifts back to the higher-priority protocol
Configuration#
Failover is configured at the top level:
failover:
enabled: true
max_consecutive_failures: 3 # failures before marking a protocol down
health_check_interval: 30 # seconds between health probes
failback_cooldown: 60 # seconds before allowing failbackThe failover manager automatically discovers protocols from the listeners config. Each unique listener protocol gets a health tracker.
Background Health Loop#
A background asyncio task re-probes down protocols at health_check_interval to detect recovery. The probe is lightweight and extensible - the default implementation uses an optimistic timer, but operators can override _probe_protocol() with real checks (TCP dial, DNS query, HTTP GET).
Manual Override#
Force traffic to a specific protocol:
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"protocol": "dns", "reason": "testing"}' \
https://ig:8080/api/failover/switchManual overrides still respect the is_up check. You can’t force traffic to a protocol that’s marked down.
Metrics#
Three Prometheus metrics track failover state:
infraguard_failover_events_total{from_protocol="https",to_protocol="dns",reason="consecutive_failures"} 1
infraguard_protocol_health{protocol="https"} 0
infraguard_protocol_health{protocol="dns"} 1
infraguard_active_protocol{protocol="dns"} 1Status API#
curl -H "Authorization: Bearer $TOKEN" https://ig:8080/api/failover/statusReturns the active protocol, all protocol health states, failure counts, and timestamps.