Add Crowdsec custom module (cimer Pablo)
This commit is contained in:
parent
815b764c4e
commit
7fa64f845a
1 changed files with 923 additions and 0 deletions
923
modules/crowdsec.nix
Normal file
923
modules/crowdsec.nix
Normal file
|
@ -0,0 +1,923 @@
|
|||
{
|
||||
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
|
||||
];
|
||||
};
|
||||
}
|
Loading…
Add table
Reference in a new issue