Building Your Raspberry Pi CM4 Network Monitor: Installation, Configuration, and Performance

Posted by

In my previous blog I explained that I wanted to build a network monitoring device. I described the hardware I will be using. In this blog I’ll talk about setting everything up and installing the software and configuring it.

Assembly

The assembly is straightforward. On the Waveshare board it’s clearly marked where you need to install the CM4. Align it correctly and gently push untill it snaps in to place.

Connect the USB-C slave port to your computer and power on the device.

Raspberry Pi OS

The first step is to install an OS on the Raspberry Pi CM4. Download the Raspberry Pi Imager from here:

https://www.raspberrypi.com/software/

The GUI is self explaining. I tried both Raspberry Pi OS and Ubuntu Pi OS. Where the Raspberry Pi OS was running smoothly, the Ubuntu Pi OS was very unresponsive from time to time. Needless to say I stuck with Raspberry Pi OS and based on that this blog is created.

Update Pi OS

After flashing the Raspberry it’s time to connect it to your router. Connect the eth1 port to your router. Find the IP address that the CM4 got from your DHCP server and SSH into the CM4. The Waveshare comes with dual HDMI. You can connect the Waveshare to a monitor and continue the setup directly on the CM4.

The first thing you want to do is block SSH on eth0:

iptables -A INPUT -i eth0 -p tcp --dport 22 -j DROP

With that done it’s time to update the CM4.

sudo apt update && sudo apt upgrade

Configure the networkcards. If you are using a monitor on your CM4, use the top right corner where you’ll find the network configuration icon. Otherwise use nmtui from the cli. The setting I used:

eth0 – dynamic IP

eth1 – static IP (192.168.1.254 in my case)

Optional: USB drive for logging

I don’t want to hammer the eMMC chip with logs and will be using an USB drive for logging purposes.

Mine showed up as /dev/sda, you can see yours with:

df -Th

First unmount the partitions:

sudo umount /dev/sda1
sudo umount /dev/sda2

Create a new partition table and single partition:

sudo fdisk /dev/sda
# Press 'd' to delete existing partitions (repeat for all partitions)
# Press 'n' for new partition
# Press 'p' for primary partition
# Press Enter for default first sector
# Press Enter for default last sector
# Press 'w' to write changes

Format the partition with ext4:

sudo mkfs.ext4 /dev/sda1

Create a mount point and mount the drive:

