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:

PriorityProtocolTypical Use
10HTTPSPrimary C2 channel
20DNSDNS-based C2 (Cobalt Strike DNS, dnscat2)
30MQTTMQTT-based C2
40WebSocketWebSocket transport (Mythic, Chisel)
50TCP TunnelRaw 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:

  1. The recovered protocol must outrank the currently active one
  2. A cooldown period must have elapsed since the last switch (default 60s) to prevent flapping
  3. 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 failback

The 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/switch

Manual 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"} 1

Status API#

curl -H "Authorization: Bearer $TOKEN" https://ig:8080/api/failover/status

Returns the active protocol, all protocol health states, failure counts, and timestamps.