nixos/suricata: init module

Changed files
+996
nixos
doc
manual
release-notes
modules
services
networking
tests
+2
nixos/doc/manual/release-notes/rl-2411.section.md
···
- [Gotenberg](https://gotenberg.dev), an API server for converting files to PDFs that can be used alongside Paperless-ngx. Available as [services.gotenberg](options.html#opt-services.gotenberg).
+
- [Suricata](https://suricata.io/), a free and open source, mature, fast and robust network threat detection engine. Available as [services.suricata](options.html#opt-services.suricata).
+
- [Playerctld](https://github.com/altdesktop/playerctl), a daemon to track media player activity. Available as [services.playerctld](option.html#opt-services.playerctld).
- [MenhirLib](https://gitlab.inria.fr/fpottier/menhir/-/tree/master/coq-menhirlib) A support library for verified Coq parsers produced by Menhir.
+282
nixos/modules/services/networking/suricata/default.nix
···
+
{
+
config,
+
pkgs,
+
lib,
+
...
+
}:
+
let
+
cfg = config.services.suricata;
+
pkg = cfg.package;
+
yaml = pkgs.formats.yaml { };
+
inherit (lib)
+
mkEnableOption
+
mkPackageOption
+
mkOption
+
types
+
literalExpression
+
filterAttrsRecursive
+
concatStringsSep
+
strings
+
lists
+
mkIf
+
;
+
in
+
{
+
meta.maintainers = with lib.maintainers; [ felbinger ];
+
+
options.services.suricata = {
+
enable = mkEnableOption "Suricata";
+
+
package = mkPackageOption pkgs "suricata" { };
+
+
configFile = mkOption {
+
type = types.path;
+
visible = false;
+
default = pkgs.writeTextFile {
+
name = "suricata.yaml";
+
text = ''
+
%YAML 1.1
+
---
+
${builtins.readFile (
+
yaml.generate "suricata-settings-raw.yaml" (
+
filterAttrsRecursive (name: value: value != null) cfg.settings
+
)
+
)}
+
'';
+
};
+
description = ''
+
Configuration file for suricata.
+
+
It is not usual to override the default values; it is recommended to use `settings`.
+
If you want to include extra configuration to the file, use the `settings.includes`.
+
'';
+
};
+
+
settings = mkOption {
+
type = types.submodule (import ./settings.nix { inherit config lib yaml; });
+
example = literalExpression ''
+
vars.address-groups.HOME_NET = "192.168.178.0/24";
+
outputs = [
+
{
+
fast = {
+
enabled = true;
+
filename = "fast.log";
+
append = "yes";
+
};
+
}
+
{
+
eve-log = {
+
enabled = true;
+
filetype = "regular";
+
filename = "eve.json";
+
community-id = true;
+
types = [
+
{
+
alert.tagged-packets = "yes";
+
}
+
];
+
};
+
}
+
];
+
af-packet = [
+
{
+
interface = "eth0";
+
cluster-id = "99";
+
cluster-type = "cluster_flow";
+
defrag = "yes";
+
}
+
{
+
interface = "default";
+
}
+
];
+
af-xdp = [
+
{
+
interface = "eth1";
+
}
+
];
+
dpdk.interfaces = [
+
{
+
interface = "eth2";
+
}
+
];
+
pcap = [
+
{
+
interface = "eth3";
+
}
+
];
+
app-layer.protocols = {
+
telnet.enabled = "yes";
+
dnp3.enabled = "yes";
+
modbus.enabled = "yes";
+
};
+
'';
+
description = "Suricata settings";
+
};
+
+
enabledSources = mkOption {
+
type = types.listOf types.str;
+
# see: nix-shell -p suricata python3Packages.pyyaml --command 'suricata-update list-sources'
+
default = [
+
"et/open"
+
"etnetera/aggressive"
+
"stamus/lateral"
+
"oisf/trafficid"
+
"tgreen/hunting"
+
"sslbl/ja3-fingerprints"
+
"sslbl/ssl-fp-blacklist"
+
"malsilo/win-malware"
+
"pawpatrules"
+
];
+
description = ''
+
List of sources that should be enabled.
+
Currently sources which require a secret-code are not supported.
+
'';
+
};
+
+
disabledRules = mkOption {
+
type = types.listOf types.str;
+
# protocol dnp3 seams to be disabled, which causes the signature evaluation to fail, so we disable the
+
# dnp3 rules, see https://github.com/OISF/suricata/blob/master/rules/dnp3-events.rules for more details
+
default = [
+
"2270000"
+
"2270001"
+
"2270002"
+
"2270003"
+
"2270004"
+
];
+
description = ''
+
List of rules that should be disabled.
+
'';
+
};
+
};
+
+
config =
+
let
+
captureInterfaces =
+
let
+
inherit (lists) unique optionals;
+
in
+
unique (
+
map (e: e.interface) (
+
(optionals (cfg.settings.af-packet != null) cfg.settings.af-packet)
+
++ (optionals (cfg.settings.af-xdp != null) cfg.settings.af-xdp)
+
++ (optionals (
+
cfg.settings.dpdk != null && cfg.settings.dpdk.interfaces != null
+
) cfg.settings.dpdk.interfaces)
+
++ (optionals (cfg.settings.pcap != null) cfg.settings.pcap)
+
)
+
);
+
in
+
mkIf cfg.enable {
+
assertions = [
+
{
+
assertion = (builtins.length captureInterfaces) > 0;
+
message = ''
+
At least one capture interface must be configured:
+
- `services.suricata.settings.af-packet`
+
- `services.suricata.settings.af-xdp`
+
- `services.suricata.settings.dpdk.interfaces`
+
- `services.suricata.settings.pcap`
+
'';
+
}
+
];
+
+
boot.kernelModules = mkIf (cfg.settings.af-packet != null) [ "af_packet" ];
+
+
users = {
+
groups.${cfg.settings.run-as.group} = { };
+
users.${cfg.settings.run-as.user} = {
+
group = cfg.settings.run-as.group;
+
isSystemUser = true;
+
};
+
};
+
+
systemd.tmpfiles.rules = [
+
"d ${cfg.settings."default-log-dir"} 755 ${cfg.settings.run-as.user} ${cfg.settings.run-as.group}"
+
"d /var/lib/suricata 755 ${cfg.settings.run-as.user} ${cfg.settings.run-as.group}"
+
"d ${cfg.settings."default-rule-path"} 755 ${cfg.settings.run-as.user} ${cfg.settings.run-as.group}"
+
];
+
+
systemd.services = {
+
suricata-update = {
+
description = "Update Suricata Rules";
+
wantedBy = [ "multi-user.target" ];
+
wants = [ "network-online.target" ];
+
after = [ "network-online.target" ];
+
+
script =
+
let
+
python = pkgs.python3.withPackages (ps: with ps; [ pyyaml ]);
+
enabledSourcesCmds = map (
+
src: "${python.interpreter} ${pkg}/bin/suricata-update enable-source ${src}"
+
) cfg.enabledSources;
+
in
+
''
+
${concatStringsSep "\n" enabledSourcesCmds}
+
${python.interpreter} ${pkg}/bin/suricata-update update-sources
+
${python.interpreter} ${pkg}/bin/suricata-update update --suricata-conf ${cfg.configFile} --no-test \
+
--disable-conf ${pkgs.writeText "suricata-disable-conf" "${concatStringsSep "\n" cfg.disabledRules}"}
+
'';
+
serviceConfig = {
+
Type = "oneshot";
+
+
PrivateTmp = true;
+
PrivateDevices = true;
+
PrivateIPC = true;
+
+
DynamicUser = true;
+
User = cfg.settings.run-as.user;
+
Group = cfg.settings.run-as.group;
+
+
ReadOnlyPaths = cfg.configFile;
+
ReadWritePaths = [
+
"/var/lib/suricata"
+
cfg.settings."default-rule-path"
+
];
+
};
+
};
+
suricata = {
+
description = "Suricata";
+
wantedBy = [ "multi-user.target" ];
+
after = [ "suricata-update.service" ];
+
serviceConfig =
+
let
+
interfaceOptions = strings.concatMapStrings (interface: " -i ${interface}") captureInterfaces;
+
in
+
{
+
ExecStartPre = "!${pkg}/bin/suricata -c ${cfg.configFile} -T";
+
ExecStart = "!${pkg}/bin/suricata -c ${cfg.configFile}${interfaceOptions}";
+
Restart = "on-failure";
+
+
User = cfg.settings.run-as.user;
+
Group = cfg.settings.run-as.group;
+
+
NoNewPrivileges = true;
+
PrivateTmp = true;
+
PrivateDevices = true;
+
PrivateIPC = true;
+
ProtectSystem = "strict";
+
DevicePolicy = "closed";
+
LockPersonality = true;
+
MemoryDenyWriteExecute = true;
+
ProtectHostname = true;
+
ProtectProc = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProtectControlGroups = true;
+
ProcSubset = "pid";
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
SystemCallArchitectures = "native";
+
RemoveIPC = true;
+
+
ReadOnlyPaths = cfg.configFile;
+
ReadWritePaths = cfg.settings."default-log-dir";
+
RuntimeDirectory = "suricata";
+
};
+
};
+
};
+
};
+
}
+625
nixos/modules/services/networking/suricata/settings.nix
···
+
{
+
lib,
+
config,
+
yaml,
+
...
+
}:
+
let
+
cfg = config.services.suricata;
+
inherit (lib)
+
mkEnableOption
+
mkOption
+
types
+
literalExpression
+
;
+
mkDisableOption =
+
name:
+
mkEnableOption name
+
// {
+
default = true;
+
example = false;
+
};
+
in
+
{
+
freeformType = yaml.type;
+
options = {
+
vars = mkOption {
+
type = types.nullOr (
+
types.submodule {
+
options = {
+
address-groups = mkOption {
+
type = (
+
types.submodule {
+
options = {
+
HOME_NET = mkOption { default = "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"; };
+
EXTERNAL_NET = mkOption { default = "!$HOME_NET"; };
+
HTTP_SERVERS = mkOption { default = "$HOME_NET"; };
+
SMTP_SERVERS = mkOption { default = "$HOME_NET"; };
+
SQL_SERVERS = mkOption { default = "$HOME_NET"; };
+
DNS_SERVERS = mkOption { default = "$HOME_NET"; };
+
TELNET_SERVERS = mkOption { default = "$HOME_NET"; };
+
AIM_SERVERS = mkOption { default = "$EXTERNAL_NET"; };
+
DC_SERVERS = mkOption { default = "$HOME_NET"; };
+
DNP3_SERVER = mkOption { default = "$HOME_NET"; };
+
DNP3_CLIENT = mkOption { default = "$HOME_NET"; };
+
MODBUS_CLIENT = mkOption { default = "$HOME_NET"; };
+
MODBUS_SERVER = mkOption { default = "$HOME_NET"; };
+
ENIP_CLIENT = mkOption { default = "$HOME_NET"; };
+
ENIP_SERVER = mkOption { default = "$HOME_NET"; };
+
};
+
}
+
);
+
default = { };
+
example = {
+
HOME_NET = "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]";
+
EXTERNAL_NET = "!$HOME_NET";
+
HTTP_SERVERS = "$HOME_NET";
+
SMTP_SERVERS = "$HOME_NET";
+
SQL_SERVERS = "$HOME_NET";
+
DNS_SERVERS = "$HOME_NET";
+
TELNET_SERVERS = "$HOME_NET";
+
AIM_SERVERS = "$EXTERNAL_NET";
+
DC_SERVERS = "$HOME_NET";
+
DNP3_SERVER = "$HOME_NET";
+
DNP3_CLIENT = "$HOME_NET";
+
MODBUS_CLIENT = "$HOME_NET";
+
MODBUS_SERVER = "$HOME_NET";
+
ENIP_CLIENT = "$HOME_NET";
+
ENIP_SERVER = "$HOME_NET";
+
};
+
description = ''
+
The address group variables for suricata, if not defined the
+
default value of suricata (see example) will be used.
+
Your settings will extend the predefined values in example.
+
'';
+
};
+
+
port-groups = mkOption {
+
type = with types; nullOr (attrsOf str);
+
default = {
+
HTTP_PORTS = "80";
+
SHELLCODE_PORTS = "!80";
+
ORACLE_PORTS = "1521";
+
SSH_PORTS = "22";
+
DNP3_PORTS = "20000";
+
MODBUS_PORTS = "502";
+
FILE_DATA_PORTS = "[$HTTP_PORTS,110,143]";
+
FTP_PORTS = "21";
+
GENEVE_PORTS = "6081";
+
VXLAN_PORTS = "4789";
+
TEREDO_PORTS = "3544";
+
};
+
description = ''
+
The port group variables for suricata.
+
'';
+
};
+
};
+
}
+
);
+
default = { }; # add default values to config
+
};
+
+
stats = mkOption {
+
type =
+
with types;
+
nullOr (submodule {
+
options = {
+
enable = mkEnableOption "suricata global stats";
+
+
interval = mkOption {
+
type = types.str;
+
default = "8";
+
description = ''
+
The interval field (in seconds) controls the interval at
+
which stats are updated in the log.
+
'';
+
};
+
+
decoder-events = mkOption {
+
type = types.bool;
+
default = true;
+
description = ''
+
Add decode events to stats
+
'';
+
};
+
+
decoder-events-prefix = mkOption {
+
type = types.str;
+
default = "decoder.event";
+
description = ''
+
Decoder event prefix in stats. Has been 'decoder' before, but that leads
+
to missing events in the eve.stats records.
+
'';
+
};
+
+
stream-events = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
Add stream events as stats.
+
'';
+
};
+
};
+
});
+
default = null; # do not add to config unless specified
+
};
+
+
plugins = mkOption {
+
type = with types; nullOr (listOf path);
+
default = null;
+
description = ''
+
Plugins -- Experimental -- specify the filename for each plugin shared object
+
'';
+
};
+
+
outputs = mkOption {
+
type =
+
with types;
+
nullOr (
+
listOf (
+
attrsOf (submodule {
+
freeformType = yaml.type;
+
options = {
+
enabled = mkEnableOption "<NAME>";
+
};
+
})
+
)
+
);
+
default = null;
+
example = literalExpression ''
+
[
+
{
+
fast = {
+
enabled = "yes";
+
filename = "fast.log";
+
append = "yes";
+
};
+
}
+
{
+
eve-log = {
+
enabled = "yes";
+
filetype = "regular";
+
filename = "eve.json";
+
community-id = true;
+
types = [
+
{
+
alert.tagged-packets = "yes";
+
}
+
];
+
};
+
}
+
];
+
'';
+
description = ''
+
Configure the type of alert (and other) logging you would like.
+
+
Valid values for <NAME> are e. g. `fast`, `eve-log`, `syslog`, `file-store`, ...
+
- `fast`: a line based alerts log similar to Snort's fast.log
+
- `eve-log`: Extensible Event Format (nicknamed EVE) event log in JSON format
+
+
For more details regarding the configuration, checkout the shipped suricata.yaml
+
```shell
+
nix-shell -p suricata yq coreutils-full --command 'yq < $(dirname $(which suricata))/../etc/suricata/suricata.yaml'
+
```
+
and the [suricata documentation](https://docs.suricata.io/en/latest/output/index.html).
+
'';
+
};
+
+
"default-log-dir" = mkOption {
+
type = types.str;
+
default = "/var/log/suricata";
+
description = ''
+
The default logging directory. Any log or output file will be placed here if it's
+
not specified with a full path name. This can be overridden with the -l command
+
line parameter.
+
'';
+
};
+
+
logging = {
+
"default-log-level" = mkOption {
+
type = types.enum [
+
"error"
+
"warning"
+
"notice"
+
"info"
+
"perf"
+
"config"
+
"debug"
+
];
+
default = "notice";
+
description = ''
+
The default log level: can be overridden in an output section.
+
Note that debug level logging will only be emitted if Suricata was
+
compiled with the --enable-debug configure option.
+
'';
+
};
+
+
"default-log-format" = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
The default output format. Optional parameter, should default to
+
something reasonable if not provided. Can be overridden in an
+
output section. You can leave this out to get the default.
+
'';
+
};
+
+
"default-output-filter" = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
A regex to filter output. Can be overridden in an output section.
+
Defaults to empty (no filter).
+
'';
+
};
+
+
"stacktrace-on-signal" = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
Requires libunwind to be available when Suricata is configured and built.
+
If a signal unexpectedly terminates Suricata, displays a brief diagnostic
+
message with the offending stacktrace if enabled.
+
'';
+
};
+
+
outputs = {
+
console = {
+
enable = mkDisableOption "logging to console";
+
};
+
file = {
+
enable = mkDisableOption "logging to file";
+
+
level = mkOption {
+
type = types.enum [
+
"error"
+
"warning"
+
"notice"
+
"info"
+
"perf"
+
"config"
+
"debug"
+
];
+
default = "info";
+
description = ''
+
Loglevel for logs written to the logfile
+
'';
+
};
+
+
filename = mkOption {
+
type = types.str;
+
default = "suricata.log";
+
description = ''
+
Filename of the logfile
+
'';
+
};
+
+
format = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
Logformat for logs written to the logfile
+
'';
+
};
+
+
type = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
Type of logfile
+
'';
+
};
+
};
+
syslog = {
+
enable = mkEnableOption "logging to syslog";
+
+
facility = mkOption {
+
type = types.str;
+
default = "local5";
+
description = ''
+
Facility to log to
+
'';
+
};
+
+
format = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
Logformat for logs send to syslog
+
'';
+
};
+
+
type = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = ''
+
Type of logs send to syslog
+
'';
+
};
+
};
+
};
+
};
+
+
"af-packet" = mkOption {
+
type =
+
with types;
+
nullOr (
+
listOf (submodule {
+
freeformType = yaml.type;
+
options = {
+
interface = mkOption {
+
type = types.str;
+
default = null;
+
};
+
};
+
})
+
);
+
default = null;
+
description = ''
+
Linux high speed capture support
+
'';
+
};
+
+
"af-xdp" = mkOption {
+
type =
+
with types;
+
nullOr (
+
listOf (submodule {
+
freeformType = yaml.type;
+
options = {
+
interface = mkOption {
+
type = types.str;
+
default = null;
+
};
+
};
+
})
+
);
+
default = null;
+
description = ''
+
Linux high speed af-xdp capture support, see
+
[docs/capture-hardware/af-xdp](https://docs.suricata.io/en/suricata-7.0.3/capture-hardware/af-xdp.html)
+
'';
+
};
+
+
"dpdk" = mkOption {
+
type =
+
with types;
+
nullOr (submodule {
+
options = {
+
eal-params.proc-type = mkOption {
+
type = with types; nullOr str;
+
default = null;
+
};
+
interfaces = mkOption {
+
type =
+
with types;
+
nullOr (
+
listOf (submodule {
+
freeformType = yaml.type;
+
options = {
+
interface = mkOption {
+
type = types.str;
+
default = null;
+
};
+
};
+
})
+
);
+
default = null;
+
};
+
};
+
});
+
default = null;
+
description = ''
+
DPDK capture support, see
+
[docs/capture-hardware/dpdk](https://docs.suricata.io/en/suricata-7.0.3/capture-hardware/dpdk.html)
+
'';
+
};
+
+
"pcap" = mkOption {
+
type =
+
with types;
+
nullOr (
+
listOf (submodule {
+
freeformType = yaml.type;
+
options = {
+
interface = mkOption {
+
type = types.str;
+
default = null;
+
};
+
};
+
})
+
);
+
default = null;
+
description = ''
+
Cross platform libpcap capture support
+
'';
+
};
+
+
"pcap-file".checksum-checks = mkOption {
+
type = types.enum [
+
"yes"
+
"no"
+
"auto"
+
];
+
default = "auto";
+
description = ''
+
Possible values are:
+
- yes: checksum validation is forced
+
- no: checksum validation is disabled
+
- auto: Suricata uses a statistical approach to detect when
+
checksum off-loading is used. (default)
+
Warning: 'checksum-validation' must be set to yes to have checksum tested
+
'';
+
};
+
+
"app-layer" = mkOption {
+
type =
+
with types;
+
nullOr (submodule {
+
options = {
+
"error-policy" = mkOption {
+
type = types.enum [
+
"drop-flow"
+
"pass-flow"
+
"bypass"
+
"drop-packet"
+
"pass-packet"
+
"reject"
+
"ignore"
+
];
+
default = "ignore";
+
description = ''
+
The error-policy setting applies to all app-layer parsers. Values can be
+
"drop-flow", "pass-flow", "bypass", "drop-packet", "pass-packet", "reject" or
+
"ignore" (the default).
+
'';
+
};
+
protocols = mkOption {
+
type =
+
with types;
+
nullOr (
+
attrsOf (submodule {
+
freeformType = yaml.type;
+
options = {
+
enabled = mkOption {
+
type = types.enum [
+
"yes"
+
"no"
+
"detection-only"
+
];
+
default = "no";
+
description = ''
+
The option "enabled" takes 3 values - "yes", "no", "detection-only".
+
"yes" enables both detection and the parser, "no" disables both, and
+
"detection-only" enables protocol detection only (parser disabled).
+
'';
+
};
+
};
+
})
+
);
+
default = null;
+
};
+
};
+
});
+
default = null; # do not add to config unless specified
+
};
+
+
"run-as" = {
+
user = mkOption {
+
type = types.str;
+
default = "suricata";
+
description = "Run Suricata with a specific user-id";
+
};
+
group = mkOption {
+
type = types.str;
+
default = "suricata";
+
description = "Run Suricata with a specific group-id";
+
};
+
};
+
+
"host-mode" = mkOption {
+
type = types.enum [
+
"router"
+
"sniffer-only"
+
"auto"
+
];
+
default = "auto";
+
description = ''
+
If the Suricata box is a router for the sniffed networks, set it to 'router'. If
+
it is a pure sniffing setup, set it to 'sniffer-only'. If set to auto, the variable
+
is internally switched to 'router' in IPS mode and 'sniffer-only' in IDS mode.
+
This feature is currently only used by the reject* keywords.
+
'';
+
};
+
+
"unix-command" = mkOption {
+
type =
+
with types;
+
nullOr (submodule {
+
options = {
+
enabled = mkOption {
+
type = types.either types.bool (types.enum [ "auto" ]);
+
default = "auto";
+
};
+
filename = mkOption {
+
type = types.path;
+
default = "/run/suricata/suricata-command.socket";
+
};
+
};
+
});
+
default = { };
+
description = ''
+
Unix command socket that can be used to pass commands to Suricata.
+
An external tool can then connect to get information from Suricata
+
or trigger some modifications of the engine. Set enabled to yes
+
to activate the feature. In auto mode, the feature will only be
+
activated in live capture mode. You can use the filename variable to set
+
the file name of the socket.
+
'';
+
};
+
+
"exception-policy" = mkOption {
+
type = types.enum [
+
"auto"
+
"drop-packet"
+
"drop-flow"
+
"reject"
+
"bypass"
+
"pass-packet"
+
"pass-flow"
+
"ignore"
+
];
+
default = "auto";
+
description = ''
+
Define a common behavior for all exception policies.
+
In IPS mode, the default is drop-flow. For cases when that's not possible, the
+
engine will fall to drop-packet. To fallback to old behavior (setting each of
+
them individually, or ignoring all), set this to ignore.
+
All values available for exception policies can be used, and there is one
+
extra option: auto - which means drop-flow or drop-packet (as explained above)
+
in IPS mode, and ignore in IDS mode. Exception policy values are: drop-packet,
+
drop-flow, reject, bypass, pass-packet, pass-flow, ignore (disable).
+
'';
+
};
+
+
"default-rule-path" = mkOption {
+
type = types.path;
+
default = "/var/lib/suricata/rules";
+
description = "Path in which suricata-update managed rules are stored by default";
+
};
+
+
"rule-files" = mkOption {
+
type = types.listOf types.str;
+
default = [ "suricata.rules" ];
+
description = "Files to load suricata-update managed rules, relative to 'default-rule-path'";
+
};
+
+
"classification-file" = mkOption {
+
type = types.str;
+
default = "/var/lib/suricata/rules/classification.config";
+
description = "Suricata classification configuration file";
+
};
+
+
"reference-config-file" = mkOption {
+
type = types.str;
+
default = "${cfg.package}/etc/suricata/reference.config";
+
description = "Suricata reference configuration file";
+
};
+
+
"threshold-file" = mkOption {
+
type = types.str;
+
default = "${cfg.package}/etc/suricata/threshold.config";
+
description = "Suricata threshold configuration file";
+
};
+
+
includes = mkOption {
+
type = with types; nullOr (listOf path);
+
default = null;
+
description = ''
+
Files to include in the suricata configuration. See
+
[docs/configuration/suricata-yaml](https://docs.suricata.io/en/suricata-7.0.3/configuration/suricata-yaml.html)
+
for available options.
+
'';
+
};
+
};
+
}
+1
nixos/tests/all-tests.nix
···
sudo = handleTest ./sudo.nix {};
sudo-rs = handleTest ./sudo-rs.nix {};
sunshine = handleTest ./sunshine.nix {};
+
suricata = handleTest ./suricata.nix {};
suwayomi-server = handleTest ./suwayomi-server.nix {};
swap-file-btrfs = handleTest ./swap-file-btrfs.nix {};
swap-partition = handleTest ./swap-partition.nix {};
+86
nixos/tests/suricata.nix
···
+
import ./make-test-python.nix (
+
{ lib, pkgs, ... }:
+
{
+
name = "suricata";
+
meta.maintainers = with lib.maintainers; [ felbinger ];
+
+
nodes = {
+
ids = {
+
imports = [
+
../modules/profiles/minimal.nix
+
../modules/services/networking/suricata/default.nix
+
];
+
+
networking.interfaces.eth1 = {
+
useDHCP = false;
+
ipv4.addresses = [
+
{
+
address = "192.168.1.2";
+
prefixLength = 24;
+
}
+
];
+
};
+
+
# disable suricata-update because this requires an Internet connection
+
systemd.services.suricata-update.enable = false;
+
+
# install suricata package to make suricatasc program available
+
environment.systemPackages = with pkgs; [ suricata ];
+
+
services.suricata = {
+
enable = true;
+
settings = {
+
vars.address-groups.HOME_NET = "192.168.1.0/24";
+
unix-command.enabled = true;
+
outputs = [ { fast.enabled = true; } ];
+
af-packet = [ { interface = "eth1"; } ];
+
classification-file = "${pkgs.suricata}/etc/suricata/classification.config";
+
};
+
};
+
+
# create suricata.rules with the rule to detect the output of the id command
+
systemd.tmpfiles.rules = [
+
''f /var/lib/suricata/rules/suricata.rules 644 suricata suricata 0 alert ip any any -> any any (msg:"GPL ATTACK_RESPONSE id check returned root"; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:2100498; rev:7; metadata:created_at 2010_09_23, updated_at 2019_07_26;)''
+
];
+
};
+
helper = {
+
imports = [ ../modules/profiles/minimal.nix ];
+
+
networking.interfaces.eth1 = {
+
useDHCP = false;
+
ipv4.addresses = [
+
{
+
address = "192.168.1.1";
+
prefixLength = 24;
+
}
+
];
+
};
+
+
services.nginx = {
+
enable = true;
+
virtualHosts."localhost".locations = {
+
"/id/".return = "200 'uid=0(root) gid=0(root) groups=0(root)'";
+
};
+
};
+
networking.firewall.allowedTCPPorts = [ 80 ];
+
};
+
};
+
+
testScript = ''
+
start_all()
+
+
# check that configuration has been applied correctly with suricatasc
+
with subtest("suricata configuration test"):
+
ids.wait_for_unit("suricata.service")
+
assert '1' in ids.succeed("suricatasc -c 'iface-list' | ${pkgs.jq}/bin/jq .message.count")
+
+
# test detection of events based on a static ruleset (output of id command)
+
with subtest("suricata rule test"):
+
helper.wait_for_unit("nginx.service")
+
ids.wait_for_unit("suricata.service")
+
+
ids.succeed("curl http://192.168.1.1/id/")
+
assert "id check returned root [**] [Classification: Potentially Bad Traffic]" in ids.succeed("tail -n 1 /var/log/suricata/fast.log"), "Suricata didn't detect the output of id comment"
+
'';
+
}
+
)