1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 forEachInstance =
12 f:
13 flip mapAttrs' config.services.fcgiwrap.instances (
14 name: cfg: nameValuePair "fcgiwrap-${name}" (f cfg)
15 );
16
17in
18{
19 imports =
20 forEach
21 [
22 "enable"
23 "user"
24 "group"
25 "socketType"
26 "socketAddress"
27 "preforkProcesses"
28 ]
29 (
30 attr:
31 mkRemovedOptionModule [ "services" "fcgiwrap" attr ] ''
32 The global shared fcgiwrap instance is no longer supported due to
33 security issues.
34 Isolated instances should instead be configured through
35 `services.fcgiwrap.instances.*'.
36 ''
37 );
38
39 options.services.fcgiwrap.instances = mkOption {
40 description = "Configuration for fcgiwrap instances.";
41 default = { };
42 type = types.attrsOf (
43 types.submodule (
44 { config, ... }:
45 {
46 options = {
47 process.prefork = mkOption {
48 type = types.ints.positive;
49 default = 1;
50 description = "Number of processes to prefork.";
51 };
52
53 process.user = mkOption {
54 type = types.nullOr types.str;
55 default = null;
56 description = ''
57 User as which this instance of fcgiwrap will be run.
58 Set to `null` (the default) to use a dynamically allocated user.
59 '';
60 };
61
62 process.group = mkOption {
63 type = types.nullOr types.str;
64 default = null;
65 description = "Group as which this instance of fcgiwrap will be run.";
66 };
67
68 socket.type = mkOption {
69 type = types.enum [
70 "unix"
71 "tcp"
72 "tcp6"
73 ];
74 default = "unix";
75 description = "Socket type: 'unix', 'tcp' or 'tcp6'.";
76 };
77
78 socket.address = mkOption {
79 type = types.str;
80 default = "/run/fcgiwrap-${config._module.args.name}.sock";
81 example = "1.2.3.4:5678";
82 description = ''
83 Socket address.
84 In case of a UNIX socket, this should be its filesystem path.
85 '';
86 };
87
88 socket.user = mkOption {
89 type = types.nullOr types.str;
90 default = null;
91 description = ''
92 User to be set as owner of the UNIX socket.
93 '';
94 };
95
96 socket.group = mkOption {
97 type = types.nullOr types.str;
98 default = null;
99 description = ''
100 Group to be set as owner of the UNIX socket.
101 '';
102 };
103
104 socket.mode = mkOption {
105 type = types.nullOr types.str;
106 default = if config.socket.type == "unix" then "0600" else null;
107 defaultText = literalExpression ''
108 if config.socket.type == "unix" then "0600" else null
109 '';
110 description = ''
111 Mode to be set on the UNIX socket.
112 Defaults to private to the socket's owner.
113 '';
114 };
115 };
116 }
117 )
118 );
119 };
120
121 config = {
122 assertions = concatLists (
123 mapAttrsToList (name: cfg: [
124 {
125 assertion = cfg.socket.type == "unix" -> cfg.socket.user != null;
126 message = "Socket owner is required for the UNIX socket type.";
127 }
128 {
129 assertion = cfg.socket.type == "unix" -> cfg.socket.group != null;
130 message = "Socket owner is required for the UNIX socket type.";
131 }
132 {
133 assertion = cfg.socket.user != null -> cfg.socket.type == "unix";
134 message = "Socket owner can only be set for the UNIX socket type.";
135 }
136 {
137 assertion = cfg.socket.group != null -> cfg.socket.type == "unix";
138 message = "Socket owner can only be set for the UNIX socket type.";
139 }
140 {
141 assertion = cfg.socket.mode != null -> cfg.socket.type == "unix";
142 message = "Socket mode can only be set for the UNIX socket type.";
143 }
144 ]) config.services.fcgiwrap.instances
145 );
146
147 systemd.services = forEachInstance (cfg: {
148 after = [ "nss-user-lookup.target" ];
149 wantedBy = optional (cfg.socket.type != "unix") "multi-user.target";
150
151 serviceConfig =
152 {
153 ExecStart = ''
154 ${pkgs.fcgiwrap}/sbin/fcgiwrap ${
155 cli.toGNUCommandLineShell { } (
156 {
157 c = cfg.process.prefork;
158 }
159 // (optionalAttrs (cfg.socket.type != "unix") {
160 s = "${cfg.socket.type}:${cfg.socket.address}";
161 })
162 )
163 }
164 '';
165 }
166 // (
167 if cfg.process.user != null then
168 {
169 User = cfg.process.user;
170 Group = cfg.process.group;
171 }
172 else
173 {
174 DynamicUser = true;
175 }
176 );
177 });
178
179 systemd.sockets = forEachInstance (
180 cfg:
181 mkIf (cfg.socket.type == "unix") {
182 wantedBy = [ "sockets.target" ];
183 socketConfig = {
184 ListenStream = cfg.socket.address;
185 SocketUser = cfg.socket.user;
186 SocketGroup = cfg.socket.group;
187 SocketMode = cfg.socket.mode;
188 };
189 }
190 );
191 };
192}