Skip to content

DNS Tunnel Suspicion

Antonios Voulvoulis edited this page Mar 21, 2026 · 1 revision

DNS Tunnel Suspicion Module

Added in: v1.30.0 Mode: Advisory-only (never bans or blocks) Default: Disabled


Overview

The DNS Tunnel Suspicion Module monitors DNS query logs for patterns that indicate DNS tunneling activity. It scores source IPs using 5 configurable signals and reports suspicion levels, but never takes enforcement action.

DNS tunneling tools (iodine, dnscat2, dns2tcp) encode data in DNS queries using high-entropy subdomain labels, excessive TXT records, and deep subdomain nesting. This module detects these patterns.


Quick Start

# Enable monitoring
sudo nftban tunnel enable

# Check status
nftban tunnel status

# Run immediate scan
sudo nftban tunnel scan

# View top suspects
nftban tunnel top

# Get signal breakdown for an IP
nftban tunnel explain 192.168.1.100

Architecture

DNS Logs (BIND/Unbound/dnsmasq/resolved)
        │
        ▼
┌─────────────────────┐
│  DNS Log Parsers    │  Auto-detect resolver, parse to normalized format
│  (4 parsers)        │  Output: SOURCE_IP|QNAME|QTYPE|RCODE
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│  Signal Computation │  Per-IP: entropy, TXT ratio, depth, volume, NXDOMAIN
│  (single-pass awk)  │  Output: signal values (colon-separated)
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│  Scoring Engine     │  Normalize signals → weighted sum → composite score
│  (0-100 score)      │  Levels: NONE / LOW / MEDIUM / HIGH
└────────┬────────────┘
         │
         ▼
┌─────────────────────┐
│  State Management   │  Per-IP state files in /var/lib/nftban/tunnel/
│  + Alerting         │  Email alerts on HIGH (configurable cooldown)
└─────────────────────┘

5 Detection Signals

# Signal Default Weight Default Threshold Description
1 Label Entropy 25% 3.5 bits/char Shannon entropy of subdomain labels. Encoded/encrypted data has entropy >4.0. Normal domains: 2.0-3.0.
2 TXT Frequency 20% 0.15 (15%) Ratio of TXT queries to total. DNS tunnels use TXT for downstream data. Normal: <5%.
3 Subdomain Depth 20% 4 labels Maximum nesting depth. Tunnels encode data in deeply nested labels (e.g., aGVsbG8.ZGF0YQ.tunnel.example.com).
4 Query Volume 20% 100 queries/interval Total DNS queries per source IP per scan interval (5 min). Tunnels generate sustained high volume.
5 NXDOMAIN Ratio 15% 0.20 (20%) Ratio of NXDOMAIN responses. Some tunnel tools generate queries for non-existent subdomains.

Scoring Formula

Each signal is normalized to 0-100 using the formula:

normalized = min(100, (value / threshold) * 50)

At the threshold value, the normalized score is 50. At 2x the threshold, it reaches 100.

The composite score is:

score = (s1 * w1 + s2 * w2 + s3 * w3 + s4 * w4 + s5 * w5) / (w1 + w2 + w3 + w4 + w5)

Suspicion Levels

Level Score Range Meaning
NONE 0-29 No suspicious activity detected
LOW 30-59 Minor anomalies, likely benign
MEDIUM 60-79 Notable patterns, worth investigating
HIGH 80-100 Strong tunnel indicators, investigate immediately

Supported DNS Resolvers

The module auto-detects the active DNS resolver and its log location.

Resolver Detection Log Locations
BIND 9 named binary or named.service / bind9.service /var/log/named/queries.log, /var/log/bind/query.log, extracted from named.conf
Unbound unbound binary or unbound.service /var/log/unbound/unbound.log, extracted from unbound.conf
dnsmasq dnsmasq binary or dnsmasq.service /var/log/dnsmasq.log, /var/log/syslog, /var/log/messages
systemd-resolved systemd-resolved.service journalctl -u systemd-resolved

Override auto-detection via config:

NFTBAN_TUNNEL_DNS_SOURCE="bind"             # Force specific resolver
NFTBAN_TUNNEL_BIND_LOG="/custom/path.log"   # Override log path

CLI Reference

tunnel status

nftban tunnel status             # Full status display
nftban tunnel status --brief     # One-line: ENABLED (H:0 M:0 L:0) dns=bind
nftban tunnel status --json      # JSON output

tunnel enable / disable

sudo nftban tunnel enable        # Persists to main.conf.local, starts timer
sudo nftban tunnel disable       # Persists to main.conf.local, stops timer

tunnel scan

