NetFlow v9 collector for ntopng
ntopng is a free/commercial NetFlow/sFlow analysis console suitible for a variety of use cases. However, if you want to collect NetFlow or sFlow data and load that into ntopng you currently have no choice but to spend 199Euro on nProbe which in my case is more expensive than the Ubiquiti USG that I wanted to collect NetFlow stats from.
Hence, I created netflow2ng.
- Make sure you have a recent version of go. I used 1.14.2. Older versions may have problems.
git clone https://github.com/synfinatic/netflow2ng.gitcd netflow2ngmake- The binary should now be in the
distdirectory. Copy it somewhere appropriate and create the necessary startup script(s).
- Pull the repository using
git clone https://github.com/synfinatic/netflow2ng.git. - Use the optional docker-compose.yaml file with
docker compose up.
Important: When using Docker, you must use host networking due to NAT causing the source
port to change for inbound Netflow packets which breaks netflow2ng.
- For a list of configuration arguments, run
netflow2ng -h. As of v0.1.1, netflow2ng defaults to the ntopng TLV format instead of JSON. If you want to use JSON, you must use it withntopngv6.3 or earlier. - Configure your network device(s) to send NetFlow stats to netflow2ng
- Configure your ntopng
service to read from netflow2ng:
ntopng -i tcp://192.168.1.1:5556where "192.168.1.1" is the IP address of your netflow2ng server. - netflow2ng encrypts the ZMQ feed by default. See ZMQ Encryption below -- especially if you are running ntopng older than 6.7.280831.
Upgrade warning: starting with v0.3.0, netflow2ng encrypts its ZMQ flow
feed by default. This mirrors ntop's own change in ntopng 6.7.280831
(Sept 2026), where secure ZMQ flow collection became the
default
and an ntopng collector silently discards cleartext flows. If you run
ntopng 6.7.280831 or newer, you do not need to change anything — netflow2ng
and ntopng will agree out of the box. If you run an older ntopng, add
--zmq-disable-encryption to netflow2ng or no flows will show up.
Encryption uses CurveZMQ, the same mechanism nProbe uses. ntopng always takes the CURVE server role (it owns the keypair) and netflow2ng takes the CURVE client role, regardless of which side binds the socket. So netflow2ng needs ntopng's public key — never its private key.
| Flag | Environment variable | Purpose |
|---|---|---|
--zmq-encryption-key |
NETFLOW2NG_ZMQ_ENCRYPTION_KEY |
ntopng's 40 character Z85 public key |
--zmq-encryption-key-file |
NETFLOW2NG_ZMQ_ENCRYPTION_KEY_FILE |
Path to a file holding that key (eg: ntopng's zmq-key.pub) |
--zmq-disable-encryption |
NETFLOW2NG_ZMQ_DISABLE_ENCRYPTION |
Send cleartext; required for ntopng older than 6.7.280831 |
--zmq-client-priv-key |
NETFLOW2NG_ZMQ_CLIENT_PRIV_KEY |
Pin netflow2ng's own private key instead of generating a throwaway one at startup |
Precedence is --zmq-encryption-key > --zmq-encryption-key-file >
ntopng's built-in default key. A flag always beats its environment variable.
netflow2ng generates a fresh client keypair every time it starts. ntopng does
not authenticate client keys, so there is normally no reason to set
--zmq-client-priv-key.
Run ntopng with --zmq-encryption. It writes a keypair to its data directory
and shows the public key on the interface's status page in the web UI:
# on the ntopng host, default datadir:
cat /var/lib/ntopng/zmq-key.pubThen point netflow2ng at it:
netflow2ng --zmq-encryption-key '<the 40 char key>'
# or, if the file is readable from the netflow2ng host:
netflow2ng --zmq-encryption-key-file /var/lib/ntopng/zmq-key.pubIf you configure neither flag, netflow2ng uses the public key that ntopng itself falls back to when no key has been configured. That works against a stock ntopng, but the key pair is published in ntopng's source, so anyone who can see your network traffic can decrypt the flows. netflow2ng logs a warning when this happens. Use a dedicated key for anything that matters.
Both sides have to agree. For ntopng older than 6.7.280831 — or if you would rather not encrypt on a trusted link — disable it on both:
netflow2ng --zmq-disable-encryption
ntopng -i tcp://192.168.1.1:5556 --zmq-disable-encryptionnetflow2ng watches its ZMQ socket for handshake events, so the encryption state shows up in the log as soon as a collector connects. A collector connected and the keys match:
level=info msg="A collector completed the ZMQ CURVE handshake on tcp://0.0.0.0:5556"
The keys do not match:
level=warning msg="ZMQ CURVE handshake failed on tcp://0.0.0.0:5556 (failure #1): \
the collector does not hold the private key matching public key <key>. Compare \
--zmq-encryption-key against ntopng's zmq-key.pub, or run both sides with \
--zmq-disable-encryption."
That warning is the only signal you get. A CURVE mismatch fails silently
everywhere else: netflow2ng still logs Started ZMQ listener and Sending first ZMQ message, ntopng still logs Collecting flows on tcp://..., and the
flows just never arrive. Nothing falls back to cleartext.
A collector that keeps retrying with the wrong key is not logged on every attempt -- the first three failures are logged, then every tenth, then every hundredth.
-
Neither handshake line ever appears. ntopng has not reached netflow2ng at all, so this is not an encryption problem: check
--interface tcp://<host>:5556on the ntopng side, routing, and any firewall between them. -
Ask ntopng what it actually received. Its REST API reports the ZMQ receive counters, which is the definitive answer:
curl -s 'http://<ntopng>:3000/lua/rest/v2/get/interface/data.lua?ifid=0' \ | jq '.rsp.zmqRecvStats'
zmq_msg_rcvdclimbing means the feed is working.zmq_msg_rcvdstuck at 0 while netflow2ng reports sending messages means the two sides never completed a handshake. -
Quote your keys. Z85 keys contain
$ & < > [ ] { } ( ) # % ! * ?, all of which the shell will happily mangle --$Kbexpands to nothing and>olbecomes a redirect. Always single-quote them:netflow2ng --zmq-encryption-key '+hO@^5%GQ]^H6=fim{?$i-eu^Qcgi0l1}I:dYFN{'--zmq-encryption-key-filesidesteps the problem entirely and is the better choice anywhere the key is not typed by hand -- compose files, init scripts, and the/etc/default/netflow2ngused by the .deb and .rpm packages. Copy ntopng'szmq-key.pubto the netflow2ng host and point at the copy. -
Check which ntopng you are running.
ntopng --version. 6.7.280831 and newer encrypt by default; older releases need--zmq-disable-encryptionon both sides. -
this build of libzmq has no CURVE support. Your libzmq was built without libsodium. Rebuild it with libsodium, install a distro package that has it, or run with--zmq-disable-encryption. The official netflow2ng Docker image includes CURVE support; you can confirm withldd /usr/bin/netflow2ng | grep sodium. -
ntopng will not finish starting up. ntopng's own
--zmq-encryption-key-privflag is marked "debug only" in its help, and hung ntopng at "Unable to retain privileges for privileged file writing" during testing. Let ntopng generate its own keypair with--zmq-encryptionand read the public key out ofzmq-key.pubinstead.
- Collect NetFlow v9 stats from one or more probes
- Run a ZMQ Publisher for ntopng to collect metrics from
- Encrypted (CurveZMQ) flow delivery, on by default, matching ntopng 6.7.280831+
- Prometheus metrics
- NetFlow Templates available via /templates HTTP endpoint
By default, netflow2ng listens on all addresses on the following ports. This can be changed via configuration arguments.
- NetFlow/IPFIX: 2055
- ZMQ connections (TCP): 5556
- Metrics: 8080
netflow2ng utilizes goflow2 for NetFlow decoding. For more information on what NetFlow fields are supported in netflow2ng, please read the goflow docs.
In theory, adding sFlow/IPFIX/NetFlow v5 support should be pretty trivial, but isn't something I plan on doing due to lack of hardware for testing/need.
- Not 199Euro
- Doesn't support any probe features (sniffing traffic directly)
- Can't write stats to MySQL/disk or act as a NetFlow proxy
- Not tested with lots of probes or on 10Gbit networks
- Targeted for Home/SOHO use.
- No commercial support, etc.
- May not support the latest versions/features of ntopng
- Written in GoLang instead of C/C++