at master 34 kB view raw
1{ 2 config, 3 pkgs, 4 lib, 5 ... 6}: 7let 8 9 format = pkgs.formats.yaml { }; 10 11 rootDir = "/var/lib/crowdsec"; 12 stateDir = "${rootDir}/state"; 13 confDir = "/etc/crowdsec/"; 14 hubDir = "${stateDir}/hub/"; 15 notificationsDir = "${confDir}/notifications/"; 16 pluginDir = "${confDir}/plugins/"; 17 parsersDir = "${confDir}/parsers/"; 18 localPostOverflowsDir = "${confDir}/postoverflows/"; 19 localPostOverflowsS01WhitelistDir = "${localPostOverflowsDir}/s01-whitelist/"; 20 localScenariosDir = "${confDir}/scenarios/"; 21 localParsersS00RawDir = "${parsersDir}/s00-raw/"; 22 localParsersS01ParseDir = "${parsersDir}/s01-parse/"; 23 localParsersS02EnrichDir = "${parsersDir}/s02-enrich/"; 24 localContextsDir = "${confDir}/contexts/"; 25 26in 27{ 28 29 options.services.crowdsec = { 30 enable = lib.mkEnableOption "CrowdSec Security Engine"; 31 32 package = lib.mkPackageOption pkgs "crowdsec" { }; 33 34 autoUpdateService = lib.mkEnableOption "if `true` `cscli hub update` will be executed daily. See `https://docs.crowdsec.net/docs/cscli/cscli_hub_update/` for more information"; 35 36 openFirewall = lib.mkOption { 37 type = lib.types.bool; 38 default = false; 39 example = true; 40 description = '' 41 Whether to automatically open firewall ports for `crowdsec`. 42 ''; 43 }; 44 45 user = lib.mkOption { 46 type = lib.types.str; 47 description = "The user to run crowdsec as"; 48 default = "crowdsec"; 49 }; 50 51 group = lib.mkOption { 52 type = lib.types.str; 53 description = "The group to run crowdsec as"; 54 default = "crowdsec"; 55 }; 56 57 name = lib.mkOption { 58 type = lib.types.str; 59 description = '' 60 Name of the machine when registering it at the central or local api. 61 ''; 62 default = config.networking.hostName; 63 defaultText = lib.literalExpression "config.networking.hostName"; 64 }; 65 66 localConfig = lib.mkOption { 67 type = lib.types.submodule { 68 options = { 69 acquisitions = lib.mkOption { 70 type = lib.types.listOf format.type; 71 default = [ ]; 72 description = '' 73 A list of acquisition specifications, which define the data sources you want to be parsed. 74 75 See <https://docs.crowdsec.net/docs/data_sources/intro> for details. 76 ''; 77 example = [ 78 { 79 source = "journalctl"; 80 journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ]; 81 labels = { 82 type = "syslog"; 83 }; 84 } 85 ]; 86 }; 87 scenarios = lib.mkOption { 88 type = lib.types.listOf format.type; 89 default = [ ]; 90 description = '' 91 A list of scenarios specifications. 92 93 See <https://docs.crowdsec.net/docs/scenarios/intro> for details. 94 ''; 95 example = [ 96 { 97 type = "leaky"; 98 name = "crowdsecurity/myservice-bf"; 99 description = "Detect myservice bruteforce"; 100 filter = "evt.Meta.log_type == 'myservice_failed_auth'"; 101 leakspeed = "10s"; 102 capacity = 5; 103 groupby = "evt.Meta.source_ip"; 104 } 105 ]; 106 }; 107 parsers = lib.mkOption { 108 type = lib.types.submodule { 109 options = { 110 s00Raw = lib.mkOption { 111 type = lib.types.listOf format.type; 112 default = [ ]; 113 description = '' 114 A list of stage s00-raw specifications. Most of the time, those are already included in the hub, but are presented here anyway. 115 116 See <https://docs.crowdsec.net/docs/parsers/intro> for details. 117 ''; 118 }; 119 s01Parse = lib.mkOption { 120 type = lib.types.listOf format.type; 121 default = [ ]; 122 description = '' 123 A list of stage s01-parse specifications. 124 125 See <https://docs.crowdsec.net/docs/parsers/intro> for details. 126 ''; 127 example = [ 128 { 129 filter = "1=1"; 130 debug = true; 131 onsuccess = "next_stage"; 132 name = "example/custom-service-logs"; 133 description = "Parsing custom service logs"; 134 grok = { 135 pattern = "^%{DATA:some_data}$"; 136 apply_on = "message"; 137 }; 138 statics = [ 139 { 140 parsed = "is_my_custom_service"; 141 value = "yes"; 142 } 143 ]; 144 } 145 ]; 146 }; 147 s02Enrich = lib.mkOption { 148 type = lib.types.listOf format.type; 149 default = [ ]; 150 description = '' 151 A list of stage s02-enrich specifications. Inside this list, you can specify Parser Whitelists. 152 153 See <https://docs.crowdsec.net/docs/whitelist/intro> for details. 154 ''; 155 example = [ 156 { 157 name = "myips/whitelist"; 158 description = "Whitelist parse events from my IPs"; 159 whitelist = { 160 reason = "My IP ranges"; 161 ip = [ 162 "1.2.3.4" 163 ]; 164 cidr = [ 165 "1.2.3.0/24" 166 ]; 167 }; 168 } 169 ]; 170 }; 171 }; 172 }; 173 description = '' 174 The set of parser specifications. 175 176 See <https://docs.crowdsec.net/docs/parsers/intro> for details. 177 ''; 178 default = { }; 179 }; 180 postOverflows = lib.mkOption { 181 type = lib.types.submodule { 182 options = { 183 s01Whitelist = lib.mkOption { 184 type = lib.types.listOf format.type; 185 default = [ ]; 186 description = '' 187 A list of stage s01-whitelist specifications. Inside this list, you can specify Postoverflows Whitelists. 188 189 See <https://docs.crowdsec.net/docs/whitelist/intro> for details. 190 ''; 191 example = [ 192 { 193 name = "postoverflows/whitelist_my_dns_domain"; 194 description = "Whitelist my reverse DNS"; 195 whitelist = { 196 reason = "Don't ban me"; 197 expression = [ 198 "evt.Enriched.reverse_dns endsWith '.local.'" 199 ]; 200 }; 201 } 202 ]; 203 }; 204 }; 205 }; 206 description = '' 207 The set of Postoverflows specifications. 208 209 See <https://docs.crowdsec.net/docs/next/log_processor/parsers/intro#postoverflows> for details. 210 ''; 211 default = { }; 212 }; 213 contexts = lib.mkOption { 214 type = lib.types.listOf format.type; 215 description = '' 216 A list of additional contexts to specify. 217 218 See <https://docs.crowdsec.net/docs/next/log_processor/alert_context/intro> for details. 219 ''; 220 example = [ 221 { 222 context = { 223 target_uri = [ "evt.Meta.http_path" ]; 224 user_agent = [ "evt.Meta.http_user_agent" ]; 225 method = [ "evt.Meta.http_verb" ]; 226 status = [ "evt.Meta.http_status" ]; 227 }; 228 } 229 ]; 230 default = [ ]; 231 }; 232 notifications = lib.mkOption { 233 type = lib.types.listOf format.type; 234 description = '' 235 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. 236 237 See <https://docs.crowdsec.net/docs/notification_plugins/intro> for details. 238 ''; 239 example = [ 240 { 241 type = "http"; 242 name = "default_http_notification"; 243 log_level = "info"; 244 format = '' 245 {{.|toJson}} 246 ''; 247 url = "https://example.com/hook"; 248 method = "POST"; 249 } 250 ]; 251 default = [ ]; 252 }; 253 profiles = lib.mkOption { 254 type = lib.types.listOf format.type; 255 description = '' 256 A list of profiles to enable. 257 258 See <https://docs.crowdsec.net/docs/profiles/intro> for more details. 259 ''; 260 example = [ 261 { 262 name = "default_ip_remediation"; 263 filters = [ 264 "Alert.Remediation == true && Alert.GetScope() == 'Ip'" 265 ]; 266 decisions = [ 267 { 268 type = "ban"; 269 duration = "4h"; 270 } 271 ]; 272 on_success = "break"; 273 } 274 { 275 name = "default_range_remediation"; 276 filters = [ 277 "Alert.Remediation == true && Alert.GetScope() == 'Range'" 278 ]; 279 decisions = [ 280 { 281 type = "ban"; 282 duration = "4h"; 283 } 284 ]; 285 on_success = "break"; 286 } 287 ]; 288 default = [ 289 { 290 name = "default_ip_remediation"; 291 filters = [ 292 "Alert.Remediation == true && Alert.GetScope() == 'Ip'" 293 ]; 294 decisions = [ 295 { 296 type = "ban"; 297 duration = "4h"; 298 } 299 ]; 300 on_success = "break"; 301 } 302 { 303 name = "default_range_remediation"; 304 filters = [ 305 "Alert.Remediation == true && Alert.GetScope() == 'Range'" 306 ]; 307 decisions = [ 308 { 309 type = "ban"; 310 duration = "4h"; 311 } 312 ]; 313 on_success = "break"; 314 } 315 ]; 316 }; 317 patterns = lib.mkOption { 318 type = lib.types.listOf lib.types.package; 319 description = '' 320 A list of files containing custom grok patterns. 321 ''; 322 default = [ ]; 323 example = lib.literalExpression '' 324 [ (pkgs.writeTextDir "custom_service_logs" (builtins.readFile ./custom_service_logs)) ] 325 ''; 326 }; 327 }; 328 }; 329 description = '' 330 The configuration for a crowdsec security engine. 331 ''; 332 default = { }; 333 }; 334 335 hub = lib.mkOption { 336 type = lib.types.submodule { 337 options = { 338 collections = lib.mkOption { 339 type = lib.types.listOf lib.types.str; 340 default = [ ]; 341 description = "List of hub collections to install"; 342 example = [ "crowdsecurity/linux" ]; 343 }; 344 345 scenarios = lib.mkOption { 346 type = lib.types.listOf lib.types.str; 347 default = [ ]; 348 description = "List of hub scenarios to install"; 349 example = [ "crowdsecurity/ssh-bf" ]; 350 }; 351 352 parsers = lib.mkOption { 353 type = lib.types.listOf lib.types.str; 354 default = [ ]; 355 description = "List of hub parsers to install"; 356 example = [ "crowdsecurity/sshd-logs" ]; 357 }; 358 359 postOverflows = lib.mkOption { 360 type = lib.types.listOf lib.types.str; 361 default = [ ]; 362 description = "List of hub postoverflows to install"; 363 example = [ "crowdsecurity/auditd-nix-wrappers-whitelist-process" ]; 364 }; 365 366 appSecConfigs = lib.mkOption { 367 type = lib.types.listOf lib.types.str; 368 default = [ ]; 369 description = "List of hub appsec configurations to install"; 370 example = [ "crowdsecurity/appsec-default" ]; 371 }; 372 373 appSecRules = lib.mkOption { 374 type = lib.types.listOf lib.types.str; 375 default = [ ]; 376 description = "List of hub appsec rules to install"; 377 example = [ "crowdsecurity/base-config" ]; 378 }; 379 380 branch = lib.mkOption { 381 type = lib.types.str; 382 default = "master"; 383 description = '' 384 The git branch on which cscli is going to fetch configurations. 385 386 See `https://docs.crowdsec.net/docs/configuration/crowdsec_configuration/#hub_branch` for more information. 387 ''; 388 example = [ 389 "master" 390 "v1.4.3" 391 "v1.4.2" 392 ]; 393 }; 394 }; 395 }; 396 default = { }; 397 description = '' 398 Hub collections, parsers, AppSec rules, etc. 399 ''; 400 }; 401 402 settings = lib.mkOption { 403 type = lib.types.submodule { 404 options = { 405 general = lib.mkOption { 406 description = '' 407 Settings for the main CrowdSec configuration file. 408 409 Refer to the defaults at <https://github.com/crowdsecurity/crowdsec/blob/master/config/config.yaml>. 410 ''; 411 type = format.type; 412 default = { }; 413 }; 414 simulation = lib.mkOption { 415 type = format.type; 416 default = { 417 simulation = false; 418 }; 419 description = '' 420 Attributes inside the simulation.yaml file. 421 ''; 422 }; 423 424 lapi = lib.mkOption { 425 type = lib.types.submodule { 426 options = { 427 credentialsFile = lib.mkOption { 428 type = lib.types.nullOr lib.types.path; 429 example = "/run/crowdsec/lapi.yaml"; 430 description = '' 431 The LAPI credential file to use. 432 ''; 433 default = null; 434 }; 435 }; 436 }; 437 description = '' 438 LAPI Configuration attributes 439 ''; 440 default = { }; 441 }; 442 capi = lib.mkOption { 443 type = lib.types.submodule { 444 options = { 445 credentialsFile = lib.mkOption { 446 type = lib.types.nullOr lib.types.path; 447 example = "/run/crowdsec/capi.yaml"; 448 description = '' 449 The CAPI credential file to use. 450 ''; 451 default = null; 452 }; 453 }; 454 }; 455 description = '' 456 CAPI Configuration attributes 457 ''; 458 default = { }; 459 }; 460 console = lib.mkOption { 461 type = lib.types.submodule { 462 options = { 463 tokenFile = lib.mkOption { 464 type = lib.types.nullOr lib.types.path; 465 example = "/run/crowdsec/console_token.yaml"; 466 description = '' 467 The Console Token file to use. 468 ''; 469 default = null; 470 }; 471 configuration = lib.mkOption { 472 type = format.type; 473 default = { 474 share_manual_decisions = false; 475 share_custom = false; 476 share_tainted = false; 477 share_context = false; 478 }; 479 description = '' 480 Attributes inside the console.yaml file. 481 ''; 482 }; 483 }; 484 }; 485 description = '' 486 Console Configuration attributes 487 ''; 488 default = { }; 489 }; 490 }; 491 }; 492 description = '' 493 Set of various configuration attributes 494 ''; 495 }; 496 }; 497 config = 498 let 499 cfg = config.services.crowdsec; 500 configFile = format.generate "crowdsec.yaml" cfg.settings.general; 501 simulationFile = format.generate "simulation.yaml" cfg.settings.simulation; 502 consoleFile = format.generate "console.yaml" cfg.settings.console.configuration; 503 patternsDir = pkgs.buildPackages.symlinkJoin { 504 name = "crowdsec-patterns"; 505 paths = [ 506 cfg.localConfig.patterns 507 "${lib.attrsets.getOutput "out" cfg.package}/share/crowdsec/config/patterns/" 508 ]; 509 }; 510 511 cscli = pkgs.writeShellScriptBin "cscli" '' 512 set -euo pipefail 513 # cscli needs crowdsec on it's path in order to be able to run `cscli explain` 514 export PATH="$PATH:${lib.makeBinPath [ cfg.package ]}" 515 sudo=exec 516 if [ "$USER" != "${cfg.user}" ]; then 517 ${ 518 if config.security.sudo.enable then 519 "sudo='exec ${config.security.wrapperDir}/sudo -u ${cfg.user}'" 520 else 521 ">&2 echo 'Aborting, cscli must be run as user `${cfg.user}`!'; exit 2" 522 } 523 fi 524 $sudo ${lib.getExe' cfg.package "cscli"} -c=${configFile} "$@" 525 ''; 526 527 localScenariosMap = (map (format.generate "scenario.yaml") cfg.localConfig.scenarios); 528 localParsersS00RawMap = ( 529 map (format.generate "parsers-s00-raw.yaml") cfg.localConfig.parsers.s00Raw 530 ); 531 localParsersS01ParseMap = ( 532 map (format.generate "parsers-s01-parse.yaml") cfg.localConfig.parsers.s01Parse 533 ); 534 localParsersS02EnrichMap = ( 535 map (format.generate "parsers-s02-enrich.yaml") cfg.localConfig.parsers.s02Enrich 536 ); 537 localPostOverflowsS01WhitelistMap = ( 538 map (format.generate "postoverflows-s01-whitelist.yaml") cfg.localConfig.postOverflows.s01Whitelist 539 ); 540 localContextsMap = (map (format.generate "context.yaml") cfg.localConfig.contexts); 541 localNotificationsMap = (map (format.generate "notification.yaml") cfg.localConfig.notifications); 542 localProfilesFile = pkgs.writeText "local_profiles.yaml" '' 543 --- 544 ${lib.strings.concatMapStringsSep "\n---\n" builtins.toJSON cfg.localConfig.profiles} 545 --- 546 ''; 547 localAcquisisionFile = pkgs.writeText "local_acquisisions.yaml" '' 548 --- 549 ${lib.strings.concatMapStringsSep "\n---\n" builtins.toJSON cfg.localConfig.acquisitions} 550 --- 551 ''; 552 553 scriptArray = [ 554 "set -euo pipefail" 555 "${lib.getExe' pkgs.coreutils "mkdir"} -p '${hubDir}'" 556 "${lib.getExe cscli} hub update" 557 ] 558 ++ lib.optionals (cfg.hub.collections != [ ]) [ 559 "${lib.getExe cscli} collections install ${ 560 lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.collections 561 }" 562 ] 563 ++ lib.optionals (cfg.hub.scenarios != [ ]) [ 564 "${lib.getExe cscli} scenarios install ${ 565 lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.scenarios 566 }" 567 ] 568 ++ lib.optionals (cfg.hub.parsers != [ ]) [ 569 "${lib.getExe cscli} parsers install ${ 570 lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.parsers 571 }" 572 ] 573 ++ lib.optionals (cfg.hub.postOverflows != [ ]) [ 574 "${lib.getExe cscli} postoverflows install ${ 575 lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.postOverflows 576 }" 577 ] 578 ++ lib.optionals (cfg.hub.appSecConfigs != [ ]) [ 579 "${lib.getExe cscli} appsec-configs install ${ 580 lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.appSecConfigs 581 }" 582 ] 583 ++ lib.optionals (cfg.hub.appSecRules != [ ]) [ 584 "${lib.getExe cscli} appsec-rules install ${ 585 lib.strings.concatMapStringsSep " " (x: lib.escapeShellArg x) cfg.hub.appSecRules 586 }" 587 ] 588 ++ lib.optionals (cfg.settings.general.api.server.enable) [ 589 '' 590 if [ ! -s "${cfg.settings.general.api.client.credentials_path}" ]; then 591 ${lib.getExe cscli} machine add "${cfg.name}" --auto 592 fi 593 '' 594 ] 595 ++ lib.optionals (cfg.settings.capi.credentialsFile != null) [ 596 '' 597 if ! ${lib.getExe pkgs.gnugrep} -q password "${cfg.settings.capi.credentialsFile}" ]; then 598 ${lib.getExe cscli} capi register 599 fi 600 '' 601 ] 602 ++ lib.optionals (cfg.settings.console.tokenFile != null) [ 603 '' 604 if [ ! -e "${cfg.settings.console.tokenFile}" ]; then 605 ${lib.getExe cscli} console enroll "$(cat ${cfg.settings.console.tokenFile})" --name ${cfg.name} 606 fi 607 '' 608 ]; 609 610 setupScript = pkgs.writeShellScriptBin "crowdsec-setup" ( 611 lib.strings.concatStringsSep "\n" scriptArray 612 ); 613 614 in 615 lib.mkIf (cfg.enable) { 616 617 warnings = 618 [ ] 619 ++ lib.optionals (cfg.localConfig.profiles == [ ]) [ 620 "By not specifying profiles in services.crowdsec.localConfig.profiles, CrowdSec will not react to any alert by default." 621 ] 622 ++ lib.optionals (cfg.localConfig.acquisitions == [ ]) [ 623 "By not specifying acquisitions in services.crowdsec.localConfig.acquisitions, CrowdSec will not look for any data source." 624 ]; 625 626 services.crowdsec.settings.general = { 627 common = { 628 daemonize = false; 629 log_media = "stdout"; 630 }; 631 config_paths = { 632 config_dir = confDir; 633 data_dir = stateDir; 634 simulation_path = simulationFile; 635 hub_dir = hubDir; 636 index_path = lib.strings.normalizePath "${stateDir}/hub/.index.json"; 637 notification_dir = notificationsDir; 638 plugin_dir = pluginDir; 639 pattern_dir = patternsDir; 640 }; 641 db_config = { 642 type = lib.mkDefault "sqlite"; 643 db_path = lib.mkDefault (lib.strings.normalizePath "${stateDir}/crowdsec.db"); 644 use_wal = lib.mkDefault true; 645 }; 646 crowdsec_service = { 647 enable = lib.mkDefault true; 648 acquisition_path = lib.mkDefault localAcquisisionFile; 649 }; 650 api = { 651 client = { 652 credentials_path = cfg.settings.lapi.credentialsFile; 653 }; 654 server = { 655 enable = lib.mkDefault false; 656 listen_uri = lib.mkDefault "127.0.0.1:8080"; 657 658 console_path = lib.mkDefault consoleFile; 659 profiles_path = lib.mkDefault localProfilesFile; 660 661 online_client = lib.mkDefault { 662 sharing = lib.mkDefault true; 663 pull = lib.mkDefault { 664 community = lib.mkDefault true; 665 blocklists = lib.mkDefault true; 666 }; 667 credentials_path = cfg.settings.capi.credentialsFile; 668 }; 669 }; 670 }; 671 prometheus = { 672 enabled = lib.mkDefault true; 673 level = lib.mkDefault "full"; 674 listen_addr = lib.mkDefault "127.0.0.1"; 675 listen_port = lib.mkDefault 6060; 676 }; 677 cscli = { 678 hub_branch = cfg.hub.branch; 679 }; 680 }; 681 682 environment = { 683 systemPackages = [ cscli ]; 684 }; 685 686 systemd.packages = [ cfg.package ]; 687 688 systemd.timers.crowdsec-update-hub = lib.mkIf (cfg.autoUpdateService) { 689 description = "Update the crowdsec hub index"; 690 wantedBy = [ "timers.target" ]; 691 timerConfig = { 692 OnCalendar = "daily"; 693 RandomizedDelaySec = 300; 694 Persistent = "yes"; 695 Unit = "crowdsec-update-hub.service"; 696 }; 697 }; 698 systemd.services = { 699 crowdsec-update-hub = lib.mkIf (cfg.autoUpdateService) { 700 description = "Update the crowdsec hub index"; 701 serviceConfig = { 702 Type = "oneshot"; 703 User = cfg.user; 704 Group = cfg.group; 705 LimitNOFILE = 65536; 706 NoNewPrivileges = true; 707 LockPersonality = true; 708 RemoveIPC = true; 709 ReadWritePaths = [ 710 rootDir 711 confDir 712 ]; 713 ProtectSystem = "strict"; 714 PrivateUsers = true; 715 ProtectHome = true; 716 PrivateTmp = true; 717 PrivateDevices = true; 718 ProtectHostname = true; 719 UMask = "0077"; 720 ProtectKernelTunables = true; 721 ProtectKernelModules = true; 722 ProtectControlGroups = true; 723 ProtectProc = "invisible"; 724 SystemCallFilter = [ 725 " " # This is needed to clear the SystemCallFilter existing definitions 726 "~@reboot" 727 "~@swap" 728 "~@obsolete" 729 "~@mount" 730 "~@module" 731 "~@debug" 732 "~@cpu-emulation" 733 "~@clock" 734 "~@raw-io" 735 "~@privileged" 736 "~@resources" 737 ]; 738 CapabilityBoundingSet = [ 739 " " # Reset all capabilities to an empty set 740 ]; 741 RestrictAddressFamilies = [ 742 " " # This is needed to clear the RestrictAddressFamilies existing definitions 743 "none" # Remove all addresses families 744 "AF_UNIX" 745 "AF_INET" 746 "AF_INET6" 747 ]; 748 DevicePolicy = "closed"; 749 ProtectKernelLogs = true; 750 SystemCallArchitectures = "native"; 751 RestrictNamespaces = true; 752 RestrictRealtime = true; 753 RestrictSUIDSGID = true; 754 ExecStart = "${lib.getExe cscli} --error hub update"; 755 ExecStartPost = "systemctl reload crowdsec.service"; 756 DynamicUser = true; 757 }; 758 }; 759 760 crowdsec = { 761 description = "CrowdSec agent"; 762 wantedBy = [ "multi-user.target" ]; 763 after = [ "network-online.target" ]; 764 wants = [ "network-online.target" ]; 765 path = lib.mkForce [ ]; 766 environment = { 767 LC_ALL = "C"; 768 LANG = "C"; 769 }; 770 serviceConfig = { 771 User = cfg.user; 772 Group = cfg.group; 773 Type = "notify"; 774 RestartSec = 60; 775 LimitNOFILE = 65536; 776 NoNewPrivileges = true; 777 LockPersonality = true; 778 RemoveIPC = true; 779 ReadWritePaths = [ 780 rootDir 781 confDir 782 ]; 783 ProtectSystem = "strict"; 784 PrivateUsers = true; 785 ProtectHome = true; 786 PrivateTmp = true; 787 PrivateDevices = true; 788 ProtectHostname = true; 789 ProtectClock = true; 790 UMask = "0077"; 791 ProtectKernelTunables = true; 792 ProtectKernelModules = true; 793 ProtectControlGroups = true; 794 ProtectProc = "invisible"; 795 SystemCallFilter = [ 796 " " # This is needed to clear the SystemCallFilter existing definitions 797 "~@reboot" 798 "~@swap" 799 "~@obsolete" 800 "~@mount" 801 "~@module" 802 "~@debug" 803 "~@cpu-emulation" 804 "~@clock" 805 "~@raw-io" 806 "~@privileged" 807 "~@resources" 808 ]; 809 CapabilityBoundingSet = [ 810 " " # Reset all capabilities to an empty set 811 "CAP_SYSLOG" # Add capability to read syslog 812 ]; 813 RestrictAddressFamilies = [ 814 " " # This is needed to clear the RestrictAddressFamilies existing definitions 815 "none" # Remove all addresses families 816 "AF_UNIX" 817 "AF_INET" 818 "AF_INET6" 819 ]; 820 DevicePolicy = "closed"; 821 ProtectKernelLogs = true; 822 SystemCallArchitectures = "native"; 823 DynamicUser = true; 824 RestrictNamespaces = true; 825 RestrictRealtime = true; 826 RestrictSUIDSGID = true; 827 ExecReload = [ 828 " " # This is needed to clear the ExecReload definitions from upstream 829 ]; 830 ExecStart = [ 831 " " # This is needed to clear the ExecStart definitions from upstream 832 "${lib.getExe' cfg.package "crowdsec"} -c ${configFile} -info" 833 ]; 834 ExecStartPre = [ 835 " " # This is needed to clear the ExecStartPre definitions from upstream 836 "${lib.getExe setupScript}" 837 "${lib.getExe' cfg.package "crowdsec"} -c ${configFile} -t -error" 838 ]; 839 }; 840 }; 841 }; 842 843 systemd.tmpfiles.settings = { 844 "10-crowdsec" = 845 846 builtins.listToAttrs ( 847 map 848 (dirName: { 849 inherit cfg; 850 name = lib.strings.normalizePath dirName; 851 value = { 852 d = { 853 user = cfg.user; 854 group = cfg.group; 855 mode = "0750"; 856 }; 857 }; 858 }) 859 [ 860 stateDir 861 hubDir 862 confDir 863 localScenariosDir 864 localPostOverflowsDir 865 localPostOverflowsS01WhitelistDir 866 parsersDir 867 localParsersS00RawDir 868 localParsersS01ParseDir 869 localParsersS02EnrichDir 870 localContextsDir 871 notificationsDir 872 pluginDir 873 ] 874 ) 875 // builtins.listToAttrs ( 876 map (scenarioFile: { 877 inherit cfg; 878 name = lib.strings.normalizePath "${localScenariosDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf scenarioFile)}"; 879 value = { 880 link = { 881 type = "L+"; 882 argument = "${scenarioFile}"; 883 }; 884 }; 885 }) localScenariosMap 886 ) 887 // builtins.listToAttrs ( 888 map (parser: { 889 inherit cfg; 890 name = lib.strings.normalizePath "${localParsersS00RawDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf parser)}"; 891 value = { 892 link = { 893 type = "L+"; 894 argument = "${parser}"; 895 }; 896 }; 897 }) localParsersS00RawMap 898 ) 899 // builtins.listToAttrs ( 900 map (parser: { 901 inherit cfg; 902 name = lib.strings.normalizePath "${localParsersS01ParseDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf parser)}"; 903 value = { 904 link = { 905 type = "L+"; 906 argument = "${parser}"; 907 }; 908 }; 909 }) localParsersS01ParseMap 910 ) 911 // builtins.listToAttrs ( 912 map (parser: { 913 inherit cfg; 914 name = lib.strings.normalizePath "${localParsersS02EnrichDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf parser)}"; 915 value = { 916 link = { 917 type = "L+"; 918 argument = "${parser}"; 919 }; 920 }; 921 }) localParsersS02EnrichMap 922 ) 923 // builtins.listToAttrs ( 924 map (postoverflow: { 925 inherit cfg; 926 name = lib.strings.normalizePath "${localPostOverflowsS01WhitelistDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf postoverflow)}"; 927 value = { 928 link = { 929 type = "L+"; 930 argument = "${postoverflow}"; 931 }; 932 }; 933 }) localPostOverflowsS01WhitelistMap 934 ) 935 // builtins.listToAttrs ( 936 map (context: { 937 inherit cfg; 938 name = lib.strings.normalizePath "${localContextsDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf context)}"; 939 value = { 940 link = { 941 type = "L+"; 942 argument = "${context}"; 943 }; 944 }; 945 }) localContextsMap 946 ) 947 // builtins.listToAttrs ( 948 map (notification: { 949 inherit cfg; 950 name = lib.strings.normalizePath "${notificationsDir}/${builtins.unsafeDiscardStringContext (builtins.baseNameOf notification)}"; 951 value = { 952 link = { 953 type = "L+"; 954 argument = "${notification}"; 955 }; 956 }; 957 }) localNotificationsMap 958 ); 959 }; 960 961 users.users.${cfg.user} = { 962 name = cfg.user; 963 description = lib.mkDefault "CrowdSec service user"; 964 isSystemUser = true; 965 group = cfg.group; 966 extraGroups = [ "systemd-journal" ]; 967 }; 968 969 users.groups.${cfg.group} = lib.mapAttrs (name: lib.mkDefault) { }; 970 971 networking.firewall.allowedTCPPorts = 972 let 973 parsePortFromURLOption = 974 url: option: 975 builtins.addErrorContext "extracting a port from URL: `${option}` requires a port to be specified, but we failed to parse a port from '${url}'" ( 976 lib.strings.toInt (lib.last (lib.strings.splitString ":" url)) 977 ); 978 in 979 lib.mkIf cfg.openFirewall [ 980 cfg.settings.general.prometheus.listen_port 981 (parsePortFromURLOption cfg.settings.general.api.server.listen_uri "config.services.crowdsec.settings.general.api.server.listen_uri") 982 ]; 983 }; 984 985 meta = { 986 maintainers = with lib.maintainers; [ 987 m0ustach3 988 tornax 989 jk 990 ]; 991 }; 992}