8 cfg = config.services.suricata;
10 yaml = pkgs.formats.yaml { };
25 meta.maintainers = with lib.maintainers; [ felbinger ];
27 options.services.suricata = {
28 enable = mkEnableOption "Suricata";
30 package = mkPackageOption pkgs "suricata" { };
32 configFile = mkOption {
35 default = pkgs.writeTextFile {
36 name = "suricata.yaml";
41 yaml.generate "suricata-settings-raw.yaml" (
42 filterAttrsRecursive (name: value: value != null) cfg.settings
48 Configuration file for suricata.
50 It is not usual to override the default values; it is recommended to use `settings`.
51 If you want to include extra configuration to the file, use the `settings.includes`.
56 type = types.submodule (import ./settings.nix { inherit config lib yaml; });
57 example = literalExpression ''
58 vars.address-groups.HOME_NET = "192.168.178.0/24";
63 filename = "fast.log";
71 filename = "eve.json";
75 alert.tagged-packets = "yes";
85 cluster-type = "cluster_flow";
89 interface = "default";
107 app-layer.protocols = {
108 telnet.enabled = "yes";
109 dnp3.enabled = "yes";
110 modbus.enabled = "yes";
113 description = "Suricata settings";
116 enabledSources = mkOption {
117 type = types.listOf types.str;
118 # see: nix-shell -p suricata python3Packages.pyyaml --command 'suricata-update list-sources'
121 "etnetera/aggressive"
125 "sslbl/ja3-fingerprints"
126 "sslbl/ssl-fp-blacklist"
127 "malsilo/win-malware"
131 List of sources that should be enabled.
132 Currently sources which require a secret-code are not supported.
136 disabledRules = mkOption {
137 type = types.listOf types.str;
138 # protocol dnp3 seams to be disabled, which causes the signature evaluation to fail, so we disable the
139 # dnp3 rules, see https://github.com/OISF/suricata/blob/master/rules/dnp3-events.rules for more details
148 List of rules that should be disabled.
157 inherit (lists) unique optionals;
160 map (e: e.interface) (
161 (optionals (cfg.settings.af-packet != null) cfg.settings.af-packet)
162 ++ (optionals (cfg.settings.af-xdp != null) cfg.settings.af-xdp)
164 cfg.settings.dpdk != null && cfg.settings.dpdk.interfaces != null
165 ) cfg.settings.dpdk.interfaces)
166 ++ (optionals (cfg.settings.pcap != null) cfg.settings.pcap)
173 assertion = (builtins.length captureInterfaces) > 0;
175 At least one capture interface must be configured:
176 - `services.suricata.settings.af-packet`
177 - `services.suricata.settings.af-xdp`
178 - `services.suricata.settings.dpdk.interfaces`
179 - `services.suricata.settings.pcap`
184 boot.kernelModules = mkIf (cfg.settings.af-packet != null) [ "af_packet" ];
187 groups.${cfg.settings.run-as.group} = { };
188 users.${cfg.settings.run-as.user} = {
189 group = cfg.settings.run-as.group;
194 systemd.tmpfiles.rules = [
195 "d ${cfg.settings."default-log-dir"} 755 ${cfg.settings.run-as.user} ${cfg.settings.run-as.group}"
196 "d /var/lib/suricata 755 ${cfg.settings.run-as.user} ${cfg.settings.run-as.group}"
197 "d ${cfg.settings."default-rule-path"} 755 ${cfg.settings.run-as.user} ${cfg.settings.run-as.group}"
202 description = "Update Suricata Rules";
203 wantedBy = [ "multi-user.target" ];
204 wants = [ "network-online.target" ];
205 after = [ "network-online.target" ];
209 python = pkgs.python3.withPackages (ps: with ps; [ pyyaml ]);
210 enabledSourcesCmds = map (
211 src: "${python.interpreter} ${pkg}/bin/suricata-update enable-source ${src}"
212 ) cfg.enabledSources;
215 ${concatStringsSep "\n" enabledSourcesCmds}
216 ${python.interpreter} ${pkg}/bin/suricata-update update-sources
217 ${python.interpreter} ${pkg}/bin/suricata-update update --suricata-conf ${cfg.configFile} --no-test \
218 --disable-conf ${pkgs.writeText "suricata-disable-conf" "${concatStringsSep "\n" cfg.disabledRules}"}
224 PrivateDevices = true;
228 User = cfg.settings.run-as.user;
229 Group = cfg.settings.run-as.group;
231 ReadOnlyPaths = cfg.configFile;
234 cfg.settings."default-rule-path"
239 description = "Suricata";
240 wantedBy = [ "multi-user.target" ];
241 after = [ "suricata-update.service" ];
244 interfaceOptions = strings.concatMapStrings (interface: " -i ${interface}") captureInterfaces;
247 ExecStartPre = "!${pkg}/bin/suricata -c ${cfg.configFile} -T";
248 ExecStart = "!${pkg}/bin/suricata -c ${cfg.configFile}${interfaceOptions}";
249 Restart = "on-failure";
251 User = cfg.settings.run-as.user;
252 Group = cfg.settings.run-as.group;
254 NoNewPrivileges = true;
256 PrivateDevices = true;
258 ProtectSystem = "strict";
259 DevicePolicy = "closed";
260 LockPersonality = true;
261 MemoryDenyWriteExecute = true;
262 ProtectHostname = true;
264 ProtectKernelLogs = true;
265 ProtectKernelModules = true;
266 ProtectKernelTunables = true;
267 ProtectControlGroups = true;
269 RestrictNamespaces = true;
270 RestrictRealtime = true;
271 RestrictSUIDSGID = true;
272 SystemCallArchitectures = "native";
275 ReadOnlyPaths = cfg.configFile;
276 ReadWritePaths = cfg.settings."default-log-dir";
277 RuntimeDirectory = "suricata";