diff --git a/modules/crowdsec.nix b/modules/crowdsec.nix new file mode 100644 index 0000000..c53f301 --- /dev/null +++ b/modules/crowdsec.nix @@ -0,0 +1,237 @@ +{ + pkgs, + lib, + config, + ... +}: +let + cfg = config.services.crowdsec; + settingsFormat = pkgs.formats.yaml { }; + + # TODO(tlater): Upstream properly installing the service file + crowdsec = pkgs.symlinkJoin { + name = "crowdsec-with-unit"; + paths = [ + pkgs.crowdsec + (pkgs.runCommandLocal "crowdsec.service" { } '' + mkdir -p $out/lib/systemd/system/ + + substitute ${pkgs.crowdsec}/share/crowdsec/config/crowdsec.service $out/lib/systemd/system/crowdsec.service \ + --replace-fail /usr/local ${pkgs.crowdsec} + '') + ]; + }; +in +{ + options.services.crowdsec = + let + inherit (lib.types) nullOr package path; + in + { + enable = lib.mkEnableOption "crowdsec"; + + package = lib.mkOption { + type = package; + default = crowdsec; + }; + + stateDirectory = lib.mkOption { + type = path; + readOnly = true; + + description = '' + The state directory of the crowdsec instance. Cannot be + changed, but is exposed for downstream use. + ''; + }; + + settings = lib.mkOption { + inherit (settingsFormat) type; + default = { }; + + description = '' + The crowdsec configuration. Refer to + <https://docs.crowdsec.net/docs/next/configuration/crowdsec_configuration/> + for details on supported values. + ''; + }; + + # clientCredentials = lib.mkOption { + # type = path; + + # description = '' + # The API client credentials to configure; Required to access + # the service at all. + # ''; + # }; + + centralApiCredentials = lib.mkOption { + type = nullOr path; + default = null; + + description = '' + The API key to access crowdsec's central API - this is + required to access any of the shared blocklists. + + Use of this feature is optional, entering no API key (the + default) turns all sharing or receiving of blocked IPs off. + + Note that adding the API key by itself does not enable + sharing of blocked IPs with the central API. This limits the + types of blocklists this instance can access. + + To also turn sharing blocked IPs on, set + `api.server.online_client.sharing = true;`. + ''; + }; + + ctiApiKey = lib.mkOption { + type = nullOr path; + default = null; + + description = '' + The API key for crowdsec's CTI offering. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + # Set up default settings; anything that *shouldn't* be changed is + # set to the default priority so that users need to use + # `lib.mkForce`. + services.crowdsec = { + stateDirectory = "/var/lib/crowdsec"; + + settings = { + common = { + # Default config daemonizes, but the service is set to + # `notify`; presumably the daemonization isn't really intended + daemonize = false; + # The default logs to files, which isn't the preferred way + # on NixOS + log_media = "stdout"; + }; + + config_paths = { + config_dir = "/etc/crowdsec/"; + data_dir = "${cfg.stateDirectory}/data/"; + # This "config" file is intended to be written to using the + # cscli tool, so you can temporarily make it so rules don't + # do anything but log what they *would* do for + # experimentation. + simulation_path = "${cfg.stateDirectory}/config/simulation.yaml"; + + pattern_dir = "${cfg.package}/share/crowdsec/config/patterns"; + + # We don't want to actually download anything; Any rules + # will be properly packaged. + hub_dir = lib.mkDefault "/var/empty/"; + index_path = lib.mkDefault "/var/empty/.index.json"; + + # Integrations aren't supported for now + notification_dir = lib.mkDefault "/var/empty/"; + plugin_dir = lib.mkDefault "/var/empty/"; + }; + + crowdsec_service.acquisition_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/acquis.yaml"; + + cscli = { + prometheus_uri = lib.mkDefault "127.0.0.1:6060"; + }; + + api = { + cti = { + enabled = cfg.ctiApiKey != null; + key = cfg.ctiApiKey; + }; + # client.credentials_path = cfg.clientCredentials; + client.credentials_path = "${cfg.stateDirectory}/local_credentials.yaml"; + server = { + listen_uri = lib.mkDefault "127.0.0.1:8080"; + profiles_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/profiles.yaml"; + console_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/console.yaml"; + + online_client = { + # By default, we don't let crowdsec phone home, since + # this is usually within NixOS users' concerns. + # + # TODO: Enable when this option becomes available + # (1.6.4, current nixpkgs-unstable) + # sharing = lib.mkDefault false; + credentials_path = cfg.centralApiCredentials; + }; + }; + }; + + # We enable prometheus by default, since cscli relies on it + # for metrics + prometheus = { + enabled = lib.mkDefault true; + level = lib.mkDefault "full"; + listen_addr = lib.mkDefault "127.0.0.1"; + listen_port = lib.mkDefault 6060; + }; + }; + }; + + systemd.packages = [ + cfg.package + ]; + + environment.etc."crowdsec/config.yaml".source = + settingsFormat.generate "crowdsec-settings.yaml" cfg.settings; + + # Note that the service basics are already defined upstream + systemd.services.crowdsec.serviceConfig = { + + # TODO: ExecStartPre to make `/var/lib/crowdsec/config` + + User = "crowdsec"; + Group = "crowdsec"; + SupplementaryGroups = [ "systemd-journal" ]; + + StateDirectory = "crowdsec"; + + PrivateTmp = true; + PrivateUsers = true; + ProtectHome = true; + CapabilityBoundingSet = [ ]; + LockPersonality = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + NoNewPrivileges = true; + RestrictSUIDSGID = true; + + ProtectProc = "invisible"; + ProcSubset = "pid"; # Needed for journal access + + RestrictNamespaces = true; + RestrictRealtime = true; + + SystemCallFilter = [ + "@system-service" + "@network-io" + ]; + SystemCallArchitectures = [ "native" ]; + SystemCallErrorNumber = "EPERM"; + + ExecPaths = [ "/nix/store" ]; + NoExecPaths = [ "/" ]; + }; + + users = { + users.crowdsec = { + isSystemUser = true; + home = cfg.stateDirectory; + group = "crowdsec"; + }; + groups = { + crowdsec = { }; + }; + }; + }; +} diff --git a/modules/default.nix b/modules/default.nix index e1db4cc..977539a 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1 +1,6 @@ -{ imports = [ ./nginxExtensions.nix ]; } +{ + imports = [ + ./crowdsec.nix + ./nginxExtensions.nix + ]; +}