feat: Add crowdsec module

This commit is contained in:
Tristan Daniël Maat 2025-01-24 00:35:23 +08:00
parent b09ad8c302
commit 2b0a30a700
Signed by: tlater
GPG key ID: 49670FD774E43268
4 changed files with 362 additions and 2 deletions

View file

@ -190,6 +190,22 @@
"type": "github"
}
},
"nixpkgs-crowdsec": {
"locked": {
"lastModified": 1738085579,
"narHash": "sha256-7mLjMrOiiIi0vI7BJwbEipYQzwA7JF/NWHP+LM4q5S8=",
"owner": "tlater",
"repo": "nixpkgs",
"rev": "426a7afc9a6ecfdac544bda4022acef31e36df34",
"type": "github"
},
"original": {
"owner": "tlater",
"ref": "tlater/fix-crowdsec",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1737192615,
@ -310,6 +326,7 @@
"disko": "disko",
"foundryvtt": "foundryvtt",
"nixpkgs": "nixpkgs_2",
"nixpkgs-crowdsec": "nixpkgs-crowdsec",
"nixpkgs-unstable": "nixpkgs-unstable",
"sonnenshift": "sonnenshift",
"sops-nix": "sops-nix",

View file

@ -26,6 +26,8 @@
url = "git+ssh://git@github.com/sonnenshift/battery-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs-crowdsec.url = "github:tlater/nixpkgs/tlater/fix-crowdsec";
};
outputs =
@ -98,7 +100,10 @@
# Garbage collection root #
###########################
packages.${system}.default = vm.config.system.build.vm;
packages.${system} = {
default = vm.config.system.build.vm;
crowdsec = pkgs.callPackage "${inputs.nixpkgs-crowdsec}/pkgs/by-name/cr/crowdsec/package.nix" { };
};
###################
# Utility scripts #

333
modules/crowdsec.nix Normal file
View file

@ -0,0 +1,333 @@
{
flake-inputs,
pkgs,
lib,
config,
...
}:
let
cfg = config.services.crowdsec;
settingsFormat = pkgs.formats.yaml { };
crowdsec = flake-inputs.self.packages.${pkgs.system}.crowdsec;
hub = pkgs.fetchFromGitHub {
owner = "crowdsecurity";
repo = "hub";
rev = "7a3b4753f4577257c0cbeb8f8f90c7f17d2ae008";
hash = "sha256-HB4jHyhiO8gjBkLmpo6bDbwhfm5m5nAtNlKhDkZjt2I=";
};
cscli = pkgs.writeShellScriptBin "cscli" ''
export PATH="$PATH:${crowdsec}/bin/"
cd ${cfg.stateDirectory}
sudo=exec
if [ "$USER" != "crowdsec" ]; then
sudo='exec /run/wrappers/bin/sudo -u crowdsec'
fi
$sudo ${crowdsec}/bin/cscli "$@"
'';
in
{
options.services.crowdsec =
let
inherit (lib.types)
nullOr
listOf
package
path
str
;
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.
'';
};
parserWhitelist = lib.mkOption {
type = listOf str;
default = [ ];
description = ''
Set of IP addresses to add to a parser-based whitelist.
Addresses can be specified either as plain IP addresses or
in CIDR notation.
'';
};
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 = {
daemonize = true;
# The default logs to files, which isn't the preferred way
# on NixOS
log_media = "stdout";
};
config_paths = {
config_dir = "${cfg.stateDirectory}/config/";
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 = lib.mkDefault "${cfg.package}/share/crowdsec/config/patterns";
hub_dir = hub;
index_path = "${hub}/.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";
};
db_config = {
type = lib.mkDefault "sqlite";
db_path = lib.mkDefault "${cfg.stateDirectory}/data/crowdsec.db";
use_wal = lib.mkDefault true;
flush = {
max_items = lib.mkDefault 5000;
max_age = lib.mkDefault "7d";
};
};
api = {
cti = {
enabled = cfg.ctiApiKey != null;
key = cfg.ctiApiKey;
};
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 = {
systemPackages = [
# TODO(tlater): Figure out a way to get completions to work
cscli
];
etc."crowdsec/config.yaml".source = settingsFormat.generate "crowdsec-settings.yaml" cfg.settings;
};
systemd = {
tmpfiles.settings."10-crowdsec" = lib.mkIf (cfg.parserWhitelist != [ ]) {
"${cfg.stateDirectory}".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${cfg.stateDirectory}/config".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${cfg.stateDirectory}/config/parsers".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${cfg.stateDirectory}/config/parsers/s02-enrich".d = {
user = "crowdsec";
group = "crowdsec";
mode = "0700";
};
"${cfg.stateDirectory}/config/parsers/s02-enrich/nixos-whitelist.yaml" = {
"L+".argument =
(settingsFormat.generate "crowdsec-nixos-whitelist.yaml" {
name = "nixos/parser-whitelist";
description = "Parser whitelist generated by the crowdsec NixOS module";
whitelist = {
reason = "Filtered by NixOS whitelist";
ip = lib.lists.filter (ip: !(lib.hasInfix "/" ip)) cfg.parserWhitelist;
cidr = lib.lists.filter (ip: lib.hasInfix "/" ip) cfg.parserWhitelist;
};
}).outPath;
};
};
services = {
crowdsec-setup = {
description = "Crowdsec database and config preparation";
script = ''
mkdir -p '${cfg.stateDirectory}/'{config,}
if [ ! -e '${cfg.settings.config_paths.simulation_path}' ]; then
cp '${cfg.package}/share/crowdsec/config/simulation.yaml' '${cfg.settings.config_paths.simulation_path}'
fi
if [ ! -e '${cfg.settings.api.client.credentials_path}' ]; then
${cfg.package}/bin/cscli machines add --auto --file '${cfg.settings.api.client.credentials_path}'
fi
'';
serviceConfig = {
User = "crowdsec";
Group = "crowdsec";
StateDirectory = "crowdsec";
Type = "oneshot";
RemainAfterExit = true;
};
};
# Note that the service basics are already defined upstream
crowdsec = {
after = [ "crowdsec-setup.service" ];
bindsTo = [ "crowdsec-setup.service" ];
serviceConfig = {
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 = { };
};
};
};
}

View file

@ -1 +1,6 @@
{ imports = [ ./nginxExtensions.nix ]; }
{
imports = [
./crowdsec.nix
./nginxExtensions.nix
];
}