1{ config, lib, options, pkgs, ... }:
2
3let
4
5 inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types;
6 inherit (lib) literalExpression mapAttrs optionalString versionAtLeast;
7
8 cfg = config.services.zabbixWeb;
9 opt = options.services.zabbixWeb;
10 fpm = config.services.phpfpm.pools.zabbix;
11
12 user = "zabbix";
13 group = "zabbix";
14 stateDir = "/var/lib/zabbix";
15
16 zabbixConfig = pkgs.writeText "zabbix.conf.php" ''
17 <?php
18 // Zabbix GUI configuration file.
19 global $DB;
20 $DB['TYPE'] = '${ { mysql = "MYSQL"; pgsql = "POSTGRESQL"; oracle = "ORACLE"; }.${cfg.database.type} }';
21 $DB['SERVER'] = '${cfg.database.host}';
22 $DB['PORT'] = '${toString cfg.database.port}';
23 $DB['DATABASE'] = '${cfg.database.name}';
24 $DB['USER'] = '${cfg.database.user}';
25 # NOTE: file_get_contents adds newline at the end of returned string
26 $DB['PASSWORD'] = ${if cfg.database.passwordFile != null then "trim(file_get_contents('${cfg.database.passwordFile}'), \"\\r\\n\")" else "''"};
27 // Schema name. Used for IBM DB2 and PostgreSQL.
28 $DB['SCHEMA'] = ''';
29 $ZBX_SERVER = '${cfg.server.address}';
30 $ZBX_SERVER_PORT = '${toString cfg.server.port}';
31 $ZBX_SERVER_NAME = ''';
32 $IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG;
33
34 ${cfg.extraConfig}
35 '';
36
37in
38{
39 # interface
40
41 options.services = {
42 zabbixWeb = {
43 enable = mkEnableOption (lib.mdDoc "the Zabbix web interface");
44
45 package = mkOption {
46 type = types.package;
47 default = pkgs.zabbix.web;
48 defaultText = literalExpression "zabbix.web";
49 description = lib.mdDoc "Which Zabbix package to use.";
50 };
51
52 server = {
53 port = mkOption {
54 type = types.port;
55 description = lib.mdDoc "The port of the Zabbix server to connect to.";
56 default = 10051;
57 };
58
59 address = mkOption {
60 type = types.str;
61 description = lib.mdDoc "The IP address or hostname of the Zabbix server to connect to.";
62 default = "localhost";
63 };
64 };
65
66 database = {
67 type = mkOption {
68 type = types.enum [ "mysql" "pgsql" "oracle" ];
69 example = "mysql";
70 default = "pgsql";
71 description = lib.mdDoc "Database engine to use.";
72 };
73
74 host = mkOption {
75 type = types.str;
76 default = "";
77 description = lib.mdDoc "Database host address.";
78 };
79
80 port = mkOption {
81 type = types.port;
82 default =
83 if cfg.database.type == "mysql" then config.services.mysql.port
84 else if cfg.database.type == "pgsql" then config.services.postgresql.port
85 else 1521;
86 defaultText = literalExpression ''
87 if config.${opt.database.type} == "mysql" then config.${options.services.mysql.port}
88 else if config.${opt.database.type} == "pgsql" then config.${options.services.postgresql.port}
89 else 1521
90 '';
91 description = lib.mdDoc "Database host port.";
92 };
93
94 name = mkOption {
95 type = types.str;
96 default = "zabbix";
97 description = lib.mdDoc "Database name.";
98 };
99
100 user = mkOption {
101 type = types.str;
102 default = "zabbix";
103 description = lib.mdDoc "Database user.";
104 };
105
106 passwordFile = mkOption {
107 type = types.nullOr types.path;
108 default = null;
109 example = "/run/keys/zabbix-dbpassword";
110 description = lib.mdDoc ''
111 A file containing the password corresponding to
112 {option}`database.user`.
113 '';
114 };
115
116 socket = mkOption {
117 type = types.nullOr types.path;
118 default = null;
119 example = "/run/postgresql";
120 description = lib.mdDoc "Path to the unix socket file to use for authentication.";
121 };
122 };
123
124 virtualHost = mkOption {
125 type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
126 example = literalExpression ''
127 {
128 hostName = "zabbix.example.org";
129 adminAddr = "webmaster@example.org";
130 forceSSL = true;
131 enableACME = true;
132 }
133 '';
134 description = lib.mdDoc ''
135 Apache configuration can be done by adapting `services.httpd.virtualHosts.<name>`.
136 See [](#opt-services.httpd.virtualHosts) for further information.
137 '';
138 };
139
140 poolConfig = mkOption {
141 type = with types; attrsOf (oneOf [ str int bool ]);
142 default = {
143 "pm" = "dynamic";
144 "pm.max_children" = 32;
145 "pm.start_servers" = 2;
146 "pm.min_spare_servers" = 2;
147 "pm.max_spare_servers" = 4;
148 "pm.max_requests" = 500;
149 };
150 description = lib.mdDoc ''
151 Options for the Zabbix PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
152 '';
153 };
154
155 extraConfig = mkOption {
156 type = types.lines;
157 default = "";
158 description = lib.mdDoc ''
159 Additional configuration to be copied verbatim into {file}`zabbix.conf.php`.
160 '';
161 };
162
163 };
164 };
165
166 # implementation
167
168 config = mkIf cfg.enable {
169
170 services.zabbixWeb.extraConfig = optionalString ((versionAtLeast config.system.stateVersion "20.09") && (versionAtLeast cfg.package.version "5.0.0")) ''
171 $DB['DOUBLE_IEEE754'] = 'true';
172 '';
173
174 systemd.tmpfiles.rules = [
175 "d '${stateDir}' 0750 ${user} ${group} - -"
176 "d '${stateDir}/session' 0750 ${user} ${config.services.httpd.group} - -"
177 ];
178
179 services.phpfpm.pools.zabbix = {
180 inherit user;
181 group = config.services.httpd.group;
182 phpOptions = ''
183 # https://www.zabbix.com/documentation/current/manual/installation/install
184 memory_limit = 128M
185 post_max_size = 16M
186 upload_max_filesize = 2M
187 max_execution_time = 300
188 max_input_time = 300
189 session.auto_start = 0
190 mbstring.func_overload = 0
191 always_populate_raw_post_data = -1
192 # https://bbs.archlinux.org/viewtopic.php?pid=1745214#p1745214
193 session.save_path = ${stateDir}/session
194 '' + optionalString (config.time.timeZone != null) ''
195 date.timezone = "${config.time.timeZone}"
196 '' + optionalString (cfg.database.type == "oracle") ''
197 extension=${pkgs.phpPackages.oci8}/lib/php/extensions/oci8.so
198 '';
199 phpEnv.ZABBIX_CONFIG = "${zabbixConfig}";
200 settings = {
201 "listen.owner" = config.services.httpd.user;
202 "listen.group" = config.services.httpd.group;
203 } // cfg.poolConfig;
204 };
205
206 services.httpd = {
207 enable = true;
208 adminAddr = mkDefault cfg.virtualHost.adminAddr;
209 extraModules = [ "proxy_fcgi" ];
210 virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
211 documentRoot = mkForce "${cfg.package}/share/zabbix";
212 extraConfig = ''
213 <Directory "${cfg.package}/share/zabbix">
214 <FilesMatch "\.php$">
215 <If "-f %{REQUEST_FILENAME}">
216 SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
217 </If>
218 </FilesMatch>
219 AllowOverride all
220 Options -Indexes
221 DirectoryIndex index.php
222 </Directory>
223 '';
224 } ];
225 };
226
227 users.users.${user} = mapAttrs (name: mkDefault) {
228 description = "Zabbix daemon user";
229 uid = config.ids.uids.zabbix;
230 inherit group;
231 };
232
233 users.groups.${group} = mapAttrs (name: mkDefault) {
234 gid = config.ids.gids.zabbix;
235 };
236
237 };
238}