Passive DNS monitoring framework built on Golang.
dnsmonster implements a packet sniffer for DNS traffic. It Ability to accept traffic from a pcap file, a live interface or a dnstap socket,
and Ability to be used to index and store hundreds of thousands of DNS queries per second as it has shown to be capable of indexing 200k+ DNS queries per second on a commodity computer. It aims to be scalable, simple and easy to use, and help
security teams to understand the details about an enterprise’s DNS traffic. dnsmonster doesn’t look to follow DNS conversations, rather it aims to index DNS packets as soon as they come in. It also doesn’t aim to breach
the privacy of the end-users, with the ability to mask Layer 3 IPs (IPv4 and IPv6), enabling teams to perform trend analysis on aggregated data without being able to trace back the queries to an individual. Blogpost
Warning
The code before version 1.x is considered beta quality and is subject to breaking changes. Please check the release notes for each tag to see the list of breaking scenarios between each release, and how to mitigate potential data loss.
Main features
Ability to use Linux’s afpacket and zero-copy packet capture.
Supports BPF
Ability to mask IP address to enhance privacy
Ability to have a pre-processing sampling ratio
Ability to have a list of “skip” fqdns to avoid writing some domains/suffix/prefix to storage
Ability to have a list of “allow” domains, used to log access to certain domains
Hot-reload of skip and allow domain files/urls
Modular output with configurable logic per output stream.
Automatic data retention policy using ClickHouse’s TTL attribute
Built-in Grafana dashboard for ClickHouse output.
Ability to be shipped as a single, statically linked binary
Ability to be configured using environment variables, command line options or configuration file
Ability to sample outputs using ClickHouse’s SAMPLE capability
Ability to send metrics using prometheus and statstd
High compression ratio thanks to ClickHouse’s built-in LZ4 storage
Supports DNS Over TCP, Fragmented DNS (udp/tcp) and IPv6
built-in SIEM integration with Splunk and Microsoft Sentinel
1 - installation
Learn how to install dnsmonster on your platform using Docker, prebuilt binaries, or compiling it from the source on any platform Go supports
dnsmonster has been built with minimum dependencies. In runtime, the only optional dependency for dnsmonster is libpcap. By building dnsmonster without libpcap, you will lose the ability to set bpf filters on your live packet captures.
installation methods
Prebuilt binaries
Each relase of dnsmonster will ship with two binaries. One for Linux amd64, built statically against an Alpine based image, and one for Windows amd64, which depends on a capture library to be installed on the OS. I’ve tested thw Windows binary with the latest version of Wireshark installed on the system and there was no issues to run the executable.
Prebuilt packages
Per each release, the statically-linked binary mentioned above is also wrapped into deb and rpm packages with no dependencies, making it easy to deploy it in Debian and RHEL based distributions. Note that the packages don’t generate any service files or configuration templates at installation time.
Run as a container
The container build process only generates a Linux amd64 output. Since dnsmonster uses raw packet capture funcationality, Docker/Podman daemon must grant the capability to the container
sudo docker run --rm -it --net=host --cap-add NET_RAW --cap-add NET_ADMIN --name dnsmonster ghcr.io/mosajjal/dnsmonster:latest --devName lo --stdoutOutputType=1
Check out the configuration section to understand the provided command line arguments.
Build from the source
with libpcap:
Make sure you have go, libpcap-devel and linux-headers packages installed. The name of the packages might differ based on your distribution. After this, simply clone the repository and run go build .
git clone https://github.com/mosajjal/dnsmonster --depth 1 /tmp/dnsmonster
cd /tmp/dnsmonster
go get
go build -o dnsmonster ./cmd/dnsmonster
without libpcap:
dnsmonster only uses one function from libpcap, and that’s converting the tcpdump-style filters into BPF bytecode. If you can live with no BPF support, you can build dnsmonster without libpcap. Note that for any other platform, the packet capture falls back to libpcap so it becomes a hard dependency (*BSD, Windows, Darwin)
git clone https://github.com/mosajjal/dnsmonster --depth 1 /tmp/dnsmonster
cd /tmp/dnsmonster
go get
go build -o dnsmonster -tags nolibpcap ./cmd/dnsmonster
The above build also works on ARMv7 (RPi4) and AArch64.
Build Statically
If you have a copy of libpcap.a, you can build the statically link it to dnsmonster and build it fully statically. In the code below, please change /root/libpcap-1.9.1/libpcap.a to the location of your copy.
git clone https://github.com/mosajjal/dnsmonster --depth 1 /tmp/dnsmonster
cd /tmp/dnsmonster/
go get
go build --ldflags "-L /root/libpcap-1.9.1/libpcap.a -linkmode external -extldflags \"-I/usr/include/libnl3 -lnl-genl-3 -lnl-3 -static\"" -a -o dnsmonster ./cmd/dnsmonster
For more information on how the statically linked binary is created, take a look at Dockerfiles in the root of the repository responsible for generating the published binaries.
2 - post-installation
Set up services and shell completions for dnsmonster
Post-install
After you install dnsmonster, you might need to take a few extra steps to build services so dnsmonster runs automatically on system startup. These steps aren’t included in the installation process by default.
Systemd service
If you’re using a modern and popular distro like Debian, Ubuntu, Fedora, Arch, RHEL, you’re probably using systemd as your init system. To have dnsmonster as a service, created a file in /etc/systemd/system/ named dnsmonster.service, and define your systemd unit there. The name dnsmonster as a service name is totally optional.
The above systemd service looks at /etc/dnsmonster.ini as a configuration file. Checkout the configuration section to see how that configuration file is generated.
to start the service and ebable it at boot time, run the following
sudo systemctl enable --now dnsmonster.service
You can also build a systemd service that takes the interface name dynamically and runs the dnsmonster instance per interface. To do so, create a service unit like this:
The above unit creates a dynamic systemd service that can be enabled for multiple Interfaces. For example, to run the service for the loopback interface in linux (lo), run the following: