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