runbound 0.3.1

RFC-compliant DNS recursive resolver — drop-in replacement for Unbound, with integrated REST API
# ─────────────────────────────────────────────────────────────────────────────
# Runbound — High-security / military-grade configuration
#
# Use-case : Air-gapped network, classified environment, or any deployment
#            where strict access control and minimal exposure are required.
#
# Security posture:
#   - Strict ACL: only known subnets allowed, everything else refused
#   - No public DNS forwarding (self-contained recursive resolver)
#   - DoT only (no plaintext DNS)
#   - Rate limit tuned to subnet scale
#   - REST API key hardened via environment variable (never in config file)
# ─────────────────────────────────────────────────────────────────────────────

server:
    interface:  10.0.0.1        # bind to internal NIC only — not exposed externally
    port:       853

    do-ip4:     yes
    do-ip6:     no              # disable IPv6 if not used in this network
    do-udp:     no
    do-tcp:     yes

    # ── Strict access control ─────────────────────────────────────────────────
    # Allow only explicitly listed subnets; refuse (not deny) unrecognised IPs
    # so they receive an explicit REFUSED and aren't left wondering.
    access-control: 127.0.0.0/8    allow   # localhost
    access-control: 10.0.0.0/8     allow   # internal network
    access-control: 172.16.0.0/12  allow   # management VLAN
    access-control: 0.0.0.0/0      refuse  # secure default

    # ── Conservative rate limit ───────────────────────────────────────────────
    # 500 q/s is ample for <1000 hosts; reduce if you want tighter control.
    rate-limit:    500
    cache-max-ttl: 900   # 15 min — short TTL enforces fast propagation of changes

    # ── DNS rebinding protection ─────────────────────────────────────────────
    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16
    private-address: 127.0.0.0/8
    private-address: ::1/128
    private-address: fd00::/8

    # ── Internal authoritative zones ─────────────────────────────────────────
    local-zone: "internal." static

    local-data: "ns1.internal.    300 IN A 10.0.0.1"
    local-data: "ns2.internal.    300 IN A 10.0.0.2"
    local-data: "ca.internal.     300 IN A 10.0.0.10"
    local-data: "ntp.internal.    300 IN A 10.0.0.11"
    local-data: "siem.internal.   300 IN A 10.0.0.20"
    local-data: "bastion.internal. 300 IN A 10.0.0.50"

    # Block common data-exfiltration channels at DNS level
    local-zone: "pastbin.com."    always_nxdomain
    local-zone: "pastebin.com."   always_nxdomain
    local-zone: "hastebin.com."   always_nxdomain
    local-zone: "transfer.sh."    always_nxdomain
    local-zone: "ngrok.io."       always_nxdomain
    local-zone: "ngrok.com."      always_nxdomain

    # ── Memory guard (always active — no config needed) ─────────────────────
    # At ≥ 80 % system RAM: rate-limiter + resolver cache purged automatically.
    # Inflight semaphore: max 4 096 concurrent requests → REFUSED if exceeded.

    # ── TLS (required — no plaintext DNS) ────────────────────────────────────
    # Internal CA-signed certificate; clients must trust your internal CA.
    tls-service-pem: /etc/runbound/cert.pem
    tls-service-key: /etc/runbound/key.pem

    # IMPORTANT: API key must be set via RUNBOUND_API_KEY env var.
    # Never write secrets in config files checked into version control.

# ── Internal recursive resolver (no external forwarding) ─────────────────────
# If the network has no internet access, remove this forward-zone entirely.
# Runbound will return SERVFAIL for names not found locally, which is correct.

# Uncomment only if internet access is available via a controlled egress proxy:
# forward-zone:
#     name:         "."
#     forward-addr: <egress-resolver-ip>@53