Skip to content

Pipeline Routing

DNS-collector's architecture is modular and built around three distinct types of components that can be chained together inside a pipeline:

  • Collectors (Inputs): Capture, sniff, or receive DNS traffic from various live streams (DNStap, AFPacket / live network sniffing, UNIX/TCP sockets, file ingestors, tailing logs, etc.).
  • Transformers (Processors): Intercept DNS message streams inline to perform normalization, traffic filtering, GeoIP enrichment, lowercasing, relabeling, privacy masking, or frequency filtering.
  • Loggers (Outputs): Output, route, and store the collected DNS events into file logs, console (stdout), analytical databases (ClickHouse, InfluxDB), log management engines (Loki, Elasticsearch), message queues (Kafka, Redis), or monitoring metrics (Prometheus, Top-N).

Each component is configured within a pipeline stanza, allowing you to build flexible data flow routing topologies.


Pipeline Flow

flowchart LR
    subgraph Sources ["DNS Sources"]
        dns_src["• DNStap<br/>• AFPacket / Sniffing<br/>• File Ingestor"]
    end

    subgraph Colls ["Collectors (Inputs)"]
        coll["• Ingestion<br/>• Parsing<br/>• Decoding"]
    end

    subgraph Trans ["Transformers (Processors)"]
        trans["• GeoIP Enrichment<br/>• Frequency Filtering<br/>• Privacy Masking"]
    end

    subgraph Logs ["Loggers (Outputs)"]
        log["• Routing<br/>• Formatting<br/>• Delivery"]
    end

    subgraph Dests ["Destinations"]
        dest["• Loki / Grafana<br/>• Elasticsearch / Kibana<br/>• ClickHouse / Kafka"]
    end

    dns_src --> coll
    coll --> trans
    trans --> log
    log --> dest

Basic Pipeline Structure

A pipeline stanza defines an Input (Collector) or an Output (Logger), optional Transformers, and a Routing Policy:

pipelines:
  # Ingestion Stanza (Collector)
  - name: "my-collector"
    dnstap:
      listen-ip: "0.0.0.0"
      listen-port: 6000

    # Optional: inline transformations
    transforms:
      normalize:
        enable: true
        qname-lowercase: true
      geoip:
        enable: true
        mmdb-country-file: "/etc/GeoLite2-Country.mmdb"

    # Required for collectors: routing policy
    routing-policy:
      forward: [ "my-logger" ]       # Processed stream
      dropped: [ "dropped-logger" ]  # Dropped/Filtered stream (optional)

  # Output Stanza (Logger)
  - name: "my-logger"
    stdout:
      mode: "text"

Common Pipeline Examples

1. DNStap Input → Multiple Outputs (Fan-out)

In this example, incoming DNStap traffic is split and delivered simultaneously to a JSON log file and to Prometheus metrics:

pipelines:
  - name: "dnstap-ingest"
    dnstap:
      listen-ip: "0.0.0.0"
      listen-port: 6000
    routing-policy:
      forward: [ "json-file", "prom-metrics" ]

  - name: "json-file"
    logfile:
      file-path: "/var/log/dns/queries.json"
      mode: "json"

  - name: "prom-metrics"
    prometheus:
      listen-ip: "0.0.0.0"
      listen-port: 9165

2. Live Network Sniffing (AFPacket) → GeoIP & Frequency Filtering → Loki

In this example, live DNS packets are captured on network interface eth0, enriched with GeoIP metadata, downsampled with frequency-filtering, and forwarded to Grafana Loki:

pipelines:
  - name: "live-capture"
    afpacket:
      interface: "eth0"
      port: 53
    transforms:
      geoip:
        enable: true
        mmdb-country-file: "/etc/GeoLite2-Country.mmdb"
      frequency-filtering:
        enable: true
        target: "qname"
        threshold-heavy: 1000
        action-on-heavy: "sample"
        sample-rate: 100
    routing-policy:
      forward: [ "loki-output" ]

  - name: "loki-output"
    lokiclient:
      server-url: "http://loki:3100/loki/api/v1/push"
      job-name: "dnscollector"
      mode: "flat-json"

3. File Ingestion → ElasticSearch + Error Isolation

In this example, PCAP or log files are ingested, normalized, and streamed into Elasticsearch, while dropped or unparseable queries are stored in a separate error file:

pipelines:
  - name: "file-collector"
    file-ingestor:
      watch-dir: "/var/log/dns/incoming/"
      pcap-filter: "port 53"
    transforms:
      normalize:
        enable: true
        qname-lowercase: true
    routing-policy:
      forward: [ "elasticsearch-output" ]
      dropped: [ "error-log" ]

  - name: "elasticsearch-output"
    elasticsearch:
      url: "http://elasticsearch:9200"
      index: "dns-logs"
      bulk-size: 500

  - name: "error-log"
    logfile:
      file-path: "/var/log/dns/errors.log"
      mode: "text"