Building a Custom Suricata RPM Package

OISF builds Suricata for Rocky Linux, AlmaLinux and RHEL in its Copr repository, and that package already has most of the high-speed capture features: eBPF, XDP and DPDK support. Two things are left out: AF_XDP capture, and the eBPF programs themselves, which the build compiles but doesn’t package. You may also need a feature that only a custom build can have, such as Napatech or PF_RING capture.

Rather than compiling Suricata from scratch, you can rebuild OISF’s own source RPM with a few changes. The result keeps everything the official package has, from its systemd service and suricata user to its log rotation and configuration handling, and RPM works out the libraries it depends on by itself.

In this post we’ll rebuild the official Suricata 8 package for Rocky Linux 9 and 10, with AF_XDP support and the eBPF programs included. For Debian and Ubuntu, see Building a Custom Suricata DEB Package.

The build setup

We used:

  • Rocky Linux 9 and Rocky Linux 10, x86_64
  • Suricata 8.0.7, from OISF’s @oisf/suricata-8.0 Copr repository

Two things to know before you start:

  • Build on the same release as your sensors. A package built on Rocky 9 is for EL9 hosts, and one built on Rocky 10 for EL10 hosts. AlmaLinux uses the same commands. RHEL and Oracle Linux name the CRB and EPEL repositories differently, so enable those two as EPEL’s guide explains.
  • Build as a normal user, on a build machine. rpmbuild works in your home directory and installs nothing, but its build dependencies come to about 130 packages, so use a VM or a container rather than a sensor. Don’t build as root.

Rebuilding the Suricata RPM

An RPM package is built from a source RPM: the program’s source code plus a spec file, the recipe that configures, compiles and packages it. We’ll take OISF’s source RPM, change four lines of its spec, and build it.

Preparing the build machine

Install the RPM build tools, and enable the repositories the build needs: CRB (CodeReady Builder) and EPEL for libraries such as libxdp-devel and hyperscan-devel, and OISF’s Copr for Suricata’s source RPM. rpmdev-setuptree then creates the ~/rpmbuild directories:

sudo dnf install -y dnf-plugins-core epel-release rpm-build rpmdevtools
sudo dnf config-manager --set-enabled crb
sudo dnf copr enable -y @oisf/suricata-8.0
rpmdev-setuptree

Getting the source RPM

Download the source RPM and unpack it into ~/rpmbuild, which puts Suricata’s source code in SOURCES and the spec, suricata.spec, in SPECS:

dnf download --source suricata
rpm -i suricata-8.0.7-*.src.rpm
cd ~/rpmbuild/SPECS

rpm prints a few warnings, which are expected: NOKEY, because dnf imports the Copr’s signing key only when it installs a package from it, and that the mockbuild user or mock group doesn’t exist, which belong to the system OISF builds its packages on.

Changing the spec

Keep a copy of the original, make the four changes, and compare:

cp suricata.spec suricata.spec.orig
sed -i 's/^Release: .*/&.custom1/' suricata.spec
sed -i 's/^BuildRequires: clang llvm libbpf-devel$/& libxdp-devel/' suricata.spec
sed -i '/^%check/i mkdir -p %{buildroot}%{_libexecdir}/suricata/ebpf\ninstall -m 644 ebpf/*.bpf %{buildroot}%{_libexecdir}/suricata/ebpf/\n' suricata.spec
sed -i 's|^%{_datadir}/%{name}/rules$|&\n%{_libexecdir}/suricata/|' suricata.spec
diff -u suricata.spec.orig suricata.spec

What each change does:

  • The release gets .custom1, so your package reads 8.0.7-1.el9.custom1. You can tell it apart from OISF’s, and dnf ranks it above OISF’s 8.0.7-1.el9.
  • libxdp-devel is added to the build dependencies: Suricata turns AF_XDP on when it finds libxdp.
  • Two install lines copy the eBPF programs to /usr/libexec/suricata/ebpf/, where Suricata’s eBPF guide puts them. The spec already compiles them (--enable-ebpf-build), but Suricata’s make install has no rule for them.
  • The file list gets that directory, so the programs go into the package.

The diff should show exactly these changes:

