291 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ config, lib, pkgs, ... }:
 | 
						|
 | 
						|
let
 | 
						|
  cfg = config.services.zabbixProxy;
 | 
						|
  pgsql = config.services.postgresql;
 | 
						|
  mysql = config.services.mysql;
 | 
						|
 | 
						|
  inherit (lib) mkDefault mkEnableOption mkIf mkOption;
 | 
						|
  inherit (lib) attrValues concatMapStringsSep literalExample optional optionalAttrs optionalString types;
 | 
						|
 | 
						|
  user = "zabbix";
 | 
						|
  group = "zabbix";
 | 
						|
  runtimeDir = "/run/zabbix";
 | 
						|
  stateDir = "/var/lib/zabbix";
 | 
						|
  passwordFile = "${runtimeDir}/zabbix-dbpassword.conf";
 | 
						|
 | 
						|
  moduleEnv = pkgs.symlinkJoin {
 | 
						|
    name = "zabbix-proxy-module-env";
 | 
						|
    paths = attrValues cfg.modules;
 | 
						|
  };
 | 
						|
 | 
						|
  configFile = pkgs.writeText "zabbix_proxy.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}
 | 
						|
  '';
 | 
						|
 | 
						|
  mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
 | 
						|
  pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 | 
						|
 | 
						|
in
 | 
						|
 | 
						|
{
 | 
						|
  # interface
 | 
						|
 | 
						|
  options = {
 | 
						|
 | 
						|
    services.zabbixProxy = {
 | 
						|
      enable = mkEnableOption "the Zabbix Proxy";
 | 
						|
 | 
						|
      package = mkOption {
 | 
						|
        type = types.package;
 | 
						|
        default =
 | 
						|
          if cfg.database.type == "mysql" then pkgs.zabbix.proxy-mysql
 | 
						|
          else if cfg.database.type == "pgsql" then pkgs.zabbix.proxy-pgsql
 | 
						|
          else pkgs.zabbix.proxy-sqlite;
 | 
						|
        defaultText = "pkgs.zabbix.proxy-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 <envar>PATH</envar>.
 | 
						|
          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" "sqlite" ];
 | 
						|
          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
 | 
						|
            <option>database.user</option>.
 | 
						|
          '';
 | 
						|
        };
 | 
						|
 | 
						|
        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 Proxy.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
      # 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
 | 
						|
          <link xlink:href="https://www.zabbix.com/documentation/current/manual/appendix/config/zabbix_proxy"/>
 | 
						|
          for details on supported values.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
    };
 | 
						|
 | 
						|
  };
 | 
						|
 | 
						|
  # implementation
 | 
						|
 | 
						|
  config = mkIf cfg.enable {
 | 
						|
 | 
						|
    assertions = [
 | 
						|
      { assertion = !config.services.zabbixServer.enable;
 | 
						|
        message = "Please choose one of services.zabbixServer or services.zabbixProxy.";
 | 
						|
      }
 | 
						|
      { assertion = cfg.database.createLocally -> cfg.database.user == user;
 | 
						|
        message = "services.zabbixProxy.database.user must be set to ${user} if services.zabbixProxy.database.createLocally is set true";
 | 
						|
      }
 | 
						|
      { assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
 | 
						|
        message = "a password cannot be specified if services.zabbixProxy.database.createLocally is set to true";
 | 
						|
      }
 | 
						|
    ];
 | 
						|
 | 
						|
    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-proxy" = {
 | 
						|
      description = "Zabbix Proxy";
 | 
						|
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
 | 
						|
 | 
						|
      path = [ "/run/wrappers" ] ++ cfg.extraPackages;
 | 
						|
      preStart = 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_proxy zabbix_proxy -f --config ${configFile}";
 | 
						|
        Restart = "always";
 | 
						|
        RestartSec = 2;
 | 
						|
 | 
						|
        User = user;
 | 
						|
        Group = group;
 | 
						|
        RuntimeDirectory = "zabbix";
 | 
						|
        StateDirectory = "zabbix";
 | 
						|
        PrivateTmp = true;
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
  };
 | 
						|
 | 
						|
}
 |