1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.netdata;
7
8 wrappedPlugins = pkgs.runCommand "wrapped-plugins" { preferLocalBuild = true; } ''
9 mkdir -p $out/libexec/netdata/plugins.d
10 ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin
11 ln -s /run/wrappers/bin/cgroup-network $out/libexec/netdata/plugins.d/cgroup-network
12 ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin
13 ln -s /run/wrappers/bin/perf.plugin $out/libexec/netdata/plugins.d/perf.plugin
14 ln -s /run/wrappers/bin/slabinfo.plugin $out/libexec/netdata/plugins.d/slabinfo.plugin
15 '';
16
17 plugins = [
18 "${cfg.package}/libexec/netdata/plugins.d"
19 "${wrappedPlugins}/libexec/netdata/plugins.d"
20 ] ++ cfg.extraPluginPaths;
21
22 localConfig = {
23 global = {
24 "plugins directory" = concatStringsSep " " plugins;
25 };
26 web = {
27 "web files owner" = "root";
28 "web files group" = "root";
29 };
30 "plugin:cgroups" = {
31 "script to get cgroup network interfaces" = "${wrappedPlugins}/libexec/netdata/plugins.d/cgroup-network";
32 "use unified cgroups" = "yes";
33 };
34 };
35 mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config);
36 configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig);
37
38 defaultUser = "netdata";
39
40in {
41 options = {
42 services.netdata = {
43 enable = mkEnableOption "netdata";
44
45 package = mkOption {
46 type = types.package;
47 default = pkgs.netdata;
48 defaultText = "pkgs.netdata";
49 description = "Netdata package to use.";
50 };
51
52 user = mkOption {
53 type = types.str;
54 default = "netdata";
55 description = "User account under which netdata runs.";
56 };
57
58 group = mkOption {
59 type = types.str;
60 default = "netdata";
61 description = "Group under which netdata runs.";
62 };
63
64 configText = mkOption {
65 type = types.nullOr types.lines;
66 description = "Verbatim netdata.conf, cannot be combined with config.";
67 default = null;
68 example = ''
69 [global]
70 debug log = syslog
71 access log = syslog
72 error log = syslog
73 '';
74 };
75
76 python = {
77 enable = mkOption {
78 type = types.bool;
79 default = true;
80 description = ''
81 Whether to enable python-based plugins
82 '';
83 };
84 extraPackages = mkOption {
85 type = types.functionTo (types.listOf types.package);
86 default = ps: [];
87 defaultText = "ps: []";
88 example = literalExample ''
89 ps: [
90 ps.psycopg2
91 ps.docker
92 ps.dnspython
93 ]
94 '';
95 description = ''
96 Extra python packages available at runtime
97 to enable additional python plugins.
98 '';
99 };
100 };
101
102 extraPluginPaths = mkOption {
103 type = types.listOf types.path;
104 default = [ ];
105 example = literalExample ''
106 [ "/path/to/plugins.d" ]
107 '';
108 description = ''
109 Extra paths to add to the netdata global "plugins directory"
110 option. Useful for when you want to include your own
111 collection scripts.
112 </para><para>
113 Details about writing a custom netdata plugin are available at:
114 <link xlink:href="https://docs.netdata.cloud/collectors/plugins.d/"/>
115 </para><para>
116 Cannot be combined with configText.
117 '';
118 };
119
120 config = mkOption {
121 type = types.attrsOf types.attrs;
122 default = {};
123 description = "netdata.conf configuration as nix attributes. cannot be combined with configText.";
124 example = literalExample ''
125 global = {
126 "debug log" = "syslog";
127 "access log" = "syslog";
128 "error log" = "syslog";
129 };
130 '';
131 };
132
133 enableAnalyticsReporting = mkOption {
134 type = types.bool;
135 default = false;
136 description = ''
137 Enable reporting of anonymous usage statistics to Netdata Inc. via either
138 Google Analytics (in versions prior to 1.29.4), or Netdata Inc.'s
139 self-hosted PostHog (in versions 1.29.4 and later).
140 See: <link xlink:href="https://learn.netdata.cloud/docs/agent/anonymous-statistics"/>
141 '';
142 };
143 };
144 };
145
146 config = mkIf cfg.enable {
147 assertions =
148 [ { assertion = cfg.config != {} -> cfg.configText == null ;
149 message = "Cannot specify both config and configText";
150 }
151 ];
152
153 systemd.services.netdata = {
154 description = "Real time performance monitoring";
155 after = [ "network.target" ];
156 wantedBy = [ "multi-user.target" ];
157 path = (with pkgs; [ curl gawk iproute2 which ])
158 ++ lib.optional cfg.python.enable (pkgs.python3.withPackages cfg.python.extraPackages)
159 ++ lib.optional config.virtualisation.libvirtd.enable (config.virtualisation.libvirtd.package);
160 environment = {
161 PYTHONPATH = "${cfg.package}/libexec/netdata/python.d/python_modules";
162 } // lib.optionalAttrs (!cfg.enableAnalyticsReporting) {
163 DO_NOT_TRACK = "1";
164 };
165 serviceConfig = {
166 ExecStart = "${cfg.package}/bin/netdata -P /run/netdata/netdata.pid -D -c ${configFile}";
167 ExecReload = "${pkgs.util-linux}/bin/kill -s HUP -s USR1 -s USR2 $MAINPID";
168 TimeoutStopSec = 60;
169 Restart = "on-failure";
170 # User and group
171 User = cfg.user;
172 Group = cfg.group;
173 # Performance
174 LimitNOFILE = "30000";
175 # Runtime directory and mode
176 RuntimeDirectory = "netdata";
177 RuntimeDirectoryMode = "0750";
178 # State directory and mode
179 StateDirectory = "netdata";
180 StateDirectoryMode = "0750";
181 # Cache directory and mode
182 CacheDirectory = "netdata";
183 CacheDirectoryMode = "0750";
184 # Logs directory and mode
185 LogsDirectory = "netdata";
186 LogsDirectoryMode = "0750";
187 # Configuration directory and mode
188 ConfigurationDirectory = "netdata";
189 ConfigurationDirectoryMode = "0755";
190 # Capabilities
191 CapabilityBoundingSet = [
192 "CAP_DAC_OVERRIDE" # is required for freeipmi and slabinfo plugins
193 "CAP_DAC_READ_SEARCH" # is required for apps plugin
194 "CAP_FOWNER" # is required for freeipmi plugin
195 "CAP_SETPCAP" # is required for apps, perf and slabinfo plugins
196 "CAP_SYS_ADMIN" # is required for perf plugin
197 "CAP_SYS_PTRACE" # is required for apps plugin
198 "CAP_SYS_RESOURCE" # is required for ebpf plugin
199 "CAP_NET_RAW" # is required for fping app
200 "CAP_SYS_CHROOT" # is required for cgroups plugin
201 "CAP_SETUID" # is required for cgroups and cgroups-network plugins
202 ];
203 # Sandboxing
204 ProtectSystem = "full";
205 ProtectHome = "read-only";
206 PrivateTmp = true;
207 ProtectControlGroups = true;
208 PrivateMounts = true;
209 };
210 };
211
212 systemd.enableCgroupAccounting = true;
213
214 security.wrappers."apps.plugin" = {
215 source = "${cfg.package}/libexec/netdata/plugins.d/apps.plugin.org";
216 capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
217 owner = cfg.user;
218 group = cfg.group;
219 permissions = "u+rx,g+x,o-rwx";
220 };
221
222 security.wrappers."cgroup-network" = {
223 source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org";
224 capabilities = "cap_setuid+ep";
225 owner = cfg.user;
226 group = cfg.group;
227 permissions = "u+rx,g+x,o-rwx";
228 };
229
230 security.wrappers."freeipmi.plugin" = {
231 source = "${cfg.package}/libexec/netdata/plugins.d/freeipmi.plugin.org";
232 capabilities = "cap_dac_override,cap_fowner+ep";
233 owner = cfg.user;
234 group = cfg.group;
235 permissions = "u+rx,g+x,o-rwx";
236 };
237
238 security.wrappers."perf.plugin" = {
239 source = "${cfg.package}/libexec/netdata/plugins.d/perf.plugin.org";
240 capabilities = "cap_sys_admin+ep";
241 owner = cfg.user;
242 group = cfg.group;
243 permissions = "u+rx,g+x,o-rwx";
244 };
245
246 security.wrappers."slabinfo.plugin" = {
247 source = "${cfg.package}/libexec/netdata/plugins.d/slabinfo.plugin.org";
248 capabilities = "cap_dac_override+ep";
249 owner = cfg.user;
250 group = cfg.group;
251 permissions = "u+rx,g+x,o-rwx";
252 };
253
254 security.pam.loginLimits = [
255 { domain = "netdata"; type = "soft"; item = "nofile"; value = "10000"; }
256 { domain = "netdata"; type = "hard"; item = "nofile"; value = "30000"; }
257 ];
258
259 users.users = optionalAttrs (cfg.user == defaultUser) {
260 ${defaultUser} = {
261 isSystemUser = true;
262 };
263 };
264
265 users.groups = optionalAttrs (cfg.group == defaultUser) {
266 ${defaultUser} = { };
267 };
268
269 };
270}