nixos/suricata: init module
This commit is contained in:
		
							parent
							
								
									53a0f1bc5c
								
							
						
					
					
						commit
						924ee0c2bc
					
				@ -132,6 +132,8 @@
 | 
			
		||||
 | 
			
		||||
- [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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								nixos/modules/services/networking/suricata/default.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,282 @@
 | 
			
		||||
{
 | 
			
		||||
  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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										625
									
								
								nixos/modules/services/networking/suricata/settings.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,625 @@
 | 
			
		||||
{
 | 
			
		||||
  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.
 | 
			
		||||
      '';
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -942,6 +942,7 @@ in {
 | 
			
		||||
  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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								nixos/tests/suricata.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,86 @@
 | 
			
		||||
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"
 | 
			
		||||
    '';
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user