Compare commits
No commits in common. "7e84f9861de803343d591be9a9138fb4854f48ce" and "0332c7a515f5200f09cd71fbb566f344613d0e28" have entirely different histories.
7e84f9861d
...
0332c7a515
3 changed files with 0 additions and 941 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
dump.sql
|
|
||||||
configuration.nix
|
|
|
@ -1,923 +0,0 @@
|
||||||
{
|
|
||||||
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
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -163,14 +163,6 @@ in {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
{
|
|
||||||
job_name = "crowdsec_nextcloud";
|
|
||||||
static_configs = [
|
|
||||||
{
|
|
||||||
targets = ["192.168.1.45:6060"];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
job_name = "deluge";
|
job_name = "deluge";
|
||||||
static_configs = [
|
static_configs = [
|
||||||
|
@ -187,14 +179,6 @@ in {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
{
|
|
||||||
job_name = "crowdsec_jellyfin";
|
|
||||||
static_configs = [
|
|
||||||
{
|
|
||||||
targets = ["192.168.1.42:6060"];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
services.loki = {
|
services.loki = {
|
||||||
|
|
Loading…
Add table
Reference in a new issue