netdata service: fix permissions for apps.plugin

apps.plugin requires capabilities for full process monitoring. with
1.9.0, netdata allows multiple directories to search for plugins and the
setuid directory can be specified here.

the module is backwards compatible with older configs. a test is
included that verifies data gathering for the elevated privileges. one
additional attribute is added to make configuration more generic than
including configuration in string form.

authored by

Casey Ransom and committed by
Casey Ransom
f3cba4f6 e9d5c55d

+76 -10
+44 -10
nixos/modules/services/monitoring/netdata.nix
··· 5 5 let 6 6 cfg = config.services.netdata; 7 7 8 - configFile = pkgs.writeText "netdata.conf" cfg.configText; 8 + wrappedPlugins = pkgs.runCommand "wrapped-plugins" {} '' 9 + mkdir -p $out/libexec/netdata/plugins.d 10 + ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin 11 + ''; 12 + 13 + localConfig = { 14 + global = { 15 + "plugins directory" = "${wrappedPlugins}/libexec/netdata/plugins.d ${pkgs.netdata}/libexec/netdata/plugins.d"; 16 + }; 17 + }; 18 + mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config); 19 + configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig); 9 20 10 21 defaultUser = "netdata"; 11 22 12 23 in { 13 24 options = { 14 25 services.netdata = { 15 - enable = mkOption { 16 - default = false; 17 - type = types.bool; 18 - description = "Whether to enable netdata monitoring."; 19 - }; 26 + enable = mkEnableOption "netdata"; 20 27 21 28 user = mkOption { 22 29 type = types.str; ··· 31 38 }; 32 39 33 40 configText = mkOption { 34 - type = types.lines; 35 - default = ""; 36 - description = "netdata.conf configuration."; 41 + type = types.nullOr types.lines; 42 + description = "Verbatim netdata.conf, cannot be combined with config."; 43 + default = null; 37 44 example = '' 38 45 [global] 39 46 debug log = syslog ··· 42 49 ''; 43 50 }; 44 51 52 + config = mkOption { 53 + type = types.attrsOf types.attrs; 54 + default = {}; 55 + description = "netdata.conf configuration as nix attributes. cannot be combined with configText."; 56 + example = literalExample '' 57 + global = { 58 + "debug log" = "syslog"; 59 + "access log" = "syslog"; 60 + "error log" = "syslog"; 61 + }; 62 + ''; 63 + }; 64 + }; 45 65 }; 46 - }; 47 66 48 67 config = mkIf cfg.enable { 68 + assertions = 69 + [ { assertion = cfg.config != {} -> cfg.configText == null ; 70 + message = "Cannot specify both config and configText"; 71 + } 72 + ]; 49 73 systemd.services.netdata = { 74 + path = with pkgs; [ gawk curl ]; 50 75 description = "Real time performance monitoring"; 51 76 after = [ "network.target" ]; 52 77 wantedBy = [ "multi-user.target" ]; ··· 65 90 TimeoutStopSec = 60; 66 91 }; 67 92 }; 93 + 94 + security.wrappers."apps.plugin" = { 95 + source = "${pkgs.netdata}/libexec/netdata/plugins.d/apps.plugin"; 96 + capabilities = "cap_dac_read_search,cap_sys_ptrace+ep"; 97 + owner = cfg.user; 98 + group = cfg.group; 99 + permissions = "u+rx,g+rx,o-rwx"; 100 + }; 101 + 68 102 69 103 users.extraUsers = optional (cfg.user == defaultUser) { 70 104 name = defaultUser;
+1
nixos/release.nix
··· 312 312 tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; }; 313 313 tests.nat.firewall-conntrack = callTest tests/nat.nix { withFirewall = true; withConntrackHelpers = true; }; 314 314 tests.nat.standalone = callTest tests/nat.nix { withFirewall = false; }; 315 + tests.netdata = callTest tests/netdata.nix { }; 315 316 tests.networking.networkd = callSubTests tests/networking.nix { networkd = true; }; 316 317 tests.networking.scripted = callSubTests tests/networking.nix { networkd = false; }; 317 318 # TODO: put in networking.nix after the test becomes more complete
+31
nixos/tests/netdata.nix
··· 1 + # This test runs netdata and checks for data via apps.plugin 2 + 3 + import ./make-test.nix ({ pkgs, ...} : { 4 + name = "netdata"; 5 + meta = with pkgs.stdenv.lib.maintainers; { 6 + maintainers = [ cransom ]; 7 + }; 8 + 9 + nodes = { 10 + netdata = 11 + { config, pkgs, ... }: 12 + { 13 + environment.systemPackages = with pkgs; [ curl jq ]; 14 + services.netdata.enable = true; 15 + }; 16 + }; 17 + 18 + testScript = '' 19 + startAll; 20 + 21 + $netdata->waitForUnit("netdata.service"); 22 + # check if netdata can read disk ops for root owned processes. 23 + # if > 0, successful. verifies both netdata working and 24 + # apps.plugin has elevated capabilities. 25 + my $cmd = <<'CMD'; 26 + curl -s http://localhost:19999/api/v1/data\?chart=users.pwrites | \ 27 + jq -e '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0' 28 + CMD 29 + $netdata->waitUntilSucceeds($cmd); 30 + ''; 31 + })