diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 1b77a895d71..e127782e85f 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -171,6 +171,9 @@ with lib;
The starting time can be configured via services.postgresqlBackup.startAt.
'')
+ # zabbixServer
+ (mkRenamedOptionModule [ "services" "zabbixServer" "dbServer" ] [ "services" "zabbixServer" "database" "host" ])
+
# Profile splitting
(mkRenamedOptionModule [ "virtualisation" "growPartition" ] [ "boot" "growPartition" ])
@@ -214,6 +217,7 @@ with lib;
(mkRemovedOptionModule [ "services" "winstone" ] "The corresponding package was removed from nixpkgs.")
(mkRemovedOptionModule [ "services" "mysql" "pidDir" ] "Don't wait for pidfiles, describe dependencies through systemd")
(mkRemovedOptionModule [ "services" "mysql" "rootPassword" ] "Use socket authentication or set the password outside of the nix store.")
+ (mkRemovedOptionModule [ "services" "zabbixServer" "dbPassword" ] "Use services.zabbixServer.database.passwordFile instead.")
# ZSH
(mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
diff --git a/nixos/modules/services/monitoring/zabbix-agent.nix b/nixos/modules/services/monitoring/zabbix-agent.nix
index 0519e7c2ad6..b1645f86110 100644
--- a/nixos/modules/services/monitoring/zabbix-agent.nix
+++ b/nixos/modules/services/monitoring/zabbix-agent.nix
@@ -1,73 +1,118 @@
-# Zabbix agent daemon.
{ config, lib, pkgs, ... }:
-with lib;
-
let
-
cfg = config.services.zabbixAgent;
- zabbix = cfg.package;
+ inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+ inherit (lib) attrValues concatMapStringsSep literalExample optionalString types;
- stateDir = "/run/zabbix";
+ user = "zabbix-agent";
+ group = "zabbix-agent";
- logDir = "/var/log/zabbix";
+ moduleEnv = pkgs.symlinkJoin {
+ name = "zabbix-agent-module-env";
+ paths = attrValues cfg.modules;
+ };
- pidFile = "${stateDir}/zabbix_agentd.pid";
-
- configFile = pkgs.writeText "zabbix_agentd.conf"
- ''
- Server = ${cfg.server}
-
- LogFile = ${logDir}/zabbix_agentd
-
- PidFile = ${pidFile}
-
- StartAgents = 1
-
- ${config.services.zabbixAgent.extraConfig}
- '';
+ configFile = pkgs.writeText "zabbix_agent.conf" ''
+ LogType = console
+ Server = ${cfg.server}
+ ListenIP = ${cfg.listen.ip}
+ ListenPort = ${toString cfg.listen.port}
+ ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+ ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+ ${cfg.extraConfig}
+ '';
in
{
-
- ###### interface
+ # interface
options = {
services.zabbixAgent = {
+ enable = mkEnableOption "the Zabbix Agent";
- enable = mkOption {
- default = false;
+ package = mkOption {
+ type = types.package;
+ default = pkgs.zabbix.agent;
+ defaultText = "pkgs.zabbix.agent";
+ description = "The Zabbix package to use.";
+ };
+
+ extraPackages = mkOption {
+ type = types.listOf types.package;
+ default = with pkgs; [ nettools ];
+ defaultText = "[ nettools ]";
+ example = "[ nettools mysql ]";
description = ''
- Whether to run the Zabbix monitoring agent on this machine.
- It will send monitoring data to a Zabbix server.
+ Packages to be added to the Zabbix PATH.
+ Typically used to add executables for scripts, but can be anything.
'';
};
- package = mkOption {
- type = types.attrs; # Note: pkgs.zabbixXY isn't a derivation, but an attrset of { server = ...; agent = ...; }.
- default = pkgs.zabbix;
- defaultText = "pkgs.zabbix";
- example = literalExample "pkgs.zabbix34";
- description = ''
- The Zabbix package to use.
+ modules = mkOption {
+ type = types.attrsOf types.package;
+ description = "A set of modules to load.";
+ default = {};
+ example = literalExample ''
+ {
+ "dummy.so" = pkgs.stdenv.mkDerivation {
+ name = "zabbix-dummy-module-''${cfg.package.version}";
+ src = cfg.package.src;
+ buildInputs = [ cfg.package ];
+ sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+ installPhase = '''
+ mkdir -p $out/lib
+ cp dummy.so $out/lib/
+ ''';
+ };
+ }
'';
};
server = mkOption {
- default = "127.0.0.1";
+ type = types.str;
description = ''
The IP address or hostname of the Zabbix server to connect to.
'';
};
+ listen = {
+ ip = mkOption {
+ type = types.str;
+ default = "0.0.0.0";
+ description = ''
+ List of comma delimited IP addresses that the agent should listen on.
+ '';
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 10050;
+ description = ''
+ Agent will listen on this port for connections from the server.
+ '';
+ };
+ };
+
+ openFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Open ports in the firewall for the Zabbix Agent.
+ '';
+ };
+
+ # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
extraConfig = mkOption {
default = "";
type = types.lines;
description = ''
- Configuration that is injected verbatim into the configuration file.
+ Configuration that is injected verbatim into the configuration file. Refer to
+
+ for details on supported values.
'';
};
@@ -75,38 +120,38 @@ in
};
-
- ###### implementation
+ # implementation
config = mkIf cfg.enable {
- users.users = mkIf (!config.services.zabbixServer.enable) (singleton
- { name = "zabbix";
- uid = config.ids.uids.zabbix;
- description = "Zabbix daemon user";
- });
+ networking.firewall = mkIf cfg.openFirewall {
+ allowedTCPPorts = [ cfg.listen.port ];
+ };
- systemd.services."zabbix-agent" =
- { description = "Zabbix Agent";
+ users.users.${user} = {
+ description = "Zabbix Agent daemon user";
+ inherit group;
+ };
- wantedBy = [ "multi-user.target" ];
+ users.groups.${group} = { };
- path = [ pkgs.nettools ];
+ systemd.services."zabbix-agent" = {
+ description = "Zabbix Agent";
- preStart =
- ''
- mkdir -m 0755 -p ${stateDir} ${logDir}
- chown zabbix ${stateDir} ${logDir}
- '';
+ wantedBy = [ "multi-user.target" ];
- serviceConfig.ExecStart = "@${zabbix.agent}/sbin/zabbix_agentd zabbix_agentd --config ${configFile}";
- serviceConfig.Type = "forking";
- serviceConfig.RemainAfterExit = true;
- serviceConfig.Restart = "always";
- serviceConfig.RestartSec = 2;
+ path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+
+ serviceConfig = {
+ ExecStart = "@${cfg.package}/sbin/zabbix_agentd zabbix_agentd -f --config ${configFile}";
+ Restart = "always";
+ RestartSec = 2;
+
+ User = user;
+ Group = group;
+ PrivateTmp = true;
};
-
- environment.systemPackages = [ zabbix.agent ];
+ };
};
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
index 823145d8b90..11311b466c3 100644
--- a/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -1,147 +1,293 @@
-# Zabbix server daemon.
{ config, lib, pkgs, ... }:
-with lib;
-
let
-
cfg = config.services.zabbixServer;
+ pgsql = config.services.postgresql;
+ mysql = config.services.mysql;
- stateDir = "/run/zabbix";
+ inherit (lib) mkDefault mkEnableOption mkIf mkOption;
+ inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
- logDir = "/var/log/zabbix";
+ user = "zabbix";
+ group = "zabbix";
+ runtimeDir = "/run/zabbix";
+ stateDir = "/var/lib/zabbix";
+ passwordFile = "${runtimeDir}/zabbix-dbpassword.conf";
- libDir = "/var/lib/zabbix";
+ moduleEnv = pkgs.symlinkJoin {
+ name = "zabbix-server-module-env";
+ paths = attrValues cfg.modules;
+ };
- pidFile = "${stateDir}/zabbix_server.pid";
+ configFile = pkgs.writeText "zabbix_server.conf" ''
+ LogType = console
+ ListenIP = ${cfg.listen.ip}
+ ListenPort = ${toString cfg.listen.port}
+ # TODO: set to cfg.database.socket if database type is pgsql?
+ DBHost = ${optionalString (cfg.database.createLocally != true) cfg.database.host}
+ ${optionalString (cfg.database.createLocally != true) "DBPort = ${cfg.database.port}"}
+ DBName = ${cfg.database.name}
+ DBUser = ${cfg.database.user}
+ ${optionalString (cfg.database.passwordFile != null) "Include ${passwordFile}"}
+ ${optionalString (mysqlLocal && cfg.database.socket != null) "DBSocket = ${cfg.database.socket}"}
+ SocketDir = ${runtimeDir}
+ FpingLocation = /run/wrappers/bin/fping
+ ${optionalString (cfg.modules != {}) "LoadModulePath = ${moduleEnv}/lib"}
+ ${concatMapStringsSep "\n" (name: "LoadModule = ${name}") (builtins.attrNames cfg.modules)}
+ ${cfg.extraConfig}
+ '';
- configFile = pkgs.writeText "zabbix_server.conf"
- ''
- ListenPort = ${cfg.listenPort}
-
- LogFile = ${logDir}/zabbix_server
-
- PidFile = ${pidFile}
-
- ${optionalString (cfg.dbServer != "localhost") ''
- DBHost = ${cfg.dbServer}
- ''}
-
- DBName = ${cfg.dbName}
-
- DBUser = ${cfg.dbUser}
-
- DBPort = ${cfg.dbPort}
-
- DBPassword = ${cfg.dbPassword}
-
- ${config.services.zabbixServer.extraConfig}
- '';
-
- useLocalMysql = cfg.dbServer == "localhost" || cfg.dbServer == "";
+ mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
+ pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
in
{
-
- ###### interface
+ # interface
options = {
- services.zabbixServer.enable = mkOption {
- default = false;
- type = types.bool;
- description = ''
- Whether to run the Zabbix server on this machine.
- '';
+ services.zabbixServer = {
+ enable = mkEnableOption "the Zabbix Server";
+
+ package = mkOption {
+ type = types.package;
+ default = if cfg.database.type == "mysql" then pkgs.zabbix.server-mysql else pkgs.zabbix.server-pgsql;
+ defaultText = "pkgs.zabbix.server-pgsql";
+ description = "The Zabbix package to use.";
+ };
+
+ extraPackages = mkOption {
+ type = types.listOf types.package;
+ default = with pkgs; [ nettools nmap traceroute ];
+ defaultText = "[ nettools nmap traceroute ]";
+ description = ''
+ Packages to be added to the Zabbix PATH.
+ Typically used to add executables for scripts, but can be anything.
+ '';
+ };
+
+ modules = mkOption {
+ type = types.attrsOf types.package;
+ description = "A set of modules to load.";
+ default = {};
+ example = literalExample ''
+ {
+ "dummy.so" = pkgs.stdenv.mkDerivation {
+ name = "zabbix-dummy-module-''${cfg.package.version}";
+ src = cfg.package.src;
+ buildInputs = [ cfg.package ];
+ sourceRoot = "zabbix-''${cfg.package.version}/src/modules/dummy";
+ installPhase = '''
+ mkdir -p $out/lib
+ cp dummy.so $out/lib/
+ ''';
+ };
+ }
+ '';
+ };
+
+ database = {
+ type = mkOption {
+ type = types.enum [ "mysql" "pgsql" ];
+ example = "mysql";
+ default = "pgsql";
+ description = "Database engine to use.";
+ };
+
+ host = mkOption {
+ type = types.str;
+ default = "localhost";
+ description = "Database host address.";
+ };
+
+ port = mkOption {
+ type = types.int;
+ default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
+ description = "Database host port.";
+ };
+
+ name = mkOption {
+ type = types.str;
+ default = "zabbix";
+ description = "Database name.";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "zabbix";
+ description = "Database user.";
+ };
+
+ passwordFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/run/keys/zabbix-dbpassword";
+ description = ''
+ A file containing the password corresponding to
+ .
+ '';
+ };
+
+ socket = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/run/postgresql";
+ description = "Path to the unix socket file to use for authentication.";
+ };
+
+ createLocally = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Whether to create a local database automatically.";
+ };
+ };
+
+ listen = {
+ ip = mkOption {
+ type = types.str;
+ default = "0.0.0.0";
+ description = ''
+ List of comma delimited IP addresses that the trapper should listen on.
+ Trapper will listen on all network interfaces if this parameter is missing.
+ '';
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 10051;
+ description = ''
+ Listen port for trapper.
+ '';
+ };
+ };
+
+ openFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Open ports in the firewall for the Zabbix Server.
+ '';
+ };
+
+ # TODO: for bonus points migrate this to https://github.com/NixOS/rfcs/pull/42
+ extraConfig = mkOption {
+ default = "";
+ type = types.lines;
+ description = ''
+ Configuration that is injected verbatim into the configuration file. Refer to
+
+ for details on supported values.
+ '';
+ };
+
};
- services.zabbixServer.dbServer = mkOption {
- default = "localhost";
- type = types.str;
- description = ''
- Hostname or IP address of the database server.
- Use an empty string ("") to use peer authentication.
- '';
- };
-
- services.zabbixServer.dbPassword = mkOption {
- type = types.str;
- description = "Password used to connect to the database server.";
- };
-
- services.zabbixServer.dbUser = mkOption {
- default = "zabbix";
- type = types.str;
- description = "User used to connect to the database server.";
- };
-
- services.zabbixServer.dbPort = mkOption {
- default = "3306";
- type = types.str;
- description = "Port used to connect to the database server.";
- };
-
- services.zabbixServer.dbName = mkOption {
- default = "zabbix";
- type = types.str;
- description = "Port used to connect to the database server.";
- };
-
- services.zabbixServer.listenPort = mkOption {
- default = "10051";
- type = types.str;
- description = "Port used to listen to the agent.";
- };
-
- services.zabbixServer.extraConfig = mkOption {
- default = "";
- type = types.lines;
- description = ''
- Configuration that is injected verbatim into the configuration file.
- '';
- };
};
- ###### implementation
+ # implementation
config = mkIf cfg.enable {
- services.mysql.enable = useLocalMysql;
- services.mysql.package = pkgs.mysql;
+ assertions = [
+ { assertion = cfg.database.createLocally -> cfg.database.user == user;
+ message = "services.zabbixServer.database.user must be set to ${user} if services.zabbixServer.database.createLocally is set true";
+ }
+ { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
+ message = "a password cannot be specified if services.zabbixServer.database.createLocally is set to true";
+ }
+ ];
- users.users = singleton
- { name = "zabbix";
- uid = config.ids.uids.zabbix;
- description = "Zabbix daemon user";
+ networking.firewall = mkIf cfg.openFirewall {
+ allowedTCPPorts = [ cfg.listen.port ];
+ };
+
+ services.mysql = optionalAttrs mysqlLocal {
+ enable = true;
+ package = mkDefault pkgs.mariadb;
+ ensureDatabases = [ cfg.database.name ];
+ ensureUsers = [
+ { name = cfg.database.user;
+ ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+ }
+ ];
+ };
+
+ services.postgresql = optionalAttrs pgsqlLocal {
+ enable = true;
+ ensureDatabases = [ cfg.database.name ];
+ ensureUsers = [
+ { name = cfg.database.user;
+ ensurePermissions = { "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES"; };
+ }
+ ];
+ };
+
+ users.users.${user} = {
+ description = "Zabbix daemon user";
+ uid = config.ids.uids.zabbix;
+ inherit group;
+ };
+
+ users.groups.${group} = {
+ gid = config.ids.gids.zabbix;
+ };
+
+ security.wrappers = {
+ fping.source = "${pkgs.fping}/bin/fping";
+ };
+
+ systemd.services."zabbix-server" = {
+ description = "Zabbix Server";
+
+ wantedBy = [ "multi-user.target" ];
+ after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+
+ path = [ "/run/wrappers" ] ++ cfg.extraPackages;
+ preStart = ''
+ # pre 19.09 compatibility
+ if test -e "${runtimeDir}/db-created"; then
+ mv "${runtimeDir}/db-created" "${stateDir}/"
+ fi
+ '' + optionalString pgsqlLocal ''
+ if ! test -e "${stateDir}/db-created"; then
+ cat ${cfg.package}/share/zabbix/database/postgresql/schema.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+ cat ${cfg.package}/share/zabbix/database/postgresql/images.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+ cat ${cfg.package}/share/zabbix/database/postgresql/data.sql | ${pgsql.package}/bin/psql ${cfg.database.name}
+ touch "${stateDir}/db-created"
+ fi
+ '' + optionalString mysqlLocal ''
+ if ! test -e "${stateDir}/db-created"; then
+ cat ${cfg.package}/share/zabbix/database/mysql/schema.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+ cat ${cfg.package}/share/zabbix/database/mysql/images.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+ cat ${cfg.package}/share/zabbix/database/mysql/data.sql | ${mysql.package}/bin/mysql ${cfg.database.name}
+ touch "${stateDir}/db-created"
+ fi
+ '' + optionalString (cfg.database.passwordFile != null) ''
+ # create a copy of the supplied password file in a format zabbix can consume
+ touch ${passwordFile}
+ chmod 0600 ${passwordFile}
+ echo -n "DBPassword = " > ${passwordFile}
+ cat ${cfg.database.passwordFile} >> ${passwordFile}
+ '';
+
+ serviceConfig = {
+ ExecStart = "@${cfg.package}/sbin/zabbix_server zabbix_server -f --config ${configFile}";
+ Restart = "always";
+ RestartSec = 2;
+
+ User = user;
+ Group = group;
+ RuntimeDirectory = "zabbix";
+ StateDirectory = "zabbix";
+ PrivateTmp = true;
};
+ };
- systemd.services."zabbix-server" =
- { description = "Zabbix Server";
+ systemd.services.httpd.after =
+ optional (config.services.zabbixWeb.enable && mysqlLocal) "mysql.service" ++
+ optional (config.services.zabbixWeb.enable && pgsqlLocal) "postgresql.service";
- wantedBy = [ "multi-user.target" ];
- after = optional useLocalMysql "mysql.service";
-
- preStart =
- ''
- mkdir -m 0755 -p ${stateDir} ${logDir} ${libDir}
- chown zabbix ${stateDir} ${logDir} ${libDir}
- ${lib.optionalString (useLocalMysql) ''
- if ! test -e "${libDir}/db-created"; then
- ${pkgs.sudo}/bin/sudo -u ${config.services.mysql.user} ${pkgs.mysql}/bin/mysql -uroot -e 'CREATE DATABASE ${cfg.dbName}'
- ${pkgs.sudo}/bin/sudo -u ${config.services.mysql.user} ${pkgs.mysql}/bin/mysql -uroot -e "GRANT ALL ON ${cfg.dbName}.* TO ${cfg.dbUser}@localhost IDENTIFIED BY \"${cfg.dbPassword}\";"
- cat ${pkgs.zabbix.server}/share/zabbix/db/schema/mysql.sql | ${pkgs.sudo}/bin/sudo -u zabbix ${pkgs.mysql}/bin/mysql -u${cfg.dbUser} -p${cfg.dbPassword} ${cfg.dbName}
- cat ${pkgs.zabbix.server}/share/zabbix/db/data/images.sql | ${pkgs.sudo}/bin/sudo -u zabbix ${pkgs.mysql}/bin/mysql -u${cfg.dbUser} -p${cfg.dbPassword} ${cfg.dbName}
- cat ${pkgs.zabbix.server}/share/zabbix/db/data/data.sql | ${pkgs.sudo}/bin/sudo -u zabbix ${pkgs.mysql}/bin/mysql -u${cfg.dbUser} -p${cfg.dbPassword} ${cfg.dbName}
- touch "${libDir}/db-created"
- fi''}
- '';
-
- path = [ pkgs.nettools ];
-
- serviceConfig.ExecStart = "${pkgs.zabbix.server}/sbin/zabbix_server --config ${configFile}";
- serviceConfig.Type = "forking";
- serviceConfig.PIDFile = pidFile;
- };
};
+
}