at 25.11-pre 18 kB view raw
1{ 2 config, 3 pkgs, 4 lib, 5 ... 6}: 7let 8 cfg = config.services.netdata; 9 10 wrappedPlugins = pkgs.runCommand "wrapped-plugins" { preferLocalBuild = true; } '' 11 mkdir -p $out/libexec/netdata/plugins.d 12 ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin 13 ln -s /run/wrappers/bin/cgroup-network $out/libexec/netdata/plugins.d/cgroup-network 14 ln -s /run/wrappers/bin/perf.plugin $out/libexec/netdata/plugins.d/perf.plugin 15 ln -s /run/wrappers/bin/slabinfo.plugin $out/libexec/netdata/plugins.d/slabinfo.plugin 16 ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin 17 ln -s /run/wrappers/bin/systemd-journal.plugin $out/libexec/netdata/plugins.d/systemd-journal.plugin 18 ln -s /run/wrappers/bin/logs-management.plugin $out/libexec/netdata/plugins.d/logs-management.plugin 19 ln -s /run/wrappers/bin/network-viewer.plugin $out/libexec/netdata/plugins.d/network-viewer.plugin 20 ln -s /run/wrappers/bin/debugfs.plugin $out/libexec/netdata/plugins.d/debugfs.plugin 21 ''; 22 23 plugins = [ 24 "${cfg.package}/libexec/netdata/plugins.d" 25 "${wrappedPlugins}/libexec/netdata/plugins.d" 26 ] ++ cfg.extraPluginPaths; 27 28 configDirectory = pkgs.runCommand "netdata-config-d" { } '' 29 mkdir $out 30 ${lib.concatStringsSep "\n" ( 31 lib.mapAttrsToList (path: file: '' 32 mkdir -p "$out/$(dirname ${path})" 33 ${if path == "apps_groups.conf" then "cp" else "ln -s"} "${file}" "$out/${path}" 34 '') cfg.configDir 35 )} 36 ''; 37 38 localConfig = { 39 global = { 40 "config directory" = "/etc/netdata/conf.d"; 41 "plugins directory" = lib.concatStringsSep " " plugins; 42 }; 43 web = { 44 "web files owner" = "root"; 45 "web files group" = "root"; 46 }; 47 "plugin:cgroups" = { 48 "script to get cgroup network interfaces" = 49 "${wrappedPlugins}/libexec/netdata/plugins.d/cgroup-network"; 50 "use unified cgroups" = "yes"; 51 }; 52 }; 53 mkConfig = lib.generators.toINI { } (lib.recursiveUpdate localConfig cfg.config); 54 configFile = pkgs.writeText "netdata.conf" ( 55 if cfg.configText != null then cfg.configText else mkConfig 56 ); 57 58 defaultUser = "netdata"; 59 60 isThereAnyWireGuardTunnels = 61 config.networking.wireguard.enable 62 || lib.any ( 63 c: lib.hasAttrByPath [ "netdevConfig" "Kind" ] c && c.netdevConfig.Kind == "wireguard" 64 ) (builtins.attrValues config.systemd.network.netdevs); 65 66 extraNdsudoPathsEnv = pkgs.buildEnv { 67 name = "netdata-ndsudo-env"; 68 paths = cfg.extraNdsudoPackages; 69 pathsToLink = [ "/bin" ]; 70 }; 71 72in 73{ 74 options = { 75 services.netdata = { 76 enable = lib.mkEnableOption "netdata"; 77 78 package = lib.mkPackageOption pkgs "netdata" { }; 79 80 user = lib.mkOption { 81 type = lib.types.str; 82 default = "netdata"; 83 description = "User account under which netdata runs."; 84 }; 85 86 group = lib.mkOption { 87 type = lib.types.str; 88 default = "netdata"; 89 description = "Group under which netdata runs."; 90 }; 91 92 configText = lib.mkOption { 93 type = lib.types.nullOr lib.types.lines; 94 description = "Verbatim netdata.conf, cannot be combined with config."; 95 default = null; 96 example = '' 97 [global] 98 debug log = syslog 99 access log = syslog 100 error log = syslog 101 ''; 102 }; 103 104 python = { 105 enable = lib.mkOption { 106 type = lib.types.bool; 107 default = true; 108 description = '' 109 Whether to enable python-based plugins 110 ''; 111 }; 112 recommendedPythonPackages = lib.mkOption { 113 type = lib.types.bool; 114 default = false; 115 description = '' 116 Whether to enable a set of recommended Python plugins 117 by installing extra Python packages. 118 ''; 119 }; 120 extraPackages = lib.mkOption { 121 type = lib.types.functionTo (lib.types.listOf lib.types.package); 122 default = ps: [ ]; 123 defaultText = lib.literalExpression "ps: []"; 124 example = lib.literalExpression '' 125 ps: [ 126 ps.psycopg2 127 ps.docker 128 ps.dnspython 129 ] 130 ''; 131 description = '' 132 Extra python packages available at runtime 133 to enable additional python plugins. 134 ''; 135 }; 136 }; 137 138 extraPluginPaths = lib.mkOption { 139 type = lib.types.listOf lib.types.path; 140 default = [ ]; 141 example = lib.literalExpression '' 142 [ "/path/to/plugins.d" ] 143 ''; 144 description = '' 145 Extra paths to add to the netdata global "plugins directory" 146 option. Useful for when you want to include your own 147 collection scripts. 148 149 Details about writing a custom netdata plugin are available at: 150 <https://docs.netdata.cloud/collectors/plugins.d/> 151 152 Cannot be combined with configText. 153 ''; 154 }; 155 156 extraNdsudoPackages = lib.mkOption { 157 type = lib.types.listOf lib.types.package; 158 default = [ ]; 159 description = '' 160 Extra packages to add to `PATH` to make available to `ndsudo`. 161 ::: {.warning} 162 `ndsudo` has SUID privileges, be careful what packages you list here. 163 ::: 164 165 ::: {.note} 166 `cfg.package` must be built with `withNdsudo = true` 167 ::: 168 ''; 169 example = '' 170 [ 171 pkgs.smartmontools 172 pkgs.nvme-cli 173 ] 174 ''; 175 }; 176 177 config = lib.mkOption { 178 type = lib.types.attrsOf lib.types.attrs; 179 default = { }; 180 description = "netdata.conf configuration as nix attributes. cannot be combined with configText."; 181 example = lib.literalExpression '' 182 global = { 183 "debug log" = "syslog"; 184 "access log" = "syslog"; 185 "error log" = "syslog"; 186 }; 187 ''; 188 }; 189 190 configDir = lib.mkOption { 191 type = lib.types.attrsOf lib.types.path; 192 default = { }; 193 description = '' 194 Complete netdata config directory except netdata.conf. 195 The default configuration is merged with changes 196 defined in this option. 197 Each top-level attribute denotes a path in the configuration 198 directory as in environment.etc. 199 Its value is the absolute path and must be readable by netdata. 200 Cannot be combined with configText. 201 ''; 202 example = lib.literalExpression '' 203 "health_alarm_notify.conf" = pkgs.writeText "health_alarm_notify.conf" ''' 204 sendmail="/path/to/sendmail" 205 '''; 206 "health.d" = "/run/secrets/netdata/health.d"; 207 ''; 208 }; 209 210 claimTokenFile = lib.mkOption { 211 type = lib.types.nullOr lib.types.path; 212 default = null; 213 description = '' 214 If set, automatically registers the agent using the given claim token 215 file. 216 ''; 217 }; 218 219 enableAnalyticsReporting = lib.mkOption { 220 type = lib.types.bool; 221 default = false; 222 description = '' 223 Enable reporting of anonymous usage statistics to Netdata Inc. via either 224 Google Analytics (in versions prior to 1.29.4), or Netdata Inc.'s 225 self-hosted PostHog (in versions 1.29.4 and later). 226 See: <https://learn.netdata.cloud/docs/agent/anonymous-statistics> 227 ''; 228 }; 229 230 deadlineBeforeStopSec = lib.mkOption { 231 type = lib.types.int; 232 default = 120; 233 description = '' 234 In order to detect when netdata is misbehaving, we run a concurrent task pinging netdata (wait-for-netdata-up) 235 in the systemd unit. 236 237 If after a while, this task does not succeed, we stop the unit and mark it as failed. 238 239 You can control this deadline in seconds with this option, it's useful to bump it 240 if you have (1) a lot of data (2) doing upgrades (3) have low IOPS/throughput. 241 ''; 242 }; 243 }; 244 }; 245 246 config = lib.mkIf cfg.enable { 247 assertions = [ 248 { 249 assertion = cfg.config != { } -> cfg.configText == null; 250 message = "Cannot specify both config and configText"; 251 } 252 ]; 253 254 # Includes a set of recommended Python plugins in exchange of imperfect disk consumption. 255 services.netdata.python.extraPackages = lib.mkIf cfg.python.recommendedPythonPackages (ps: [ 256 ps.requests 257 ps.pandas 258 ps.numpy 259 ps.psycopg2 260 ps.python-ldap 261 ps.netdata-pandas 262 ]); 263 264 services.netdata.configDir.".opt-out-from-anonymous-statistics" = lib.mkIf ( 265 !cfg.enableAnalyticsReporting 266 ) (pkgs.writeText ".opt-out-from-anonymous-statistics" ""); 267 environment.etc."netdata/netdata.conf".source = configFile; 268 environment.etc."netdata/conf.d".source = configDirectory; 269 270 systemd.tmpfiles.settings = lib.mkIf cfg.package.withNdsudo { 271 "95-netdata-ndsudo" = { 272 "/var/lib/netdata/ndsudo" = { 273 "d" = { 274 mode = "0550"; 275 user = cfg.user; 276 group = cfg.group; 277 }; 278 }; 279 280 "/var/lib/netdata/ndsudo/ndsudo" = { 281 "L+" = { 282 argument = "/run/wrappers/bin/ndsudo"; 283 }; 284 }; 285 286 "/var/lib/netdata/ndsudo/runtime-dependencies" = { 287 "L+" = { 288 argument = "${extraNdsudoPathsEnv}/bin"; 289 }; 290 }; 291 }; 292 }; 293 294 systemd.services.netdata = { 295 description = "Real time performance monitoring"; 296 after = [ 297 "network.target" 298 "suid-sgid-wrappers.service" 299 ]; 300 # No wrapper means no "useful" netdata. 301 requires = [ "suid-sgid-wrappers.service" ]; 302 wantedBy = [ "multi-user.target" ]; 303 path = 304 (with pkgs; [ 305 curl 306 gawk 307 iproute2 308 which 309 procps 310 bash 311 nvme-cli # for go.d 312 iw # for charts.d 313 apcupsd # for charts.d 314 # TODO: firehol # for FireQoS -- this requires more NixOS module support. 315 util-linux # provides logger command; required for syslog health alarms 316 ]) 317 ++ lib.optional cfg.python.enable (pkgs.python3.withPackages cfg.python.extraPackages) 318 ++ lib.optional config.virtualisation.libvirtd.enable config.virtualisation.libvirtd.package 319 ++ lib.optional config.virtualisation.docker.enable config.virtualisation.docker.package 320 ++ lib.optionals config.virtualisation.podman.enable [ 321 pkgs.jq 322 config.virtualisation.podman.package 323 ] 324 ++ lib.optional config.boot.zfs.enabled config.boot.zfs.package; 325 environment = 326 { 327 PYTHONPATH = "${cfg.package}/libexec/netdata/python.d/python_modules"; 328 NETDATA_PIPENAME = "/run/netdata/ipc"; 329 } 330 // lib.optionalAttrs (!cfg.enableAnalyticsReporting) { 331 DO_NOT_TRACK = "1"; 332 }; 333 restartTriggers = [ 334 config.environment.etc."netdata/netdata.conf".source 335 config.environment.etc."netdata/conf.d".source 336 ]; 337 serviceConfig = 338 { 339 ExecStart = "${cfg.package}/bin/netdata -P /run/netdata/netdata.pid -D -c /etc/netdata/netdata.conf"; 340 ExecReload = "${pkgs.util-linux}/bin/kill -s HUP -s USR1 -s USR2 $MAINPID"; 341 ExecStartPost = pkgs.writeShellScript "wait-for-netdata-up" '' 342 while [ "$(${cfg.package}/bin/netdatacli ping)" != pong ]; do sleep 0.5; done 343 ''; 344 345 TimeoutStopSec = cfg.deadlineBeforeStopSec; 346 Restart = "on-failure"; 347 # User and group 348 User = cfg.user; 349 Group = cfg.group; 350 # Performance 351 LimitNOFILE = "30000"; 352 # Runtime directory and mode 353 RuntimeDirectory = "netdata"; 354 RuntimeDirectoryMode = "0750"; 355 # State directory and mode 356 StateDirectory = "netdata"; 357 StateDirectoryMode = "0750"; 358 # Cache directory and mode 359 CacheDirectory = "netdata"; 360 CacheDirectoryMode = "0750"; 361 # Logs directory and mode 362 LogsDirectory = "netdata"; 363 LogsDirectoryMode = "0750"; 364 # Configuration directory and mode 365 ConfigurationDirectory = "netdata"; 366 ConfigurationDirectoryMode = "0755"; 367 # AmbientCapabilities 368 AmbientCapabilities = lib.optional isThereAnyWireGuardTunnels "CAP_NET_ADMIN"; 369 # Capabilities 370 CapabilityBoundingSet = [ 371 "CAP_DAC_OVERRIDE" # is required for freeipmi and slabinfo plugins 372 "CAP_DAC_READ_SEARCH" # is required for apps and systemd-journal plugin 373 "CAP_FOWNER" # is required for freeipmi plugin 374 "CAP_SETPCAP" # is required for apps, perf and slabinfo plugins 375 "CAP_SYS_ADMIN" # is required for perf plugin 376 "CAP_SYS_PTRACE" # is required for apps plugin 377 "CAP_SYS_RESOURCE" # is required for ebpf plugin 378 "CAP_NET_RAW" # is required for fping app 379 "CAP_SYS_CHROOT" # is required for cgroups plugin 380 "CAP_SETUID" # is required for cgroups and cgroups-network plugins 381 "CAP_SYSLOG" # is required for systemd-journal plugin 382 ] ++ lib.optional isThereAnyWireGuardTunnels "CAP_NET_ADMIN"; 383 # Sandboxing 384 ProtectSystem = "full"; 385 ProtectHome = "read-only"; 386 PrivateTmp = true; 387 ProtectControlGroups = true; 388 PrivateMounts = true; 389 } 390 // (lib.optionalAttrs (cfg.claimTokenFile != null) { 391 LoadCredential = [ 392 "netdata_claim_token:${cfg.claimTokenFile}" 393 ]; 394 395 ExecStartPre = pkgs.writeShellScript "netdata-claim" '' 396 set -euo pipefail 397 398 if [[ -f /var/lib/netdata/cloud.d/claimed_id ]]; then 399 # Already registered 400 exit 401 fi 402 403 exec ${cfg.package}/bin/netdata-claim.sh \ 404 -token="$(< "$CREDENTIALS_DIRECTORY/netdata_claim_token")" \ 405 -url=https://app.netdata.cloud \ 406 -daemon-not-running 407 ''; 408 }); 409 }; 410 411 systemd.enableCgroupAccounting = true; 412 413 security.wrappers = 414 { 415 "apps.plugin" = { 416 source = "${cfg.package}/libexec/netdata/plugins.d/apps.plugin.org"; 417 capabilities = "cap_dac_read_search,cap_sys_ptrace+ep"; 418 owner = cfg.user; 419 group = cfg.group; 420 permissions = "u+rx,g+x,o-rwx"; 421 }; 422 423 "debugfs.plugin" = { 424 source = "${cfg.package}/libexec/netdata/plugins.d/debugfs.plugin.org"; 425 capabilities = "cap_dac_read_search+ep"; 426 owner = cfg.user; 427 group = cfg.group; 428 permissions = "u+rx,g+x,o-rwx"; 429 }; 430 431 "cgroup-network" = { 432 source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org"; 433 capabilities = "cap_setuid+ep"; 434 owner = cfg.user; 435 group = cfg.group; 436 permissions = "u+rx,g+x,o-rwx"; 437 }; 438 439 "perf.plugin" = { 440 source = "${cfg.package}/libexec/netdata/plugins.d/perf.plugin.org"; 441 capabilities = "cap_sys_admin+ep"; 442 owner = cfg.user; 443 group = cfg.group; 444 permissions = "u+rx,g+x,o-rwx"; 445 }; 446 447 "systemd-journal.plugin" = { 448 source = "${cfg.package}/libexec/netdata/plugins.d/systemd-journal.plugin.org"; 449 capabilities = "cap_dac_read_search,cap_syslog+ep"; 450 owner = cfg.user; 451 group = cfg.group; 452 permissions = "u+rx,g+x,o-rwx"; 453 }; 454 455 "slabinfo.plugin" = { 456 source = "${cfg.package}/libexec/netdata/plugins.d/slabinfo.plugin.org"; 457 capabilities = "cap_dac_override+ep"; 458 owner = cfg.user; 459 group = cfg.group; 460 permissions = "u+rx,g+x,o-rwx"; 461 }; 462 463 } 464 // lib.optionalAttrs (cfg.package.withIpmi) { 465 "freeipmi.plugin" = { 466 source = "${cfg.package}/libexec/netdata/plugins.d/freeipmi.plugin.org"; 467 capabilities = "cap_dac_override,cap_fowner+ep"; 468 owner = cfg.user; 469 group = cfg.group; 470 permissions = "u+rx,g+x,o-rwx"; 471 }; 472 } 473 // lib.optionalAttrs (cfg.package.withNetworkViewer) { 474 "network-viewer.plugin" = { 475 source = "${cfg.package}/libexec/netdata/plugins.d/network-viewer.plugin.org"; 476 capabilities = "cap_sys_admin,cap_dac_read_search,cap_sys_ptrace+ep"; 477 owner = cfg.user; 478 group = cfg.group; 479 permissions = "u+rx,g+x,o-rwx"; 480 }; 481 } 482 // lib.optionalAttrs (cfg.package.withNdsudo) { 483 "ndsudo" = { 484 source = "${cfg.package}/libexec/netdata/plugins.d/ndsudo.org"; 485 setuid = true; 486 owner = "root"; 487 group = cfg.group; 488 permissions = "u+rx,g+x,o-rwx"; 489 }; 490 }; 491 492 security.pam.loginLimits = [ 493 { 494 domain = "netdata"; 495 type = "soft"; 496 item = "nofile"; 497 value = "10000"; 498 } 499 { 500 domain = "netdata"; 501 type = "hard"; 502 item = "nofile"; 503 value = "30000"; 504 } 505 ]; 506 507 users.users = lib.optionalAttrs (cfg.user == defaultUser) { 508 ${defaultUser} = { 509 group = defaultUser; 510 isSystemUser = true; 511 extraGroups = 512 lib.optional config.virtualisation.docker.enable "docker" 513 ++ lib.optional config.virtualisation.podman.enable "podman"; 514 }; 515 }; 516 517 users.groups = lib.optionalAttrs (cfg.group == defaultUser) { 518 ${defaultUser} = { }; 519 }; 520 521 }; 522}