Networking & Diagnostics8 min read

    Install and Configure Snort 3 on Ubuntu

    By Amir— Reliability & Network Engineering
    Share

    Snort is an open-source network intrusion detection and prevention system (IDS/IPS). It reads packets off an interface, decodes them, and matches them against rules that describe known-bad traffic — exploit attempts, scanning, malware callbacks, policy violations — and raises an alert (or, inline, drops the packet). It has been the reference open-source IDS since 1998; Snort 3 (2021) is a rewrite with a Lua configuration, multithreading and a cleaner rule language.

    This guide installs Snort 3 on Ubuntu 22.04 and 24.04 from source, because the snort package in Ubuntu's repositories is the legacy 2.9 branch with a different configuration format and rule set. It then does the minimum real configuration: your network definition, the free community rules, a local rule you can trigger on purpose, running on a live interface, a systemd unit, and the NIC setting that otherwise makes Snort miss traffic.

    Snort's three modes

    • Sniffer — print decoded packets to the console (snort -i eth0). A slower tcpdump; mostly for checking Snort sees the interface.
    • Packet logger — write packets to disk (-l /var/log/snort).
    • IDS/IPS — apply rules; alert (IDS, passive) or drop (IPS, inline with two interfaces or NFQUEUE). This guide sets up IDS mode; IPS is a configuration change on top once rules are tuned.

    1. Build dependencies

    sudo apt update && sudo apt upgrade -y
    sudo apt install -y build-essential cmake pkg-config git wget curl \
      libpcap-dev libpcre3-dev libpcre2-dev libdumbnet-dev zlib1g-dev libluajit-5.1-dev \
      libhwloc-dev libssl-dev libunwind-dev libfl-dev flex bison libtool \
      autoconf automake libmnl-dev libtirpc-dev uuid-dev libnghttp2-dev \
      libgoogle-perftools-dev libhyperscan-dev
    

    libhyperscan-dev (Intel Hyperscan; on Ubuntu 24.04 the drop-in fork is libvectorscan-dev) and libgoogle-perftools-dev (tcmalloc) are optional but make rule matching and memory behaviour noticeably better; the configure step picks them up automatically. Older Snort 3 releases link against libpcre3, releases from 3.3 onward against libpcre2 — installing both covers either.

    2. Build libdaq

    Snort 3 captures packets through the Data Acquisition library, which must be built first:

    mkdir -p ~/snort-src && cd ~/snort-src
    git clone https://github.com/snort3/libdaq.git
    cd libdaq
    ./bootstrap
    ./configure
    make -j"$(nproc)"
    sudo make install
    sudo ldconfig
    

    3. Build Snort 3

    cd ~/snort-src
    git clone https://github.com/snort3/snort3.git
    cd snort3
    ./configure_cmake.sh --prefix=/usr/local --enable-tcmalloc
    cd build
    make -j"$(nproc)"
    sudo make install
    sudo ldconfig
    snort -V
    

    Expect a banner ending in Version 3.x.y.z. If you prefer a release tarball over the git head, download it from the snort3 GitHub releases page and run the same configure_cmake.sh from the extracted directory.

    4. Stop the NIC from hiding traffic

    Modern NICs reassemble packets in hardware (GRO/LRO). Snort then sees a few giant packets instead of the real stream and misses rule matches. Turn offloading off on the sniffing interface and make it persistent:

    IFACE=eth0   # your capture interface: ip -br link
    sudo ethtool -K "$IFACE" gro off lro off
    
    sudo tee /etc/systemd/system/snort-nic.service >/dev/null <<EOF2
    [Unit]
    Description=Disable NIC offloading for Snort on $IFACE
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=oneshot
    ExecStart=/usr/sbin/ip link set dev $IFACE promisc on
    ExecStart=/usr/sbin/ethtool -K $IFACE gro off lro off
    RemainAfterExit=yes
    
    [Install]
    WantedBy=multi-user.target
    EOF2
    sudo systemctl daemon-reload
    sudo systemctl enable --now snort-nic.service
    

    In a VM, do the same for the virtual NIC; on a SPAN/mirror port, put the interface in promiscuous mode as above and give it no IP address.

    5. Configure snort.lua

    The main configuration is /usr/local/etc/snort/snort.lua. Three edits:

    sudo nano /usr/local/etc/snort/snort.lua
    
    1. HOME_NET — the networks you are protecting. Alerts and many rules are directional, so this matters:

      HOME_NET = '192.168.1.0/24'          -- or '[10.0.0.0/8,192.168.0.0/16]'
      EXTERNAL_NET = '!$HOME_NET'
      
    2. Rule files — find the ips = block and point it at the rules (created in the next step):

      ips =
      {
          enable_builtin_rules = true,
          include = RULE_PATH .. '/local.rules',
          variables = default_variables
      }
      

      Snort 3 reads one include here; put further files inside local.rules with include snort3-community.rules lines, or pass extra files on the command line with -R.

    3. Alert output — leave the defaults; we select the output format on the command line / in the service (-A alert_fast).

    Check the file parses:

    sudo snort -c /usr/local/etc/snort/snort.lua -T
    # ...
    # Snort successfully validated the configuration (with 0 warnings).
    

    6. Rules: community set plus a local test rule

    sudo mkdir -p /usr/local/etc/rules /var/log/snort
    cd /tmp
    wget https://www.snort.org/downloads/community/snort3-community-rules.tar.gz
    tar xzf snort3-community-rules.tar.gz
    sudo cp snort3-community-rules/snort3-community.rules /usr/local/etc/rules/
    sudo cp snort3-community-rules/sid-msg.map /usr/local/etc/rules/ 2>/dev/null || true
    

    Now a rule you can trigger yourself:

    sudo tee /usr/local/etc/rules/local.rules >/dev/null <<'RULES'
    include snort3-community.rules
    
    alert icmp any any -> $HOME_NET any ( msg:"LOCAL ICMP echo to HOME_NET"; itype:8; sid:1000001; rev:1; )
    alert tcp any any -> $HOME_NET 22 ( msg:"LOCAL SSH connection attempt"; flow:to_server,established; sid:1000002; rev:1; )
    RULES
    

    Local rule SIDs must be ≥ 1,000,000 to stay out of the reserved ranges. Re-validate with -T (it now loads the rules and reports how many).

    Set RULE_PATH in snort.lua if it is not already /usr/local/etc/rules:

    RULE_PATH = '/usr/local/etc/rules'
    

    7. Run Snort in IDS mode

    sudo snort -c /usr/local/etc/snort/snort.lua -i eth0 -A alert_fast -s 65535 -k none
    
    • -A alert_fast — one line per alert to stdout; -A alert_json for machine-readable output; -l /var/log/snort to write files instead.
    • -s 65535 — full snap length, so large packets are not truncated before matching.
    • -k none — ignore checksum errors; needed when offloading cannot be disabled (checksums are computed by the NIC after Snort sees the packet) or you get zero alerts.

    From another machine, ping the Snort host and try an SSH connection. You should see:

    10/18-10:42:07.113201 [**] [1:1000001:1] "LOCAL ICMP echo to HOME_NET" [**] [Priority: 0] {ICMP} 203.0.113.5 -> 192.168.1.10
    10/18-10:42:19.880412 [**] [1:1000002:1] "LOCAL SSH connection attempt" [**] [Priority: 0] {TCP} 203.0.113.5:51234 -> 192.168.1.10:22
    

    Press Ctrl+C; Snort prints packet and rule statistics on exit.

    8. Run it as a service

    sudo tee /etc/systemd/system/snort3.service >/dev/null <<'EOF2'
    [Unit]
    Description=Snort 3 NIDS
    After=network-online.target snort-nic.service
    Wants=network-online.target
    
    [Service]
    Type=simple
    ExecStart=/usr/local/bin/snort -c /usr/local/etc/snort/snort.lua -i eth0 -A alert_fast -s 65535 -k none -l /var/log/snort -D -u snort -g snort --create-pidfile
    ExecReload=/bin/kill -SIGHUP $MAINPID
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    EOF2
    sudo useradd -r -s /usr/sbin/nologin -M snort 2>/dev/null || true
    sudo chown -R snort:snort /var/log/snort
    sudo systemctl daemon-reload
    sudo systemctl enable --now snort3
    sudo systemctl status snort3 --no-pager
    tail -f /var/log/snort/alert_fast.txt
    

    Remove -D if you prefer systemd to manage the foreground process (Type=simple works either way on current Snort builds; if the unit flaps, drop -D). Rotate /var/log/snort/alert_fast.txt with a logrotate stanza and copytruncate.

    9. Keeping rules current

    The community rules are a free, delayed subset. For production use register at snort.org for the Registered (free, 30-day delayed) or Subscriber rule sets and automate updates with PulledPork 3:

    cd ~/snort-src
    git clone https://github.com/shirkdog/pulledpork3.git
    

    Configure pulledpork.conf with your oinkcode and snort_path, run it from a daily timer, and systemctl reload snort3 afterwards. OpenAppID (application detection — "this is Dropbox traffic") is an optional add-on; download the detector package from snort.org and point appid = { app_detector_dir = '/usr/local/lib' } at it in snort.lua.

    Troubleshooting

    • snort: error while loading shared libraries: libdaq.so.3: cannot open shared object file — sudo ldconfig after installing libdaq, or add /usr/local/lib to /etc/ld.so.conf.d/local.conf and run ldconfig.
    • ERROR: can't find local.rules — RULE_PATH in snort.lua does not match where you put the files, or the include line in ips is missing. Paths in include lines inside rule files are relative to that file.
    • No alerts at all, even for the ICMP test rule — checksum offloading (-k none), wrong interface (-i), or HOME_NET does not contain the destination address. Run once with -A alert_fast and --warn-all to see what Snort thinks it is doing; the exit statistics show whether any packets were received.
    • Snort successfully validated but the service exits immediately — the snort user cannot read the config/rules or write /var/log/snort; journalctl -u snort3 -n 30 shows the permission error. Also check the -i interface exists at boot (predictable names).
    • Very high CPU — too many rules for the traffic volume, or Hyperscan not compiled in (snort --help-config search_engine lists available engines). Start with the community set, disable categories you do not need, then add.
    • WARNING: No preprocessors configured for policy 0 — harmless on 3.x; it refers to legacy 2.9 terminology.
    • Ubuntu's apt install snort asks for HOME_NET and installs 2.9.x — that is the legacy branch; its snort.conf and rules are not compatible with anything in this guide. Remove it (sudo apt purge snort snort-common snort-rules-default) if you installed it by accident.

    Summary

    1. Build libdaq, then Snort 3, from source on Ubuntu (configure_cmake.sh, make, make install, ldconfig).
    2. Disable GRO/LRO and enable promiscuous mode on the capture NIC, persistently.
    3. Set HOME_NET, RULE_PATH and the ips.include in snort.lua; validate with -T.
    4. Install the community rules and a local test rule (SID ≥ 1,000,000).
    5. Run with -A alert_fast -s 65535 -k none, confirm the test rule fires, then install the systemd unit.
    6. Automate rule updates with PulledPork 3 and move to the registered rule set.