923 lines
31 KiB
Nix
923 lines
31 KiB
Nix
{
|
|
config,
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
|
|
format = pkgs.formats.yaml { };
|
|
|
|
rootDir = "/var/lib/crowdsec";
|
|
stateDir = "${rootDir}/state";
|
|
confDir = "/etc/crowdsec/";
|
|
hubDir = "${stateDir}/hub/";
|
|
notificationsDir = "${confDir}/notifications/";
|
|
pluginDir = "${confDir}/plugins/";
|
|
parsersDir = "${confDir}/parsers/";
|
|
localPostOverflowsDir = "${confDir}/postoverflows/";
|
|
localPostOverflowsS01WhitelistDir = "${localPostOverflowsDir}/s01-whitelist/";
|
|
localScenariosDir = "${confDir}/scenarios/";
|
|
localParsersS00RawDir = "${parsersDir}/s00-raw/";
|
|
localParsersS01ParseDir = "${parsersDir}/s01-parse/";
|
|
localParsersS02EnrichDir = "${parsersDir}/s02-enrich/";
|
|
localContextsDir = "${confDir}/contexts/";
|
|
|
|
in
|
|
{
|
|
|
|
options.services.crowdsec = with lib; {
|
|
enable = mkEnableOption "CrowdSec Security Engine";
|
|
|
|
package = mkPackageOption pkgs "crowdsec" { };
|
|
|
|
autoUpdateService = mkEnableOption "Auto Hub Update";
|
|
|
|
openFirewall = mkEnableOption "opening the ports in the firewall";
|
|
|
|
user = mkOption {
|
|
type = types.str;
|
|
description = "The user to run crowdsec as";
|
|
default = "crowdsec";
|
|
};
|
|
|
|
group = mkOption {
|
|
type = types.str;
|
|
description = "The group to run crowdsec as";
|
|
default = "crowdsec";
|
|
};
|
|
|
|
name = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
Name of the machine when registering it at the central or local api.
|
|
'';
|
|
default = config.networking.hostName;
|
|
defaultText = lib.literalExpression "config.networking.hostName";
|
|
};
|
|
|
|
localConfig = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
acquisitions = mkOption {
|
|
type = types.listOf format.type;
|
|
default = [ ];
|
|
description = ''
|
|
A list of acquisition specifications, which define the data sources you want to be parsed.
|
|
See <https://docs.crowdsec.net/docs/data_sources/intro> for details.
|
|
'';
|
|
example = [
|
|
{
|
|
source = "journalctl";
|
|
journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ];
|
|
labels = {
|
|
type = "syslog";
|
|
};
|
|
}
|
|
];
|
|
};
|
|
scenarios = mkOption {
|
|
type = types.listOf format.type;
|
|
default = [ ];
|
|
description = ''
|
|
A list of scenarios specifications.
|
|
See <https://docs.crowdsec.net/docs/scenarios/intro> for details.
|
|
'';
|
|
example = [
|
|
{
|
|
type = "leaky";
|
|
name = "crowdsecurity/myservice-bf";
|
|
description = "Detect myservice bruteforce";
|
|
filter = "evt.Meta.log_type == 'myservice_failed_auth'";
|
|
leakspeed = "10s";
|
|
capacity = 5;
|
|
groupby = "evt.Meta.source_ip";
|
|
}
|
|
];
|
|
};
|
|
parsers = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
s00Raw = mkOption {
|
|
type = types.listOf format.type;
|
|
default = [ ];
|
|
description = ''
|
|
A list of stage s00-raw specifications. Most of the time, those are already included in the hub, but are presented here anyway.
|
|
See <https://docs.crowdsec.net/docs/parsers/intro> for details.
|
|
'';
|
|
};
|
|
s01Parse = mkOption {
|
|
type = types.listOf format.type;
|
|
default = [ ];
|
|
description = ''
|
|
A list of stage s01-parse specifications.
|
|
See <https://docs.crowdsec.net/docs/parsers/intro> for details.
|
|
'';
|
|
example = [
|
|
{
|
|
filter = "1=1";
|
|
debug = true;
|
|
onsuccess = "next_stage";
|
|
name = "example/custom-service-logs";
|
|
description = "Parsing custom service logs";
|
|
grok = {
|
|
pattern = "^%{DATA:some_data}$";
|
|
apply_on = "message";
|
|
};
|
|
statics = [
|
|
{
|
|
parsed = "is_my_custom_service";
|
|
value = "yes";
|
|
}
|
|
];
|
|
}
|
|
];
|
|
};
|
|
s02Enrich = mkOption {
|
|
type = types.listOf format.type;
|
|
default = [ ];
|
|
description = ''
|
|
A list of stage s02-enrich specifications. Inside this list, you can specify Parser Whitelists.
|
|
See <https://docs.crowdsec.net/docs/whitelist/intro> for details.
|
|
'';
|
|
example = [
|
|
{
|
|
name = "myips/whitelist";
|
|
description = "Whitelist parse events from my IPs";
|
|
whitelist = {
|
|
reason = "My IP ranges";
|
|
ip = [
|
|
"1.2.3.4"
|
|
];
|
|
cidr = [
|
|
"1.2.3.0/24"
|
|
];
|
|
};
|
|
}
|
|
];
|
|
};
|
|
};
|
|
};
|
|
default = { };
|
|
};
|
|
postOverflows = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
s01Whitelist = mkOption {
|
|
type = types.listOf format.type;
|
|
default = [ ];
|
|
description = ''
|
|
A list of stage s01-whitelist specifications. Inside this list, you can specify Postoverflows Whitelists.
|
|
See <https://docs.crowdsec.net/docs/whitelist/intro> for details.
|
|
'';
|
|
example = [
|
|
{
|
|
name = "postoverflows/whitelist_my_dns_domain";
|
|
description = "Whitelist my reverse DNS";
|
|
whitelist = {
|
|
reason = "Don't ban me";
|
|
expression = [
|
|
"evt.Enriched.reverse_dns endsWith '.local.'"
|
|
];
|
|
};
|
|
}
|
|
];
|
|
};
|
|
};
|
|
};
|
|
default = { };
|
|
};
|
|
contexts = mkOption {
|
|
type = types.listOf format.type;
|
|
description = ''
|
|
A list of additional contexts to specify.
|
|
See <https://docs.crowdsec.net/docs/next/log_processor/alert_context/intro> for details.
|
|
'';
|
|
example = [
|
|
{
|
|
context = {
|
|
target_uri = [ "evt.Meta.http_path" ];
|
|
user_agent = [ "evt.Meta.http_user_agent" ];
|
|
method = [ "evt.Meta.http_verb" ];
|
|
status = [ "evt.Meta.http_status" ];
|
|
};
|
|
}
|
|
];
|
|
default = [ ];
|
|
};
|
|
notifications = mkOption {
|
|
type = types.listOf format.type;
|
|
description = ''
|
|
A list of notifications to enable and use in your profiles. Note that for now, only the plugins shipped by default with CrowdSec are supported.
|
|
See <https://docs.crowdsec.net/docs/notification_plugins/intro> for details.
|
|
'';
|
|
example = [
|
|
{
|
|
type = "http";
|
|
name = "default_http_notification";
|
|
log_level = "info";
|
|
format = ''
|
|
{{.|toJson}}
|
|
'';
|
|
url = "https://example.com/hook";
|
|
method = "POST";
|
|
}
|
|
];
|
|
default = [ ];
|
|
};
|
|
profiles = mkOption {
|
|
type = types.listOf format.type;
|
|
description = ''
|
|
A list of profiles to enable.
|
|
See <https://docs.crowdsec.net/docs/profiles/intro> for more details.
|
|
'';
|
|
example = [
|
|
{
|
|
name = "default_ip_remediation";
|
|
filters = [
|
|
"Alert.Remediation == true && Alert.GetScope() == 'Ip'"
|
|
];
|
|
decisions = [
|
|
{
|
|
type = "ban";
|
|
duration = "4h";
|
|
}
|
|
];
|
|
on_success = "break";
|
|
}
|
|
{
|
|
name = "default_range_remediation";
|
|
filters = [
|
|
"Alert.Remediation == true && Alert.GetScope() == 'Range'"
|
|
];
|
|
decisions = [
|
|
{
|
|
type = "ban";
|
|
duration = "4h";
|
|
}
|
|
];
|
|
on_success = "break";
|
|
}
|
|
];
|
|
default = [
|
|
{
|
|
name = "default_ip_remediation";
|
|
filters = [
|
|
"Alert.Remediation == true && Alert.GetScope() == 'Ip'"
|
|
];
|
|
decisions = [
|
|
{
|
|
type = "ban";
|
|
duration = "4h";
|
|
}
|
|
];
|
|
on_success = "break";
|
|
}
|
|
{
|
|
name = "default_range_remediation";
|
|
filters = [
|
|
"Alert.Remediation == true && Alert.GetScope() == 'Range'"
|
|
];
|
|
decisions = [
|
|
{
|
|
type = "ban";
|
|
duration = "4h";
|
|
}
|
|
];
|
|
on_success = "break";
|
|
}
|
|
];
|
|
};
|
|
patterns = mkOption {
|
|
type = types.listOf types.package;
|
|
default = [ ];
|
|
example = lib.literalExpression ''
|
|
[ (pkgs.writeTextDir "custom_service_logs" (builtins.readFile ./custom_service_logs)) ]
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
default = { };
|
|
};
|
|
|
|
hub = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
collections = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "List of hub collections to install";
|
|
example = [ "crowdsecurity/linux" ];
|
|
};
|
|
|
|
scenarios = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "List of hub scenarios to install";
|
|
example = [ "crowdsecurity/ssh-bf" ];
|
|
};
|
|
|
|
parsers = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "List of hub parsers to install";
|
|
example = [ "crowdsecurity/sshd-logs" ];
|
|
};
|
|
|
|
postOverflows = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "List of hub postoverflows to install";
|
|
example = [ "crowdsecurity/auditd-nix-wrappers-whitelist-process" ];
|
|
};
|
|
|
|
appSecConfigs = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "List of hub appsec configurations to install";
|
|
example = [ "crowdsecurity/appsec-default" ];
|
|
};
|
|
|
|
appSecRules = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
description = "List of hub appsec rules to install";
|
|
example = [ "crowdsecurity/base-config" ];
|
|
};
|
|
};
|
|
};
|
|
default = { };
|
|
description = ''
|
|
Hub collections, parsers, AppSec rules, etc.
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
general = mkOption {
|
|
description = ''
|
|
Settings for the main CrowdSec configuration file.
|
|
Refer to the defaults at <https://github.com/crowdsecurity/crowdsec/blob/master/config/config.yaml>.
|
|
'';
|
|
type = format.type;
|
|
default = { };
|
|
};
|
|
simulation = mkOption {
|
|
type = format.type;
|
|
default = {
|
|
simulation = false;
|
|
};
|
|
description = ''
|
|
Attributes inside the simulation.yaml file.
|
|
'';
|
|
};
|
|
|
|
lapi = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
credentialsFile = mkOption {
|
|
type = types.nullOr types.path;
|
|
example = "/run/crowdsec/lapi.yaml";
|
|
description = ''
|
|
The LAPI credential file to use.
|
|
'';
|
|
default = null;
|
|
};
|
|
};
|
|
};
|
|
description = ''
|
|
LAPI Configuration attributes
|
|
'';
|
|
default = { };
|
|
};
|
|
capi = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
credentialsFile = mkOption {
|
|
type = types.nullOr types.path;
|
|
example = "/run/crowdsec/capi.yaml";
|
|
description = ''
|
|
The CAPI credential file to use.
|
|
'';
|
|
default = null;
|
|
};
|
|
};
|
|
};
|
|
description = ''
|
|
CAPI Configuration attributes
|
|
'';
|
|
default = { };
|
|
};
|
|
console = mkOption {
|
|
type = types.submodule {
|
|
options = {
|
|
tokenFile = mkOption {
|
|
type = types.nullOr types.path;
|
|
example = "/run/crowdsec/console_token.yaml";
|
|
description = ''
|
|
The Console Token file to use.
|
|
'';
|
|
default = null;
|
|
};
|
|
configuration = mkOption {
|
|
type = format.type;
|
|
default = {
|
|
share_manual_decisions = false;
|
|
share_custom = false;
|
|
share_tainted = false;
|
|
share_context = false;
|
|
};
|
|
description = ''
|
|
Attributes inside the console.yaml file.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
description = ''
|
|
Console Configuration attributes
|
|
'';
|
|
default = { };
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
config =
|
|
let
|
|
cfg = config.services.crowdsec;
|
|
configFile = format.generate "crowdsec.yaml" cfg.settings.general;
|
|
simulationFile = format.generate "simulation.yaml" cfg.settings.simulation;
|
|
consoleFile = format.generate "console.yaml" cfg.settings.console.configuration;
|
|
patternsDir = pkgs.buildPackages.symlinkJoin {
|
|
name = "crowdsec-patterns";
|
|
paths = [
|
|
cfg.localConfig.patterns
|
|
"${lib.attrsets.getOutput "out" cfg.package}/share/crowdsec/config/patterns/"
|
|
];
|
|
};
|
|
|
|
cscli = pkgs.writeShellScriptBin "cscli" ''
|
|
set -euo pipefail
|
|
# cscli needs crowdsec on it's path in order to be able to run `cscli explain`
|
|
export PATH="$PATH:${lib.makeBinPath [ cfg.package ]}"
|
|
sudo=exec
|
|
if [ "$USER" != "${cfg.user}" ]; then
|
|
sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}'
|
|
fi
|
|
$sudo ${lib.getExe' cfg.package "cscli"} -c=${configFile} "$@"
|
|
'';
|
|
|
|
localScenariosMap = (map (format.generate "scenario.yaml") cfg.localConfig.scenarios);
|
|
localParsersS00RawMap = (
|
|
map (format.generate "parsers-s00-raw.yaml") cfg.localConfig.parsers.s00Raw
|
|
);
|
|
localParsersS01ParseMap = (
|
|
map (format.generate "parsers-s01-parse.yaml") cfg.localConfig.parsers.s01Parse
|
|
);
|
|
localParsersS02EnrichMap = (
|
|
map (format.generate "parsers-s02-enrich.yaml") cfg.localConfig.parsers.s02Enrich
|
|
);
|
|
localPostOverflowsS01WhitelistMap = (
|
|
map (format.generate "postoverflows-s01-whitelist.yaml") cfg.localConfig.postOverflows.s01Whitelist
|
|
);
|
|
localContextsMap = (map (format.generate "context.yaml") cfg.localConfig.contexts);
|
|
localNotificationsMap = (map (format.generate "notification.yaml") cfg.localConfig.notifications);
|
|
localProfilesFile = pkgs.writeText "local_profiles.yaml" ''
|
|
---
|
|
${lib.strings.concatMapStringsSep "\n---\n" builtins.toJSON cfg.localConfig.profiles}
|
|
---
|
|
'';
|
|
localAcquisisionFile = pkgs.writeText "local_acquisisions.yaml" ''
|
|
---
|
|
${lib.strings.concatMapStringsSep "\n---\n" builtins.toJSON cfg.localConfig.acquisitions}
|
|
---
|
|
'';
|
|
|
|
scriptArray =
|
|
[
|
|
"set -euo pipefail"
|
|
"${lib.getExe cscli} hub update"
|
|
]
|
|
++ lib.optionals (cfg.hub.collections != [ ]) [
|
|
"${lib.getExe cscli} collections install ${
|
|
lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.collections
|
|
}"
|
|
]
|
|
++ lib.optionals (cfg.hub.scenarios != [ ]) [
|
|
"${lib.getExe cscli} scenarios install ${
|
|
lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.scenarios
|
|
}"
|
|
]
|
|
++ lib.optionals (cfg.hub.parsers != [ ]) [
|
|
"${lib.getExe cscli} parsers install ${
|
|
lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.parsers
|
|
}"
|
|
]
|
|
++ lib.optionals (cfg.hub.postOverflows != [ ]) [
|
|
"${lib.getExe cscli} postoverflows install ${
|
|
lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.postOverflows
|
|
}"
|
|
]
|
|
++ lib.optionals (cfg.hub.appSecConfigs != [ ]) [
|
|
"${lib.getExe cscli} appsec-configs install ${
|
|
lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.appSecConfigs
|
|
}"
|
|
]
|
|
++ lib.optionals (cfg.hub.appSecRules != [ ]) [
|
|
"${lib.getExe cscli} appsec-rules install ${
|
|
lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.appSecRules
|
|
}"
|
|
]
|
|
++ lib.optionals (cfg.settings.general.api.server.enable) [
|
|
''
|
|
if [ ! -s "${cfg.settings.general.api.client.credentials_path}" ]; then
|
|
${lib.getExe cscli} machine add "${cfg.name}" --auto
|
|
fi
|
|
''
|
|
]
|
|
++ lib.optionals (cfg.settings.capi.credentialsFile != null) [
|
|
''
|
|
if ! grep -q password "${cfg.settings.capi.credentialsFile}" ]; then
|
|
${lib.getExe cscli} capi register
|
|
fi
|
|
''
|
|
]
|
|
++ lib.optionals (cfg.settings.console.tokenFile != null) [
|
|
''
|
|
if [ ! -e "${cfg.settings.console.tokenFile}" ]; then
|
|
${lib.getExe cscli} console enroll "$(cat ${cfg.settings.console.tokenFile})" --name ${cfg.name}
|
|
fi
|
|
''
|
|
];
|
|
|
|
setupScript = pkgs.writeShellScriptBin "crowdsec-setup" (
|
|
lib.strings.concatStringsSep "\n" scriptArray
|
|
);
|
|
|
|
in
|
|
lib.mkIf (cfg.enable) {
|
|
|
|
warnings =
|
|
[ ]
|
|
++ lib.optionals (cfg.localConfig.profiles == [ ]) [
|
|
"By not specifying profiles in services.crowdsec.localConfig.profiles, CrowdSec will not react to any alert by default."
|
|
]
|
|
++ lib.optionals (cfg.localConfig.acquisitions == [ ]) [
|
|
"By not specifying acquisitions in services.crowdsec.localConfig.acquisitions, CrowdSec will not look for any data source."
|
|
];
|
|
|
|
services.crowdsec.settings.general = with lib; {
|
|
common = {
|
|
daemonize = false;
|
|
log_media = "stdout";
|
|
};
|
|
config_paths = {
|
|
config_dir = confDir;
|
|
data_dir = stateDir;
|
|
simulation_path = simulationFile;
|
|
hub_dir = hubDir;
|
|
index_path = lib.strings.normalizePath "${stateDir}/hub/.index.json";
|
|
notification_dir = notificationsDir;
|
|
plugin_dir = pluginDir;
|
|
pattern_dir = patternsDir;
|
|
};
|
|
db_config = {
|
|
type = mkDefault "sqlite";
|
|
db_path = mkDefault (lib.strings.normalizePath "${stateDir}/crowdsec.db");
|
|
use_wal = mkDefault true;
|
|
};
|
|
crowdsec_service = {
|
|
enable = mkDefault true;
|
|
acquisition_path = mkDefault localAcquisisionFile;
|
|
};
|
|
api = {
|
|
client = {
|
|
credentials_path = cfg.settings.lapi.credentialsFile;
|
|
};
|
|
server = {
|
|
enable = mkDefault false;
|
|
listen_uri = mkDefault "127.0.0.1:8080";
|
|
|
|
console_path = mkDefault consoleFile;
|
|
profiles_path = mkDefault localProfilesFile;
|
|
|
|
#online_client = mkDefault {
|
|
# sharing = mkDefault true;
|
|
# pull = mkDefault {
|
|
# community = mkDefault true;
|
|
# blocklists = mkDefault true;
|
|
# };
|
|
# credentials_path = cfg.settings.capi.credentialsFile;
|
|
#};
|
|
};
|
|
};
|
|
prometheus = {
|
|
enabled = mkDefault true;
|
|
level = mkDefault "full";
|
|
listen_addr = mkDefault "127.0.0.1";
|
|
listen_port = mkDefault 6060;
|
|
};
|
|
cscli = {
|
|
hub_branch = "v${cfg.package.version}";
|
|
};
|
|
};
|
|
|
|
environment = {
|
|
systemPackages = [ cscli ];
|
|
};
|
|
|
|
systemd.packages = [ cfg.package ];
|
|
|
|
systemd.timers.crowdsec-update-hub = lib.mkIf (cfg.autoUpdateService) {
|
|
description = "Update the crowdsec hub index";
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = "daily";
|
|
Persistent = "yes";
|
|
Unit = "crowdsec-update-hub.service";
|
|
};
|
|
};
|
|
systemd.services = {
|
|
crowdsec-update-hub = lib.mkIf (cfg.autoUpdateService) {
|
|
description = "Update the crowdsec hub index";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
LimitNOFILE = 65536;
|
|
NoNewPrivileges = true;
|
|
LockPersonality = true;
|
|
RemoveIPC = true;
|
|
ReadWritePaths = [
|
|
rootDir
|
|
confDir
|
|
];
|
|
ProtectSystem = "strict";
|
|
PrivateUsers = true;
|
|
ProtectHome = true;
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
ProtectHostname = true;
|
|
UMask = "0077";
|
|
ProtectKernelTunables = true;
|
|
ProtectKernelModules = true;
|
|
ProtectControlGroups = true;
|
|
ProtectProc = "invisible";
|
|
SystemCallFilter = [
|
|
" " # This is needed to clear the SystemCallFilter existing definitions
|
|
"~@reboot"
|
|
"~@swap"
|
|
"~@obsolete"
|
|
"~@mount"
|
|
"~@module"
|
|
"~@debug"
|
|
"~@cpu-emulation"
|
|
"~@clock"
|
|
"~@raw-io"
|
|
"~@privileged"
|
|
"~@resources"
|
|
];
|
|
CapabilityBoundingSet = [
|
|
" " # Reset all capabilities to an empty set
|
|
];
|
|
RestrictAddressFamilies = [
|
|
" " # This is needed to clear the RestrictAddressFamilies existing definitions
|
|
"none" # Remove all addresses families
|
|
"AF_UNIX"
|
|
"AF_INET"
|
|
"AF_INET6"
|
|
];
|
|
DevicePolicy = "closed";
|
|
ProtectKernelLogs = true;
|
|
SystemCallArchitectures = "native";
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
RestrictSUIDSGID = true;
|
|
ExecStart = "${lib.getExe cscli} --error hub update";
|
|
ExecStartPost = "systemctl reload crowdsec.service";
|
|
};
|
|
};
|
|
|
|
crowdsec = {
|
|
description = "CrowdSec is a free, modern & collaborative behavior detection engine, coupled with a global IP reputation network.";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-online.target" ];
|
|
wants = [ "network-online.target" ];
|
|
path = lib.mkForce [ ];
|
|
environment = {
|
|
LC_ALL = "C";
|
|
LANG = "C";
|
|
};
|
|
serviceConfig = {
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
Type = "simple";
|
|
RestartSec = 60;
|
|
LimitNOFILE = 65536;
|
|
NoNewPrivileges = true;
|
|
LockPersonality = true;
|
|
RemoveIPC = true;
|
|
ReadWritePaths = [
|
|
rootDir
|
|
confDir
|
|
];
|
|
ProtectSystem = "strict";
|
|
PrivateUsers = true;
|
|
ProtectHome = true;
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
ProtectHostname = true;
|
|
ProtectClock = true;
|
|
UMask = "0077";
|
|
ProtectKernelTunables = true;
|
|
ProtectKernelModules = true;
|
|
ProtectControlGroups = true;
|
|
ProtectProc = "invisible";
|
|
SystemCallFilter = [
|
|
" " # This is needed to clear the SystemCallFilter existing definitions
|
|
"~@reboot"
|
|
"~@swap"
|
|
"~@obsolete"
|
|
"~@mount"
|
|
"~@module"
|
|
"~@debug"
|
|
"~@cpu-emulation"
|
|
"~@clock"
|
|
"~@raw-io"
|
|
"~@privileged"
|
|
"~@resources"
|
|
];
|
|
CapabilityBoundingSet = [
|
|
" " # Reset all capabilities to an empty set
|
|
"CAP_SYSLOG" # Add capability to read syslog
|
|
];
|
|
RestrictAddressFamilies = [
|
|
" " # This is needed to clear the RestrictAddressFamilies existing definitions
|
|
"none" # Remove all addresses families
|
|
"AF_UNIX"
|
|
"AF_INET"
|
|
"AF_INET6"
|
|
];
|
|
DevicePolicy = "closed";
|
|
ProtectKernelLogs = true;
|
|
SystemCallArchitectures = "native";
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
RestrictSUIDSGID = true;
|
|
ExecReload = [
|
|
" " # This is needed to clear the ExecReload definitions from upstream
|
|
];
|
|
ExecStart = [
|
|
" " # This is needed to clear the ExecStart definitions from upstream
|
|
"${lib.getExe' cfg.package "crowdsec"} -c ${configFile} -info"
|
|
];
|
|
ExecStartPre = [
|
|
" " # This is needed to clear the ExecStartPre definitions from upstream
|
|
"${lib.getExe setupScript}"
|
|
"${lib.getExe' cfg.package "crowdsec"} -c ${configFile} -t -error"
|
|
];
|
|
};
|
|
};
|
|
};
|
|
|
|
systemd.tmpfiles.settings = {
|
|
"10-crowdsec" =
|
|
builtins.listToAttrs (
|
|
map
|
|
(dirName: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath dirName;
|
|
value = {
|
|
d = {
|
|
user = cfg.user;
|
|
group = cfg.group;
|
|
mode = "0750";
|
|
};
|
|
};
|
|
})
|
|
[
|
|
stateDir
|
|
hubDir
|
|
confDir
|
|
localScenariosDir
|
|
localPostOverflowsDir
|
|
localPostOverflowsS01WhitelistDir
|
|
parsersDir
|
|
localParsersS00RawDir
|
|
localParsersS01ParseDir
|
|
localParsersS02EnrichDir
|
|
localContextsDir
|
|
notificationsDir
|
|
pluginDir
|
|
]
|
|
)
|
|
// builtins.listToAttrs (
|
|
map (scenarioFile: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath "${localScenariosDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf scenarioFile)}";
|
|
value = {
|
|
link = {
|
|
type = "L+";
|
|
argument = "${scenarioFile}";
|
|
};
|
|
};
|
|
}) localScenariosMap
|
|
)
|
|
// builtins.listToAttrs (
|
|
map (parser: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath "${localParsersS00RawDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf parser)}";
|
|
value = {
|
|
link = {
|
|
type = "L+";
|
|
argument = "${parser}";
|
|
};
|
|
};
|
|
}) localParsersS00RawMap
|
|
)
|
|
// builtins.listToAttrs (
|
|
map (parser: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath "${localParsersS01ParseDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf parser)}";
|
|
value = {
|
|
link = {
|
|
type = "L+";
|
|
argument = "${parser}";
|
|
};
|
|
};
|
|
}) localParsersS01ParseMap
|
|
)
|
|
// builtins.listToAttrs (
|
|
map (parser: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath "${localParsersS02EnrichDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf parser)}";
|
|
value = {
|
|
link = {
|
|
type = "L+";
|
|
argument = "${parser}";
|
|
};
|
|
};
|
|
}) localParsersS02EnrichMap
|
|
)
|
|
// builtins.listToAttrs (
|
|
map (postoverflow: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath "${localPostOverflowsS01WhitelistDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf postoverflow)}";
|
|
value = {
|
|
link = {
|
|
type = "L+";
|
|
argument = "${postoverflow}";
|
|
};
|
|
};
|
|
}) localPostOverflowsS01WhitelistMap
|
|
)
|
|
// builtins.listToAttrs (
|
|
map (context: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath "${localContextsDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf context)}";
|
|
value = {
|
|
link = {
|
|
type = "L+";
|
|
argument = "${context}";
|
|
};
|
|
};
|
|
}) localContextsMap
|
|
)
|
|
// builtins.listToAttrs (
|
|
map (notification: {
|
|
inherit cfg;
|
|
name = lib.strings.normalizePath "${notificationsDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf notification)}";
|
|
value = {
|
|
link = {
|
|
type = "L+";
|
|
argument = "${notification}";
|
|
};
|
|
};
|
|
}) localNotificationsMap
|
|
);
|
|
};
|
|
|
|
users.users.${cfg.user} = {
|
|
name = cfg.user;
|
|
description = lib.mkDefault "CrowdSec service user";
|
|
isSystemUser = true;
|
|
group = cfg.group;
|
|
extraGroups = [ "systemd-journal" ];
|
|
};
|
|
|
|
users.groups.${cfg.group} = lib.mapAttrs (name: lib.mkDefault) { };
|
|
|
|
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
|
|
6060
|
|
8080
|
|
];
|
|
};
|
|
|
|
meta = {
|
|
maintainers = with lib.maintainers; [
|
|
m0ustach3
|
|
jk
|
|
];
|
|
};
|
|
}
|