Suricata is a Network Security Monitoring (NSM) tool that uses sets of community created and user defined signatures (also referred to as rules) to examine and process network traffic. Suricata can generate log events, trigger alerts, and drop traffic when it detects suspicious packets or requests to any number of different services running on a server.
By default Suricata works as a passive Intrusion Detection System (IDS) to scan for suspicious traffic on a server or network. It will generate and log alerts for further investigation. It can also be configured as an active Intrusion Prevention System (IPS) to log, alert, and completely block network traffic that matches specific rules.
You can deploy Suricata on a gateway host in a network to scan all incoming and outgoing network traffic from other systems, or you can run it locally on individual machines in either mode.
In this tutorial you will learn how to install Suricata, and how to customize some of its default settings on Ubuntu 20.04 to suit your needs. You will also learn how to download existing sets of signatures (usually referred to as rulesets) that Suricata uses to scan network traffic. Finally you’ll learn how to test whether Suricata is working correctly when it detects suspicious requests and data in a response.
Depending on your network configuration and how you intend to use Suricata, you may need more or less CPU and RAM for your server. Generally, the more traffic you plan to inspect the more resources you should allocate to Suricata. In a production environment plan to use at least 2 CPUs and 4 or 8GB of RAM to start with. From there you can scale up resources according to Suricata’s performance and the amount of traffic that you need to process.
If you plan to use Suricata to protect the server that it is running on, you will need:
Otherwise, if you plan to use Suricata on a gateway host to monitor and protect multiple servers, you will need to ensure that the host’s networking is configured correctly.
<$>[info] If you are using DigitalOcean you can follow this guide on How to Configure a Droplet as a VPC Gateway. Those instructions should work for most Ubuntu servers as well. <$>
To get started installing Suricata, you will need to add the Open Information Security Foundation’s (OISF) software repository information to your Ubuntu system. You can use the add-apt-repository
command to do this.
Run the following command to add the repository to your system and update the list of available packages:
sudo add-apt-repository ppa:oisf/suricata-stable
Press ENTER
when you are prompted to confirm that you want to add the repository. The command will update the list of available packages for you after it adds the new repository.
Now you can install the suricata
package using the apt
command:
sudo apt install suricata
Now that the package is installed, enable the suricata.service
so that it will run when your system restarts. Use the systemctl
command to enable it:
sudo systemctl enable suricata.service
You should receive output like the following indicating the service is enabled:
[secondary_label Output]
suricata.service is not a native service, redirecting to systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable suricata
Before moving on to the next section of this tutorial, which explains how to configure Suricata, stop the service using systemctl
:
sudo systemctl stop suricata.service
Stopping Suricata ensures that when you edit and test the configuration file, any changes that you make will be validated and loaded when Suricata starts up again.
The Suricata package from the OISF repositories ships with a configuration file that covers a wide variety of use cases. The default mode for Suricata is IDS mode, so no traffic will be dropped, only logged. Leaving this mode set to the default is a good idea as you learn Suricata. Once you have Suricata configured and integrated into your environment, and have a good idea of the kinds of traffic that it will alert you about, you can opt to turn on IPS mode.
However, the default configuration still has a few settings that you may need to change depending on your environment and needs.
Suricata can include a Community ID field in its JSON output to make it easier to match individual event records to records in datasets generated by other tools.
If you plan to use Suricata with other tools like Zeek or Elasticsearch, adding the Community ID now is a good idea.
To enable the option, open /etc/suricata/suricata.yaml
using nano or your preferred editor:
sudo nano /etc/suricata/suricata.yaml
Find line 120 which reads # Community Flow ID
. If you are using nano
type CTRL+_
and then 120
when prompted to enter a line number. Below that line is the community-id
key. Set it to true
to enable the setting:
[label /etc/suricata/suricata.yaml]
. . .
# Community Flow ID
# Adds a 'community_id' field to EVE records. These are meant to give
# records a predictable flow ID that can be used to match records to
# output of other tools such as Zeek (Bro).
#
# Takes a 'seed' that needs to be same across sensors and tools
# to make the id less predictable.
# enable/disable the community id feature.
community-id: <^>true<^>
. . .
Now when you examine events, they will have an ID like 1:S+3BA2UmrHK0Pk+u3XH78GAFTtQ=
that you can use to correlate records across different NMS tools.
Save and close the /etc/suricata/suricata.yaml
file. If you are using nano
, you can do so with CTRL+X
, then Y
and ENTER
to confirm.
You may need to override the default network interface or interfaces that you would like Suricata to inspect traffic on. The configuration file that comes with the OISF Suricata package defaults to inspecting traffic on a device called eth0
. If your system uses a different default network interface, or if you would like to inspect traffic on more than one interface, then you will need to change this value.
To determine the device name of your default network interface, you can use the ip
command as follows:
ip -p -j route show default
The -p
flag formats the output to be more readable, and the -j
flag prints the output as JSON.
You should receive output like the following:
[secondary_label Output]
[ {
"dst": "default",
"gateway": "203.0.113.254",
<^>"dev": "eth0"<^>,
"protocol": "static",
"flags": [ ]
} ]
The dev
line indicates the default device. In this example output, the device is the highlighted eth0
interface. Your output may show a device name like ens...
or eno...
. Whatever the name is, make a note of it.
Now you can edit Suricata’s configuration and verify or change the interface name. Open the /etc/suricata/suricata.yaml
configuration file using nano
or your preferred editor:
sudo nano /etc/suricata/suricata.yaml
Scroll through the file until you come to a line that reads af-packet:
around line 580. If you are using nano
you can also go to the line directly by entering CTRL+_
and typing the line number. Below that line is the default interface that Suricata will use to inspect traffic. Edit the line to match your interface like the highlighted example that follows:
[label /etc/suriata/suricata.yaml]
# Linux high speed capture support
af-packet:
- interface: <^>eth0<^>
# Number of receive threads. "auto" uses the number of cores
#threads: auto
# Default clusterid. AF_PACKET will load balance packets based on flow.
cluster-id: 99
. . .
If you want to inspect traffic on additional interfaces, you can add more - interface: eth...
YAML objects. For example, to add a device named enp0s1
, scroll down to the bottom of the af-packet
section to around line 650. To add a new interface, insert it before the -interface: default
section like the following highlighted example:
[label /ec/suricata/suricata.yaml]
# For eBPF and XDP setup including bypass, filter and load balancing, please
# see doc/userguide/capture-hardware/ebpf-xdp.rst for more info.
<^>- interface: enp0s1<^>
<^>cluster-id: 98<^>
- interface: default
#threads: auto
#use-mmap: no
#tpacket-v3: yes
Be sure to choose a unique cluster-id
value for each - interface
object.
Keep your editor open and proceed to the next section where you will configure live rule reloading. If you do not want to enable that setting then you can save and close the /etc/suricata/suricata.yaml
file. If you are using nano
, you can do so with CTRL+X
, then Y
and ENTER
to confirm.
Suricata supports live rule reloading, which means you can add, remove, and edit rules without needing to restart the running Suricata process. To enable the live reload option, scroll to the bottom of the configuration file and add the following lines:
[label /etc/suricata/suricata.yaml]
. . .
detect-engine:
- rule-reload: true
With this setting in place, you will be able to send the SIGUSR2
system signal to the running process, and Suricata will reload any changed rules into memory.
A command like the following will notify the Suricata process to reload its rulesets, without restarting the process:
sudo kill -usr2 $(pidof suricata)
The $(pidof suricata)
portion of the command invokes a subshell, and finds the process ID of the running Suricata daemon. The beginning sudo kill -usr2
part of the command uses the kill
utility to send the SIGUSR2
signal to the process ID that is reported back by the subshell.
You can use this command any time you run suricata-update
or when you add or edit your own custom rules.
Save and close the /etc/suricata/suricata.yaml
file. If you are using nano
, you can do so with CTRL+X
, then Y
and ENTER
to confirm.
At this point in the tutorial, if you were to start Suricata, you would receive a warning message like the following in the logs that there are no loaded rules:
[secondary_label Output]
<Warning> - [ERRCODE: SC_ERR_NO_RULES(42)] - No rule files match the pattern /var/lib/suricata/rules/suricata.rules
By default the Suricata package includes a limited set of detection rules (in the /etc/suricata/rules
directory), so turning Suricata on at this point would only detect a limited amount of bad traffic.
Suricata includes a tool called suricata-update
that can fetch rulesets from external providers. Run it as follows to download an up to date ruleset for your Suricata server:
sudo suricata-update
You should receive output like the following:
[secondary_label Output]
19/10/2021 -- 19:31:03 - <Info> -- Using data-directory /var/lib/suricata.
19/10/2021 -- 19:31:03 - <Info> -- Using Suricata configuration /etc/suricata/suricata.yaml
19/10/2021 -- 19:31:03 - <Info> -- Using /etc/suricata/rules for Suricata provided rules.
. . .
19/10/2021 -- 19:31:03 - <Info> -- <^>No sources configured, will use Emerging Threats Open<^>
19/10/2021 -- 19:31:03 - <Info> -- <^>Fetching https://rules.emergingthreats.net/open/suricata-6.0.3/emerging.rules.tar.gz.<^>
100% - 3044855/3044855
. . .
19/10/2021 -- 19:31:06 - <Info> -- <^>Writing rules to /var/lib/suricata/rules/suricata.rules: total: 31011; enabled: 23649; added: 31011; removed 0; modified: 0<^>
19/10/2021 -- 19:31:07 - <Info> -- Writing /var/lib/suricata/rules/classification.config
19/10/2021 -- 19:31:07 - <Info> -- Testing with suricata -T.
19/10/2021 -- 19:31:32 - <Info> -- <^>Done.<^>
The highlighted lines indicate suricata-update
has fetched the free Emerging Threats ET Open Rules, and saved them to Suricata’s /var/lib/suricata/rules/suricata.rules
file. It also indicates the number of rules that were processed, in this example, 31011 were added and of those 23649 were enabled.
The suricata-update
tool can fetch rules from a variety of free and commercial ruleset providers. Some rulesets like the ET Open set that you already added are available for free, while others require a paid subscription.
You can list the default set of rule providers using the list-sources
flag to suricata-update
like this:
sudo suricata-update list-sources
You will receive a list of sources like the following:
[secondary_label Output]
. . .
19/10/2021 -- 19:27:34 - <Info> -- Adding all sources
19/10/2021 -- 19:27:34 - <Info> -- Saved /var/lib/suricata/update/cache/index.yaml
Name: et/open
Vendor: Proofpoint
Summary: Emerging Threats Open Ruleset
License: MIT
. . .
For example, if you wanted to include the tgreen/hunting
ruleset, you could enable it using the following command:
sudo suricata-update enable-source tgreen/hunting
Then run suricata-update
again and the new set of rules will be added, in addition to the existing ET Open rules and any others that you have downloaded.
Now that you have edited Suricata’s configuration file to include the optional Community ID, specify the default network interface, and enabled live rule reloading, it is a good idea to test the configuration.
Suricata has a built-in test mode that will check the configuration file and any included rules for validity. Validate your changes from the previous section using the -T
flag to run Suricata in test mode. The -v
flag will print some additional information, and the -c
flag tells Suricata where to find its configuration file:
sudo suricata -T -c /etc/suricata/suricata.yaml -v
The test can take some time depending on the amount of CPU you have allocated to Suricata and the number of rules that you have added, so be prepared to wait for a minute or two for it to complete.
With the default ET Open ruleset you should receive output like the following:
[secondary_label Output]
21/10/2021 -- 15:00:40 - <Info> - Running suricata under test mode
21/10/2021 -- 15:00:40 - <Notice> - This is Suricata version 6.0.3 RELEASE running in SYSTEM mode
21/10/2021 -- 15:00:40 - <Info> - CPUs/cores online: 2
21/10/2021 -- 15:00:40 - <Info> - fast output device (regular) initialized: fast.log
21/10/2021 -- 15:00:40 - <Info> - eve-log output device (regular) initialized: eve.json
21/10/2021 -- 15:00:40 - <Info> - stats output device (regular) initialized: stats.log
21/10/2021 -- 15:00:46 - <Info> - 1 rule files processed. 23879 rules successfully loaded, 0 rules failed
21/10/2021 -- 15:00:46 - <Info> - Threshold config parsed: 0 rule(s) found
21/10/2021 -- 15:00:47 - <Info> - 23882 signatures processed. 1183 are IP-only rules, 4043 are inspecting packet payload, 18453 inspect application layer, 107 are decoder event only
21/10/2021 -- 15:01:13 - <Notice> - Configuration provided was successfully loaded. Exiting.
21/10/2021 -- 15:01:13 - <Info> - cleaning up signature grouping structure... complete
If there is an error in your configuration file, then the test mode will generate a specific error code and message that you can use to help troubleshoot. For example, including a rules file that does not exist called test.rules
would generate an error like the following:
[secondary_label Output]
21/10/2021 -- 15:10:15 - <Info> - Running suricata under test mode
21/10/2021 -- 15:10:15 - <Notice> - This is Suricata version 6.0.3 RELEASE running in SYSTEM mode
21/10/2021 -- 15:10:15 - <Info> - CPUs/cores online: 2
21/10/2021 -- 15:10:15 - <Info> - eve-log output device (regular) initialized: eve.json
21/10/2021 -- 15:10:15 - <Info> - stats output device (regular) initialized: stats.log
21/10/2021 -- 15:10:21 - <Warning> - <^>[ERRCODE: SC_ERR_NO_RULES(42)] - No rule files match the pattern /var/lib/suricata/rules/test.rules<^>
With that error you could then edit your configuration file to include the correct path, or fix invalid variables and configuration options.
Once your Suricata test mode run completes successfully you can move to the next step, which is starting Suricata in daemon mode.
Now that you have a valid Suricata configuration and ruleset, you can start the Suricata server. Run the following systemctl
command:
sudo systemctl start suricata.service
You can examine the status of the service using the systemctl status
command:
sudo systemctl status suricata.service
You should receive output like the following:
[secondary_label Output]
● suricata.service - LSB: Next Generation IDS/IPS
Loaded: loaded (/etc/init.d/suricata; generated)
Active: <^>active (running)<^> since Thu 2021-10-21 18:22:56 UTC; 1min 57s ago
Docs: man:systemd-sysv-generator(8)
Process: 22636 ExecStart=/etc/init.d/suricata start (code=exited, status=0/SUCCESS)
Tasks: 8 (limit: 2344)
Memory: 359.2M
CGroup: /system.slice/suricata.service
└─22656 /usr/bin/suricata -c /etc/suricata/suricata.yaml --pidfile /var/run/suricata.pid --af-packet -D -vvv
Oct 21 18:22:56 suricata systemd[1]: Starting LSB: Next Generation IDS/IPS...
Oct 21 18:22:56 suricata suricata[22636]: Starting suricata in IDS (af-packet) mode... done.
Oct 21 18:22:56 suricata systemd[1]: Started LSB: Next Generation IDS/IPS.
As with the test mode command, it will take Suricata a minute or two to load and parse all of the rules. You can use the tail
command to watch for a specific message in Suricata’s logs that indicates it has finished starting:
sudo tail -f /var/log/suricata/suricata.log
You will receive a number of lines of output, and the terminal may appear to be stuck while Suricata loads. Continue waiting for output until you receive a line like the following:
[secondary_label Output]
19/10/2021 -- 19:22:39 - <Info> - All AFP capture threads are running.
This line indicates Suricata is running and ready to inspect traffic. You can exit the tail
command using CTRL+C
.
Now that you have verified that Suricata is running, the next step in this tutorial is to check whether Suricata detects a request to a test URL that is designed to generate an alert.
The ET Open ruleset that you downloaded contains over 30000 rules. A full explanation of how Suricata rules work, and how to construct them is beyond the scope of this introductory tutorial. A subsequent tutorial in this series will explain how rules work and how to build your own.
For the purposes of this tutorial, testing whether Suricata is detecting suspicious traffic with the configuration that you generated is sufficient. The Suricata Quickstart recommends testing the ET Open rule with number 2100498
using the curl
command.
Run the following to generate an HTTP request, which will return a response that matches Suricata’s alert rule:
curl http://testmynids.org/uid/index.html
The curl
command will output a response like the following:
[secondary_label Output]
uid=0(root) gid=0(root) groups=0(root)
This example response data is designed to trigger an alert, by pretending to return the output of a command like id
that might run on a compromised remote system via a web shell.
Now you can check Suricata’s logs for a corresponding alert. There are two logs that are enabled with the default Suricata configuration. The first is in /var/log/suricata/fast.log
and the second is a machine readable log in /var/log/suricata/eve.log
.
/var/log/suricata/fast.log
To check for a log entry in /var/log/suricata/fast.log
that corresponds to your curl
request use the grep
command. Using the 2100498
rule identifier from the Quickstart documentation, search for entries that match it using the following command:
grep 2100498 /var/log/suricata/fast.log
If your request used IPv6, then you should receive output like the following, where <^>2001:DB8::1<^>
is your system’s public IPv6 address:
[secondary_label Output]
10/21/2021-18:35:54.950106 [**] [1:<^>2100498<^>:7] GPL ATTACK_RESPONSE id check returned root [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 2600:9000:2000:4400:0018:30b3:e400:93a1:80 -> <^>2001:DB8::1<^>:34628
If your request used IPv4, then your log should have a message like this, where <^>203.0.113.1<^>
is your system’s public IPv4 address:
[secondary_label Output]
10/21/2021-18:35:57.247239 [**] [1:<^>2100498<^>:7] GPL ATTACK_RESPONSE id check returned root [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 204.246.178.81:80 -> <^>203.0.113.1<^>:36364
Note the highlighted <^>2100498<^>
value in the output, which is the Signature ID (sid
) that Suricata uses to identify a rule.
/var/log/suricata/eve.log
Suricata also logs events to /var/log/suricata/eve.log
(nicknamed the EVE log) using JSON to format entries.
The Suricata documentation recommends using the jq
utility to read and filter the entries in this file. Install jq
if you do not have it on your system using the following apt
command:
sudo apt install jq
Once you have jq
installed, you can filter the events in the EVE log by searching for the 2100498
signature with the following command:
jq 'select(.alert .signature_id==2100498)' /var/log/suricata/eve.json
The command examines each JSON entry and prints any that have an alert
object, with a signature_id
key that matches the 2100498
value that you are searching for. The output will resemble the following:
[secondary_label Output]
{
"timestamp": "2021-10-21T19:42:47.368856+0000",
"flow_id": 775889108832281,
"in_iface": "eth0",
"event_type": "alert",
"src_ip": "203.0.113.1",
"src_port": 80,
"dest_ip": "147.182.148.159",
"dest_port": 38920,
"proto": "TCP",
<^>"community_id": "1:XLNse90QNVTgyXCWN9JDovC0XF4=",<^>
"alert": {
"action": "allowed",
"gid": 1,
<^>"signature_id": 2100498,<^>
"rev": 7,
"signature": "GPL ATTACK_RESPONSE id check returned root",
"category": "Potentially Bad Traffic",
. . .
}
Note the highlighted <^>"signature_id": 2100498,<^>
line, which is the key that jq
is searching for. Also note the highlighted <^>"community_id": "1:XLNse90QNVTgyXCWN9JDovC0XF4=",<^>
line in the JSON output. This key is the generated Community Flow Identifier that you enabled in Suricata’s configuration file.
Each alert will generate a unique Community Flow Identifier. Other NMS tools can also generate the same identifier to enable cross-referencing a Suricata alert with output from other tools.
A matching log entry in either log file means that Suricata successfully inspected the network traffic, matched it against a detection rule, and generated an alert for subsequent analysis or logging. A future tutorial in this series will explore how to send Suricata alerts to a Security Information Event Management (SIEM) system for further processing.
Once you have alerts set up and tested, you can choose how you want to handle them. For some use cases, logging alerts for auditing purposes may be sufficient; or you may prefer to take a more active approach to blocking traffic from systems that generate repeated alerts.
If you would like to block traffic based on the alerts that Suricata generates, one approach is to use entries from the EVE log and then add firewall rules to restrict access to your system or systems. You can use the jq
tool to extract specific fields from an alert, and then add UFW or IPtables rules to block requests.
Again, this example is a hypothetical scenario using deliberately crafted request and response data. Your knowledge of the systems and protocols that your environment should be able to access is essential in order to determine which traffic is legitimate and which can be blocked.
In this tutorial you installed Suricata from the OISF software repositories. Installing Suricata this way ensures that you can receive updates whenever a new version of Suricata is released. After installing Suricata you edited the default configuration to add a Community Flow ID for use with other security tools. You also enabled live rule reloading, and downloaded an initial set of rules.
Once you validated Suricata’s configuration, you started the process and generated some test HTTP traffic. You verified that Suricata could detect suspicious traffic by examining both of the default logs to make sure they contained an alert corresponding to the rule you were testing.
For more information about Suricata, visit the official Suricata Site. For more details on any of the configuration options that you configured in this tutorial, refer to the Suricata User Guide.
Now that you have Suricata installed and configured, you can continue to the next tutorial in this series Understanding Suricata Signatures where you’ll explore how to write your own custom Suricata rules. You’ll learn about different ways to create alerts, or even how to drop traffic entirely, based on criteria like invalid TCP/IP packets, the contents of DNS queries, HTTP requests and responses, and even TLS handshakes.