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