diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index 8974f9bd..2365b765 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -7,6 +7,7 @@ ./programs/sessiond ./programs/wezterm.nix ./services/archivebox.nix + ./services/crowdsec.nix ./services/ctrld.nix ./services/gallery-dl.nix ./services/uxplay.nix diff --git a/modules/nixos/services/crowdsec.nix b/modules/nixos/services/crowdsec.nix new file mode 100644 index 00000000..83792100 --- /dev/null +++ b/modules/nixos/services/crowdsec.nix @@ -0,0 +1,199 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.crowdsec; + + settingsFormat = pkgs.formats.yaml { }; + + settingsSubmodule = { lib, ... }: { + freeformType = settingsFormat.type; + options.config_paths = { + notification_dir = lib.mkOption { + type = lib.types.path; + description = '' + Directory where configuration files of notification plugins are kept. + ''; + default = pluginsConfigDrv; + defaultText = '' + All of the compiled configuration files from + {option}`services.crowdsec.plugins.settings`. + ''; + example = "./config/crowdsec/plugins"; + }; + + plugin_dir = lib.mkOption { + type = lib.types.path; + description = '' + Directory where plugin executables are kept. + ''; + default = pluginsDir; + defaultText = '' + All of the compiled plugins from + {options}`services.crowdsec.plugins.package`. + ''; + }; + }; + }; + + pluginsDir = pkgs.symlinkJoin { + name = "crowdsec-system-plugins"; + paths = lib.mapAttrsToList (n: v: "${v.package}/share/crowdsec") cfg.plugins; + }; + + pluginsConfigDrv = let + pluginsConfigs = + lib.mapAttrsToList + (n: v: + pkgs.writeTextDir "/notifications/${n}.yaml" (lib.generators.toYAML { } v.settings)) + cfg.plugins; + in pkgs.symlinkJoin { + name = "crowdsec-system-plugins-configs"; + paths = pluginsConfigs; + }; + + crowdsecPluginsModule = { name, config, ... }: { + options = { + settings = lib.mkOption { + type = settingsFormat.type; + description = '' + Configuration settings associated with the plugin. + + ::: {.caution} + This setting is effectively ignored if + {option}`services.crowdsec.settings.config_paths.notification_dir` is + set. + ::: + ''; + default = { }; + example = { + type = "http"; + log_level = "info"; + }; + }; + + package = lib.mkOption { + type = with lib.types; nullOr package; + description = '' + Derivation containing a Crowdsec plugin at `$out/share/crowdsec`. + ''; + default = null; + example = lib.literalExpression "pkgs.crowdsec-slack-notification"; + }; + }; + }; + + configFile = settingsFormat.generate "crowdsec-config" cfg.settings; +in +{ + options.services.crowdsec = { + enable = lib.mkEnableOption "[Crowdsec](https://crowdsec.net), a monitoring service using crowdsourced data"; + + package = lib.mkPackageOption pkgs "crowdsec" { }; + + extraArgs = lib.mkOption { + type = with lib.types; listOf str; + description = '' + Extra arguments to be passed to the Crowdsec service. + ''; + default = [ ]; + example = [ "-warning" ]; + }; + + settings = lib.mkOption { + type = lib.types.submodule settingsSubmodule; + description = '' + Configuration settings to be used with the service. + ''; + default = { }; + example = lib.literalExpression '' + { + common = { + daemonize = false; + log_media = "stdout"; + } + } + ''; + }; + + plugins = lib.mkOption { + type = with lib.types; attrsOf (submodule crowdsecPluginsModule); + description = '' + Set of Crowdsec plugins and their configuration (if given). + ''; + default = { }; + example = lib.literalExpression '' + { + http = { + settings = { + type = "http"; + log_level = "info"; + }; + }; + + slack = { + package = pkgs.crowdsec-slack-notification; + settings = { + type = ""; + log_level = "info"; + }; + }; + } + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.crowdsec = { + description = "Crowdsec monitoring server"; + script = '' + ${lib.getExe' cfg.package "crowdsec"} -c ${configFile} ${lib.escapeShellArgs cfg.extraArgs} + ''; + after = [ + "syslog.target" + "network-online.target" + "remote-fs.target" + "nss-lookup.target" + ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ReadWritePaths = + lib.optionals (cfg.settings.common.log_media or "" == "file") [ + cfg.settings.common.log_folder + ]; + + Type = "notify"; + Restart = "always"; + RestartSec = "60"; + + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + PrivateMounts = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RemoveIPC = true; + StandardOutput = "journal"; + StandardError = "journal"; + SystemCallFilter = "@system-service"; + SystemCallErrorNumber = "EPERM"; + + RestrictAddressFamilies = [ + "AF_LOCAL" + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictSUIDGUID = true; + MemoryDenyWriteExecute = true; + }; + }; + }; +}