sudo nftban tunnel scan          # Interactive scan with output
sudo nftban tunnel scan --quiet  # Timer mode (minimal output, logs to file)

tunnel top

nftban tunnel top                # Top 10 suspects
nftban tunnel top --limit 20    # Top 20
nftban tunnel top --json        # JSON array

tunnel explain

nftban tunnel explain 192.168.1.1        # Signal breakdown table
nftban tunnel explain 192.168.1.1 --json # JSON with per-signal details

tunnel config

nftban tunnel config             # Show all configuration
nftban tunnel config --json      # JSON output

Configuration

File: /etc/nftban/conf.d/tunnel/main.conf Override: /etc/nftban/conf.d/tunnel/main.conf.local

Key Settings

# Master switch (default: NO)
NFTBAN_TUNNEL_ENABLED="NO"

# Scan interval in minutes (default: 5)
NFTBAN_TUNNEL_SCAN_INTERVAL="5"

# DNS source: auto, bind, unbound, dnsmasq, resolved
NFTBAN_TUNNEL_DNS_SOURCE="auto"

# Suspicion level thresholds
NFTBAN_TUNNEL_THRESHOLD_LOW="30"
NFTBAN_TUNNEL_THRESHOLD_MEDIUM="60"
NFTBAN_TUNNEL_THRESHOLD_HIGH="80"

# Signal weights (must sum to 100)
NFTBAN_TUNNEL_WEIGHT_ENTROPY="25"
NFTBAN_TUNNEL_WEIGHT_TXT_FREQ="20"
NFTBAN_TUNNEL_WEIGHT_SUBDOMAIN_DEPTH="20"
NFTBAN_TUNNEL_WEIGHT_QUERY_VOLUME="20"
NFTBAN_TUNNEL_WEIGHT_NXDOMAIN_RATIO="15"

# Signal thresholds (midpoint of normalization)
NFTBAN_TUNNEL_ENTROPY_THRESHOLD="3.5"
NFTBAN_TUNNEL_TXT_RATIO_THRESHOLD="0.15"
NFTBAN_TUNNEL_SUBDOMAIN_DEPTH_THRESHOLD="4"
NFTBAN_TUNNEL_QUERY_VOLUME_THRESHOLD="100"
NFTBAN_TUNNEL_NXDOMAIN_RATIO_THRESHOLD="0.20"

# State management
NFTBAN_TUNNEL_STATE_TTL="24"           # Hours before state expires
NFTBAN_TUNNEL_MAX_TRACKED_IPS="1000"   # Max IPs to track

# Alerting
NFTBAN_TUNNEL_ALERT_ON_HIGH="YES"
NFTBAN_TUNNEL_ALERT_EMAIL=""           # Falls back to NFTBAN_ALERT_EMAIL
NFTBAN_TUNNEL_ALERT_COOLDOWN="3600"    # Seconds between alerts

# Exclusions
NFTBAN_TUNNEL_EXCLUDE_DOMAINS=""       # Comma-separated, glob patterns
NFTBAN_TUNNEL_EXCLUDE_IPS=""           # Comma-separated, exact match

Systemd Units

Unit Type Description
nftban-tunnel.timer Timer Triggers scan every 5 minutes (30s jitter)
nftban-tunnel.service Oneshot Runs nftban tunnel scan --quiet

The service runs as root with security hardening: NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, MemoryMax=256M.


State Files

State is stored per-IP in /var/lib/nftban/tunnel/:

/var/lib/nftban/tunnel/
  192.168.1.100.state     # IPv4
  2001_db8__1.state       # IPv6 (colons replaced with underscores)
  .last_alert             # Alert cooldown timestamp

Each .state file contains: TIMESTAMP|SCORE|LEVEL|SIGNALS

State files expire after NFTBAN_TUNNEL_STATE_TTL hours (default: 24).


Log File

Location: /var/log/nftban/tunnel.log Rotation: Managed by logrotate (weekly, 4 copies, compress)

Log format:

2026-03-21 14:30:00|HIGH|ip=192.168.1.100 score=87 signals=4.21:0.35:6:450:0.12
2026-03-21 14:30:00|INFO|Scan complete — 1523 queries, 12 IPs scored, HIGH=1 MED=2 LOW=3

Known Limitations (v1.30.0)

  1. No timestamp filtering in BIND/Unbound/dnsmasq parsers — entire log file is parsed each scan. With logrotate this is acceptable. Full filtering planned for v1.31+.
  2. IP exclusions are exact match only — CIDR notation not yet supported.
  3. systemd-resolved parser is best-effort — resolved logging format is not standardized.
  4. Advisory-only — no enforcement mode. Planned for future release.

See Also

Clone this wiki locally