1{
2 lib,
3 config,
4 yaml,
5 ...
6}:
7let
8 cfg = config.services.suricata;
9 inherit (lib)
10 mkEnableOption
11 mkOption
12 types
13 literalExpression
14 ;
15 mkDisableOption =
16 name:
17 mkEnableOption name
18 // {
19 default = true;
20 example = false;
21 };
22in
23{
24 freeformType = yaml.type;
25 options = {
26 vars = mkOption {
27 type = types.nullOr (
28 types.submodule {
29 options = {
30 address-groups = mkOption {
31 type = (
32 types.submodule {
33 options = {
34 HOME_NET = mkOption {
35 default = "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]";
36 description = ''
37 HOME_NET variable.
38 '';
39 };
40 EXTERNAL_NET = mkOption {
41 default = "!$HOME_NET";
42 description = ''
43 EXTERNAL_NET variable.
44 '';
45 };
46 HTTP_SERVERS = mkOption {
47 default = "$HOME_NET";
48 description = ''
49 HTTP_SERVERS variable.
50 '';
51 };
52 SMTP_SERVERS = mkOption {
53 default = "$HOME_NET";
54 description = ''
55 SMTP_SERVERS variable.
56 '';
57 };
58 SQL_SERVERS = mkOption {
59 default = "$HOME_NET";
60 description = ''
61 SQL_SERVERS variable.
62 '';
63 };
64 DNS_SERVERS = mkOption {
65 default = "$HOME_NET";
66 description = ''
67 DNS_SERVERS variable.
68 '';
69 };
70 TELNET_SERVERS = mkOption {
71 default = "$HOME_NET";
72 description = ''
73 TELNET_SERVERS variable.
74 '';
75 };
76 AIM_SERVERS = mkOption {
77 default = "$EXTERNAL_NET";
78 description = ''
79 AIM_SERVERS variable.
80 '';
81 };
82 DC_SERVERS = mkOption {
83 default = "$HOME_NET";
84 description = ''
85 DC_SERVERS variable.
86 '';
87 };
88 DNP3_SERVER = mkOption {
89 default = "$HOME_NET";
90 description = ''
91 DNP3_SERVER variable.
92 '';
93 };
94 DNP3_CLIENT = mkOption {
95 default = "$HOME_NET";
96 description = ''
97 DNP3_CLIENT variable.
98 '';
99 };
100 MODBUS_CLIENT = mkOption {
101 default = "$HOME_NET";
102 description = ''
103 MODBUS_CLIENT variable
104 '';
105 };
106 MODBUS_SERVER = mkOption {
107 default = "$HOME_NET";
108 description = ''
109 MODBUS_SERVER variable.
110 '';
111 };
112 ENIP_CLIENT = mkOption {
113 default = "$HOME_NET";
114 description = ''
115 ENIP_CLIENT variable.
116 '';
117 };
118 ENIP_SERVER = mkOption {
119 default = "$HOME_NET";
120 description = ''
121 ENIP_SERVER variable.
122 '';
123 };
124 };
125 }
126 );
127 default = { };
128 example = {
129 HOME_NET = "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]";
130 EXTERNAL_NET = "!$HOME_NET";
131 HTTP_SERVERS = "$HOME_NET";
132 SMTP_SERVERS = "$HOME_NET";
133 SQL_SERVERS = "$HOME_NET";
134 DNS_SERVERS = "$HOME_NET";
135 TELNET_SERVERS = "$HOME_NET";
136 AIM_SERVERS = "$EXTERNAL_NET";
137 DC_SERVERS = "$HOME_NET";
138 DNP3_SERVER = "$HOME_NET";
139 DNP3_CLIENT = "$HOME_NET";
140 MODBUS_CLIENT = "$HOME_NET";
141 MODBUS_SERVER = "$HOME_NET";
142 ENIP_CLIENT = "$HOME_NET";
143 ENIP_SERVER = "$HOME_NET";
144 };
145 description = ''
146 The address group variables for suricata, if not defined the
147 default value of suricata (see example) will be used.
148 Your settings will extend the predefined values in example.
149 '';
150 };
151
152 port-groups = mkOption {
153 type = with types; nullOr (attrsOf str);
154 default = {
155 HTTP_PORTS = "80";
156 SHELLCODE_PORTS = "!80";
157 ORACLE_PORTS = "1521";
158 SSH_PORTS = "22";
159 DNP3_PORTS = "20000";
160 MODBUS_PORTS = "502";
161 FILE_DATA_PORTS = "[$HTTP_PORTS,110,143]";
162 FTP_PORTS = "21";
163 GENEVE_PORTS = "6081";
164 VXLAN_PORTS = "4789";
165 TEREDO_PORTS = "3544";
166 };
167 description = ''
168 The port group variables for suricata.
169 '';
170 };
171 };
172 }
173 );
174 default = { }; # add default values to config
175 description = ''
176 Variables to be used within the suricata rules.
177 '';
178 };
179
180 stats = mkOption {
181 type =
182 with types;
183 nullOr (submodule {
184 options = {
185 enable = mkEnableOption "suricata global stats";
186
187 interval = mkOption {
188 type = types.str;
189 default = "8";
190 description = ''
191 The interval field (in seconds) controls the interval at
192 which stats are updated in the log.
193 '';
194 };
195
196 decoder-events = mkOption {
197 type = types.bool;
198 default = true;
199 description = ''
200 Add decode events to stats
201 '';
202 };
203
204 decoder-events-prefix = mkOption {
205 type = types.str;
206 default = "decoder.event";
207 description = ''
208 Decoder event prefix in stats. Has been 'decoder' before, but that leads
209 to missing events in the eve.stats records.
210 '';
211 };
212
213 stream-events = mkOption {
214 type = types.bool;
215 default = false;
216 description = ''
217 Add stream events as stats.
218 '';
219 };
220 };
221 });
222 default = null; # do not add to config unless specified
223 description = ''
224 Engine statistics such as packet counters, memory use counters and others can be logged in several ways. A separate text log 'stats.log' and an EVE record type 'stats' are enabled by default.
225 '';
226 };
227
228 plugins = mkOption {
229 type = with types; nullOr (listOf path);
230 default = null;
231 description = ''
232 Plugins -- Experimental -- specify the filename for each plugin shared object.
233 '';
234 };
235
236 outputs = mkOption {
237 type =
238 with types;
239 nullOr (
240 listOf (
241 attrsOf (submodule {
242 freeformType = yaml.type;
243 options = {
244 enabled = mkEnableOption "<NAME>";
245 };
246 })
247 )
248 );
249 default = null;
250 example = literalExpression ''
251 [
252 {
253 fast = {
254 enabled = "yes";
255 filename = "fast.log";
256 append = "yes";
257 };
258 }
259 {
260 eve-log = {
261 enabled = "yes";
262 filetype = "regular";
263 filename = "eve.json";
264 community-id = true;
265 types = [
266 {
267 alert.tagged-packets = "yes";
268 }
269 ];
270 };
271 }
272 ];
273 '';
274 description = ''
275 Configure the type of alert (and other) logging you would like.
276
277 Valid values for <NAME> are e. g. `fast`, `eve-log`, `syslog`, `file-store`, ...
278 - `fast`: a line based alerts log similar to Snort's fast.log
279 - `eve-log`: Extensible Event Format (nicknamed EVE) event log in JSON format
280
281 For more details regarding the configuration, checkout the shipped suricata.yaml
282 ```shell
283 nix-shell -p suricata yq coreutils-full --command 'yq < $(dirname $(which suricata))/../etc/suricata/suricata.yaml'
284 ```
285 and the [suricata documentation](https://docs.suricata.io/en/latest/output/index.html).
286 '';
287 };
288
289 "default-log-dir" = mkOption {
290 type = types.str;
291 default = "/var/log/suricata";
292 description = ''
293 The default logging directory. Any log or output file will be placed here if it's
294 not specified with a full path name. This can be overridden with the -l command
295 line parameter.
296 '';
297 };
298
299 logging = {
300 "default-log-level" = mkOption {
301 type = types.enum [
302 "error"
303 "warning"
304 "notice"
305 "info"
306 "perf"
307 "config"
308 "debug"
309 ];
310 default = "notice";
311 description = ''
312 The default log level: can be overridden in an output section.
313 Note that debug level logging will only be emitted if Suricata was
314 compiled with the --enable-debug configure option.
315 '';
316 };
317
318 "default-log-format" = mkOption {
319 type = types.nullOr types.str;
320 default = null;
321 description = ''
322 The default output format. Optional parameter, should default to
323 something reasonable if not provided. Can be overridden in an
324 output section. You can leave this out to get the default.
325 '';
326 };
327
328 "default-output-filter" = mkOption {
329 type = types.nullOr types.str;
330 default = null;
331 description = ''
332 A regex to filter output. Can be overridden in an output section.
333 Defaults to empty (no filter).
334 '';
335 };
336
337 "stacktrace-on-signal" = mkOption {
338 type = types.nullOr types.str;
339 default = null;
340 description = ''
341 Requires libunwind to be available when Suricata is configured and built.
342 If a signal unexpectedly terminates Suricata, displays a brief diagnostic
343 message with the offending stacktrace if enabled.
344 '';
345 };
346
347 outputs = {
348 console = {
349 enable = mkDisableOption "logging to console";
350 };
351 file = {
352 enable = mkDisableOption "logging to file";
353
354 level = mkOption {
355 type = types.enum [
356 "error"
357 "warning"
358 "notice"
359 "info"
360 "perf"
361 "config"
362 "debug"
363 ];
364 default = "info";
365 description = ''
366 Loglevel for logs written to the logfile.
367 '';
368 };
369
370 filename = mkOption {
371 type = types.str;
372 default = "suricata.log";
373 description = ''
374 Filename of the logfile.
375 '';
376 };
377
378 format = mkOption {
379 type = types.nullOr types.str;
380 default = null;
381 description = ''
382 Logformat for logs written to the logfile.
383 '';
384 };
385
386 type = mkOption {
387 type = types.nullOr types.str;
388 default = null;
389 description = ''
390 Type of logfile.
391 '';
392 };
393 };
394 syslog = {
395 enable = mkEnableOption "logging to syslog";
396
397 facility = mkOption {
398 type = types.str;
399 default = "local5";
400 description = ''
401 Facility to log to.
402 '';
403 };
404
405 format = mkOption {
406 type = types.nullOr types.str;
407 default = null;
408 description = ''
409 Logformat for logs send to syslog.
410 '';
411 };
412
413 type = mkOption {
414 type = types.nullOr types.str;
415 default = null;
416 description = ''
417 Type of logs send to syslog.
418 '';
419 };
420 };
421 };
422 };
423
424 "af-packet" = mkOption {
425 type =
426 with types;
427 nullOr (
428 listOf (submodule {
429 freeformType = yaml.type;
430 options = {
431 interface = mkOption {
432 type = types.str;
433 default = null;
434 description = ''
435 af-packet capture interface, see [upstream docs reagrding tuning](https://docs.suricata.io/en/latest/performance/tuning-considerations.html#af-packet).
436 '';
437 };
438 };
439 })
440 );
441 default = null;
442 description = ''
443 Linux high speed capture support.
444 '';
445 };
446
447 "af-xdp" = mkOption {
448 type =
449 with types;
450 nullOr (
451 listOf (submodule {
452 freeformType = yaml.type;
453 options = {
454 interface = mkOption {
455 type = types.str;
456 default = null;
457 description = ''
458 af-xdp capture interface, see [upstream docs](https://docs.suricata.io/en/latest/capture-hardware/af-xdp.html).
459 '';
460 };
461 };
462 })
463 );
464 default = null;
465 description = ''
466 Linux high speed af-xdp capture support, see
467 [docs/capture-hardware/af-xdp](https://docs.suricata.io/en/suricata-7.0.3/capture-hardware/af-xdp.html).
468 '';
469 };
470
471 "dpdk" = mkOption {
472 type =
473 with types;
474 nullOr (submodule {
475 options = {
476 eal-params.proc-type = mkOption {
477 type = with types; nullOr str;
478 default = null;
479 description = ''
480 dpdk eal-params.proc-type, see [data plane development kit docs](https://doc.dpdk.org/guides/linux_gsg/linux_eal_parameters.html#multiprocessing-related-options).
481 '';
482 };
483 interfaces = mkOption {
484 type =
485 with types;
486 nullOr (
487 listOf (submodule {
488 freeformType = yaml.type;
489 options = {
490 interface = mkOption {
491 type = types.str;
492 default = null;
493 description = ''
494 See upstream docs: [docs/capture-hardware/dpdk](https://docs.suricata.io/en/suricata-7.0.7/capture-hardware/dpdk.html) and [docs/configuration/suricata-yaml.html#data-plane-development-kit-dpdk](https://docs.suricata.io/en/suricata-7.0.7/configuration/suricata-yaml.html#data-plane-development-kit-dpdk).
495 '';
496 };
497 };
498 })
499 );
500 default = null;
501 description = ''
502 See upstream docs: [docs/capture-hardware/dpdk](https://docs.suricata.io/en/suricata-7.0.7/capture-hardware/dpdk.html) and [docs/configuration/suricata-yaml.html#data-plane-development-kit-dpdk](https://docs.suricata.io/en/suricata-7.0.7/configuration/suricata-yaml.html#data-plane-development-kit-dpdk).
503 '';
504 };
505 };
506 });
507 default = null;
508 description = ''
509 Data Plane Development Kit is a framework for fast packet processing in data plane applications running on a wide variety of CPU architectures. DPDK's Environment Abstraction Layer (EAL) provides a generic interface to low-level resources. It is a unique way how DPDK libraries access NICs. EAL creates an API for an application to access NIC resources from the userspace level. In DPDK, packets are not retrieved via interrupt handling. Instead, the application polls the NIC for newly received packets.
510
511 DPDK allows the user space application to directly access memory where the NIC stores the packets. As a result, neither DPDK nor the application copies the packets for the inspection. The application directly processes packets via passed packet descriptors.
512 See [docs/capture-hardware/dpdk](https://docs.suricata.io/en/suricata-7.0.7/capture-hardware/dpdk.html) and [docs/configuration/suricata-yaml.html#data-plane-development-kit-dpdk](https://docs.suricata.io/en/suricata-7.0.7/configuration/suricata-yaml.html#data-plane-development-kit-dpdk).
513 '';
514 };
515
516 "pcap" = mkOption {
517 type =
518 with types;
519 nullOr (
520 listOf (submodule {
521 freeformType = yaml.type;
522 options = {
523 interface = mkOption {
524 type = types.str;
525 default = null;
526 description = ''
527 pcap capture interface, see [upstream docs](https://docs.suricata.io/en/latest/manpages/suricata.html).
528 '';
529 };
530 };
531 })
532 );
533 default = null;
534 description = ''
535 Cross platform libpcap capture support.
536 '';
537 };
538
539 "pcap-file".checksum-checks = mkOption {
540 type = types.enum [
541 "yes"
542 "no"
543 "auto"
544 ];
545 default = "auto";
546 description = ''
547 Possible values are:
548 - yes: checksum validation is forced
549 - no: checksum validation is disabled
550 - auto: Suricata uses a statistical approach to detect when
551 checksum off-loading is used. (default)
552 Warning: 'checksum-validation' must be set to yes to have checksum tested.
553 '';
554 };
555
556 "app-layer" = mkOption {
557 type =
558 with types;
559 nullOr (submodule {
560 options = {
561 "error-policy" = mkOption {
562 type = types.enum [
563 "drop-flow"
564 "pass-flow"
565 "bypass"
566 "drop-packet"
567 "pass-packet"
568 "reject"
569 "ignore"
570 ];
571 default = "ignore";
572 description = ''
573 The error-policy setting applies to all app-layer parsers. Values can be
574 "drop-flow", "pass-flow", "bypass", "drop-packet", "pass-packet", "reject" or
575 "ignore" (the default).
576 '';
577 };
578 protocols = mkOption {
579 type =
580 with types;
581 nullOr (
582 attrsOf (submodule {
583 freeformType = yaml.type;
584 options = {
585 enabled = mkOption {
586 type = types.enum [
587 "yes"
588 "no"
589 "detection-only"
590 ];
591 default = "no";
592 description = ''
593 The option "enabled" takes 3 values - "yes", "no", "detection-only".
594 "yes" enables both detection and the parser, "no" disables both, and
595 "detection-only" enables protocol detection only (parser disabled).
596 '';
597 };
598 };
599 })
600 );
601 default = null;
602 description = ''
603 app-layer protocols, see [upstream docs](https://docs.suricata.io/en/latest/rules/app-layer.html).
604 '';
605 };
606 };
607 });
608 default = null; # do not add to config unless specified
609 description = ''
610 app-layer configuration, see [upstream docs](https://docs.suricata.io/en/latest/rules/app-layer.html).
611 '';
612 };
613
614 "run-as" = {
615 user = mkOption {
616 type = types.str;
617 default = "suricata";
618 description = "Run Suricata with a specific user-id.";
619 };
620 group = mkOption {
621 type = types.str;
622 default = "suricata";
623 description = "Run Suricata with a specific group-id.";
624 };
625 };
626
627 "host-mode" = mkOption {
628 type = types.enum [
629 "router"
630 "sniffer-only"
631 "auto"
632 ];
633 default = "auto";
634 description = ''
635 If the Suricata box is a router for the sniffed networks, set it to 'router'. If
636 it is a pure sniffing setup, set it to 'sniffer-only'. If set to auto, the variable
637 is internally switched to 'router' in IPS mode and 'sniffer-only' in IDS mode.
638 This feature is currently only used by the reject* keywords.
639 '';
640 };
641
642 "unix-command" = mkOption {
643 type =
644 with types;
645 nullOr (submodule {
646 options = {
647 enabled = mkOption {
648 type = types.either types.bool (types.enum [ "auto" ]);
649 default = "auto";
650 description = ''
651 Enable unix-command socket.
652 '';
653 };
654 filename = mkOption {
655 type = types.path;
656 default = "/run/suricata/suricata-command.socket";
657 description = ''
658 Filename for unix-command socket.
659 '';
660 };
661 };
662 });
663 default = { };
664 description = ''
665 Unix command socket that can be used to pass commands to Suricata.
666 An external tool can then connect to get information from Suricata
667 or trigger some modifications of the engine. Set enabled to yes
668 to activate the feature. In auto mode, the feature will only be
669 activated in live capture mode. You can use the filename variable to set
670 the file name of the socket.
671 '';
672 };
673
674 "exception-policy" = mkOption {
675 type = types.enum [
676 "auto"
677 "drop-packet"
678 "drop-flow"
679 "reject"
680 "bypass"
681 "pass-packet"
682 "pass-flow"
683 "ignore"
684 ];
685 default = "auto";
686 description = ''
687 Define a common behavior for all exception policies.
688 In IPS mode, the default is drop-flow. For cases when that's not possible, the
689 engine will fall to drop-packet. To fallback to old behavior (setting each of
690 them individually, or ignoring all), set this to ignore.
691 All values available for exception policies can be used, and there is one
692 extra option: auto - which means drop-flow or drop-packet (as explained above)
693 in IPS mode, and ignore in IDS mode. Exception policy values are: drop-packet,
694 drop-flow, reject, bypass, pass-packet, pass-flow, ignore (disable).
695 '';
696 };
697
698 "default-rule-path" = mkOption {
699 type = types.path;
700 default = "/var/lib/suricata/rules";
701 description = "Path in which suricata-update managed rules are stored by default.";
702 };
703
704 "rule-files" = mkOption {
705 type = types.listOf types.str;
706 default = [ "suricata.rules" ];
707 description = "Files to load suricata-update managed rules, relative to 'default-rule-path'.";
708 };
709
710 "classification-file" = mkOption {
711 type = types.str;
712 default = "/var/lib/suricata/rules/classification.config";
713 description = "Suricata classification configuration file.";
714 };
715
716 "reference-config-file" = mkOption {
717 type = types.str;
718 default = "${cfg.package}/etc/suricata/reference.config";
719 defaultText = "\${config.services.suricata.package}/etc/suricata/reference.config";
720 description = "Suricata reference configuration file.";
721 };
722
723 "threshold-file" = mkOption {
724 type = types.str;
725 default = "${cfg.package}/etc/suricata/threshold.config";
726 defaultText = "\${config.services.suricata.package}/etc/suricata/threshold.config";
727 description = "Suricata threshold configuration file.";
728 };
729
730 includes = mkOption {
731 type = with types; nullOr (listOf path);
732 default = null;
733 description = ''
734 Files to include in the suricata configuration. See
735 [docs/configuration/suricata-yaml](https://docs.suricata.io/en/suricata-7.0.3/configuration/suricata-yaml.html)
736 for available options.
737 '';
738 };
739 };
740}