sudo mkdir -p /mnt/ntopng
sudo mount /dev/sda1 /mnt/ntopng
sudo mkdir -p /mnt/ntopng/logs
sudo mkdir -p /mnt/ntopng/flows
sudo mkdir -p /mnt/ntopng/rrd
sudo chown -R ntopng:ntop /mnt/ntopng/*

Add to /etc/fstab for permanent mounting:

echo "/dev/sda1    /mnt/ntopng    ext4    defaults,noatime    0    2" | sudo tee -a /etc/fstab

Install ntopng

I want to monitor the bandwidth per IP and for that I will be using ntopng. Here are the steps how to install it.

# Add ntop repository

wget https://packages.ntop.org/RaspberryPI/apt-ntop.deb
sudo dpkg -i apt-ntop.deb

# Update package information

sudo apt update

# Install ntopng and nprobe

sudo apt install ntopng nprobe

You will need to edit the configuration of ntopng. Here is my configuration:

cat /etc/ntopng/ntopng.conf

# Specify where to store the PID file for process management
-G=/var/run/ntopng.pid

# Set the data directory for storing persistent data on the USB drive
--data-dir=/mnt/ntopng

# Configure flow storage: use disk storage, keep data for 7 days, max 2GB
-F="disk;days=7;max=2048"

# Bind ntopng to CPU core #2 for better performance
--cpu-affinity=2

# Limit maximum number of active flows to 50,000 to control memory usage
--max-num-flows=50000

# Limit maximum number of tracked hosts to 10,000
--max-num-hosts=10000

# Remove idle hosts after 1 hour (3600 seconds)
--host-max-idle=3600

# Export flow data every 30 seconds
--dump-flows=30

# Export host data every 300 seconds (5 minutes)
--dump-hosts=300

# Store log files on the USB drive
--log-dir=/mnt/ntopng/logs

# Monitoring interfaces (eth0,eth1 commented out)
#-i=eth0,eth1

# Currently monitoring only eth0 interface
-i=eth0

# Set web interface port to 3000
-w=3000

# Set URL prefix for the web interface
--http-prefix="/ntop"

# Use local Redis instance for data caching
--redis-host=localhost

# Enable DNS resolution (mode 1: resolve all numeric IPs)
--dns-mode=1

# Enable detailed flow collection for better traffic analysis
--enable-flow-collection

# Listen on port 2055 for NetFlow data from network devices
--collector-port=2055

# Listen on port 6343 for sFlow data from network devices
--collector-port=6343

Optionally: if you used a USB for logging set the correct permissions:

sudo chown ntopng:ntop /mnt/ntopng
sudo chmod 755 /mnt/ntopng

Let’s test the configuration and start ntopng

sudo systemctl enable ntopng
sudo systemctl restart ntopng

You should be able to logon to ntopng on:

http://<CM4 IP>:3000 (in my case http://192.168.1.254:3000)

If not check the status:

sudo systemctl status ntopng

OS configuration

By default IP forwarding is disabled. You need to enable it with this command:

# Enable IP forwarding immediately

sudo echo 1 > /proc/sys/net/ipv4/ip_forward

# Make the change permanent (survives reboots)

sudo echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sudo sysctl -p

Optional: disable IPv6:

sudo echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
sudo echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
sudo echo "net.ipv6.conf.eth0.disable_ipv6 = 1" >> /etc/sysctl.conf

Now traffic from eth0 will be forwarded to eth1 and vice versa.

Iptables configuration

Now that the OS will forward packets, we need to enable that in IPtables.

# Clear existing rules
sudo iptables -F
sudo iptables -t nat -F

# Set default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

# Allow local traffic
sudo iptables -A INPUT -i lo -j ACCEPT

# Allow established connections
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# Allow internal network traffic
sudo iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT

# Block and log ntopng web interface access from WAN
sudo iptables -A INPUT -p tcp --dport 3000 -m limit --limit 5/min -j LOG --log-prefix "ntopng DROP: "
sudo iptables -A INPUT -p tcp --dport 3000 -j DROP

# Block and log SSH access from WAN
sudo iptables -A INPUT -p tcp --dport 22 -m limit --limit 5/min -j LOG --log-prefix "SSH DROP: "
sudo iptables -A INPUT -p tcp --dport 22 -j DROP

# Allow DNS forwarding
sudo iptables -A FORWARD -p udp --dport 53 -j ACCEPT
sudo iptables -A FORWARD -p udp --sport 53 -j ACCEPT

# Allow LAN to WAN traffic
sudo iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT

# Allow related WAN to LAN traffic
sudo iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT

# Enable NAT
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Save configuration:

sudo apt install iptables-persistent
sudo iptables-save > /etc/iptables/rules.v4

That’s it! You should have a working network monitoring tool now!

Fail2ban

As this device is directly behind my modem it will get hammered by hackers, you want to secure it as best as possible. I use fail2ban to block IPs trying to hack the CM4.

Install fail2ban:

sudo apt install fail2ban

First, create a custom jail configuration (don’t modify the default one):

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit the local configuration:

sudo vi /etc/fail2ban/jail.local

Add this:

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
banaction = iptables-multiport

[sshd]
enabled = true
port = ssh
filter = custom-ssh
logpath = /mnt/ntopng/logs/iptables/ssh.log
maxretry = 3
findtime = 600
bantime = 3600

Since we’re using iptables to log SSH drop attempts, we need to create a custom filter:

sudo vi /etc/fail2ban/filter.d/custom-ssh.local

Add this:

[INCLUDES]
before = common.conf

[Definition]
_daemon = (?:kernel|raspberrypi kernel)
failregex = ^.* %(_daemon)s: \[\d+\.\d+\] SSH DROP:.* SRC=<HOST>.*DPT=22
            ^.* %(_daemon)s: SSH DROP:.* SRC=<HOST>.*DPT=22
ignoreregex =

Set up logging directory for iptables:

sudo mkdir -p /mnt/ntopng/logs/iptables
sudo chown syslog:adm /mnt/ntopng/logs/iptables

Configure rsyslog to send iptables logs to this location:

sudo vi /etc/rsyslog.d/10-iptables.conf

Add this:

# Create template for iptables logging
template(name="IptablesFormat" type="string" string="%TIMESTAMP:::date-rfc3339% %msg%\n")

# Log iptables messages
if $msg contains "SSH DROP" then {
    action(type="omfile"
           file="/mnt/ntopng/logs/iptables/ssh.log"
           template="IptablesFormat"
           fileOwner="syslog"
           fileGroup="adm"
           fileCreateMode="0640"
           dirCreateMode="0750")
    stop
}

if $msg contains "ntopng DROP" then {
    action(type="omfile"
           file="/mnt/ntopng/logs/iptables/ntopng.log"
           template="IptablesFormat"
           fileOwner="syslog"
           fileGroup="adm"
           fileCreateMode="0640"
           dirCreateMode="0750")
    stop
}

Restart and enable services:

sudo systemctl restart rsyslog
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban

Verify fail2ban is working:

sudo fail2ban-client status sshd

This setup will automatically ban IPs that attempt to access SSH or the ntopng interface from the internet. The typical ban time is one hour, but you can adjust this based on your security preferences.

Final setup

With all the software installed it’s time to put the CM4 to work. My setup is like this:

Internet -> CM4 eth0

CM4 eth1 -> Router

That way the CM4 is inline and will see all the traffic coming in and going out of my network. This setup gives the best privacy to my family members. That’s because the CM4 sees my router as a client. Everything behind the router is not visible due to NAT.

Performance

I did a simple iperf3 test on the CM4 from my PC to the Waveshare interfaces.

eth1:
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.02   sec  86.5 MBytes   715 Mbits/sec
[  5]   1.02-2.00   sec  76.1 MBytes   647 Mbits/sec
[  5]   2.00-3.00   sec  93.5 MBytes   782 Mbits/sec
[  5]   3.00-4.01   sec   109 MBytes   909 Mbits/sec
[  5]   4.01-5.01   sec   109 MBytes   914 Mbits/sec
[  5]   5.01-6.01   sec   108 MBytes   907 Mbits/sec
[  5]   6.01-7.02   sec  92.5 MBytes   774 Mbits/sec
[  5]   7.02-8.00   sec   102 MBytes   868 Mbits/sec
[  5]   8.00-9.00   sec   110 MBytes   917 Mbits/sec
[  5]   9.00-10.00  sec   109 MBytes   915 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec   996 MBytes   835 Mbits/sec                  receiver


eth0:
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.01   sec   112 MBytes   930 Mbits/sec
[  5]   1.01-2.02   sec   110 MBytes   925 Mbits/sec
[  5]   2.02-3.02   sec   109 MBytes   912 Mbits/sec
[  5]   3.02-4.02   sec   111 MBytes   928 Mbits/sec
[  5]   4.02-5.00   sec   106 MBytes   905 Mbits/sec
[  5]   5.00-6.00   sec   112 MBytes   934 Mbits/sec
[  5]   6.00-7.01   sec   112 MBytes   933 Mbits/sec
[  5]   7.01-8.01   sec   110 MBytes   925 Mbits/sec
[  5]   8.01-9.01   sec   112 MBytes   934 Mbits/sec
[  5]   9.01-10.00  sec   107 MBytes   904 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec  1.07 GBytes   923 Mbits/sec                  receiver

I’m happy with that I see.

 

Conclusion

It was a bit tricky setting everything up. I had a hard time with the network part. I “forgot” to change my routers wan interface to the IP of the CM4 eth1 interface.

I’m also shocked by the amount of traffic:

Total Traffic      10.9 GB [15,132,481 Pkts]

And the system isn’t even online for half a day:

 11:47:00 up 1 day, 6 min,  6 users,  load average: 1.42, 1.17, 1.18

Note that the CM4 handles the load well, currently the load average is just above 1. But for extremely high-traffic networks (gigabit+), you might want to consider adjusting the sampling rate or monitoring parameters.

 

Thanks for reading!

 

Leave a Reply

Your email address will not be published. Required fields are marked *