-Release: 1%{?dist}
+Release: 1%{?dist}.custom1
...
-BuildRequires: clang llvm libbpf-devel
+BuildRequires: clang llvm libbpf-devel libxdp-devel
...
+mkdir -p %{buildroot}%{_libexecdir}/suricata/ebpf
+install -m 644 ebpf/*.bpf %{buildroot}%{_libexecdir}/suricata/ebpf/
...
+%{_libexecdir}/suricata/

Building the package

Install the build dependencies the spec lists, then build:

sudo dnf builddep -y suricata.spec
rpmbuild -ba suricata.spec

rpmbuild configures and compiles Suricata, runs its tests and writes the packages, in about ten minutes on a 16-core machine. Along the way, configure’s summary should show AF_XDP, eBPF and XDP support enabled:

Suricata Configuration:
  AF_PACKET support:                       yes
  AF_XDP support:                          yes
  DPDK support:                            yes
  eBPF support:                            yes
  XDP support:                             yes

The build ends by listing the packages it wrote. Yours is ~/rpmbuild/RPMS/x86_64/suricata-8.0.7-1.el9.custom1.x86_64.rpm (el10 on Rocky 10). The suricata-debuginfo and suricata-debugsource packages next to it are only for debugging crashes. RPM works out the package’s dependencies from the libraries Suricata links against, so libxdp is now one of them without you listing it.

Installing and checking the package

Copy the package to a sensor running the same release. Suricata needs two libraries from EPEL, hiredis and Hyperscan (Vectorscan on EL10), so enable EPEL there, then install the package:

sudo dnf install -y epel-release
sudo dnf install -y ./suricata-8.0.7-*.custom1.x86_64.rpm

Then check that AF_XDP, eBPF and XDP support are enabled, and that the eBPF programs are installed:

$ suricata --build-info
This is Suricata version 8.0.7 RELEASE
...
Suricata Configuration:
  AF_PACKET support:                       yes
  AF_XDP support:                          yes
  DPDK support:                            yes
  eBPF support:                            yes
  XDP support:                             yes
...
$ ls /usr/libexec/suricata/ebpf/
bypass_filter.bpf  filter.bpf  lb.bpf  vlan_filter.bpf  xdp_filter.bpf  xdp_lb.bpf

To use an eBPF program, point the af-packet section of suricata.yaml at it, for example ebpf-lb-file: /usr/libexec/suricata/ebpf/lb.bpf with cluster-type: cluster_ebpf. Suricata’s eBPF and XDP guide covers each program.

AF_XDP is a capture method of its own, next to AF_PACKET: start Suricata with --af-xdp=<interface>, or set it up in the af-xdp section of suricata.yaml, as Suricata’s AF_XDP guide explains. Suricata starts one AF_XDP capture thread per receive queue that the network card reports, up to the number of CPU cores, so it needs a card whose driver reports its queues. A container’s virtual interface reports none: Suricata logs af-xdp: 0 queues, so using 0 threads and captures nothing.

Keeping your build

If a sensor also has OISF’s Copr enabled, dnf will treat OISF’s next release, such as 8.0.8, as an upgrade of yours, and replace your build with one without AF_XDP. To keep yours, exclude Suricata from that repository:

sudo dnf config-manager --save --setopt='copr:copr.fedorainfracloud.org:group_oisf:suricata-8.0.excludepkgs=suricata'

When a new Suricata version comes out, rebuild it the same way from OISF’s new source RPM.

Building with other features

The same steps add other features that the official package leaves out, such as Napatech and PF_RING support: add the option to the %configure line of suricata.spec (--enable-napatech or --enable-pfring), and install the vendor’s driver and development files on the build machine first. Your sensors will need the vendor’s driver as well.

How IDSTower can help

IDSTower deploys your package to every host of a cluster with its User-Custom Packages Repository:

  • Copy the package and its dependencies once. Put them on the IDSTower server, in a folder named after the Suricata version and the hosts’ OS release and architecture: /opt/idstower/custom/Suricata/packages/8.0.7/Rocky9/x86_64/ (Rocky10 for Rocky 10). Copy only suricata-8.0.7-1.el9.custom1.x86_64.rpm: the folder must hold just one file whose name starts with suricata, so leave the debuginfo and debugsource packages out. Version 8.0.7 then shows up in the cluster setup wizard.
  • IDSTower installs them on every host, including hosts without internet access, and checks that each host reports the version you picked. It then runs Suricata under its own configuration and services, alongside health monitoring and rule life-cycle management, from one web interface.

To collect the dependency packages, run this on a host set up like your sensors, with the same release, EPEL enabled and no Suricata, then copy the files from suricata-deps/ into the folder, next to your package:

sudo dnf install -y --downloadonly --downloaddir=suricata-deps ./suricata-8.0.7-*.custom1.x86_64.rpm

Try it in the live demo (no signup), or get a free license.