Merge #361424: refactor lib.packagesFromDirectoryRecursive (v2)
[NixPkgs.git] / nixos / modules / services / networking / 3proxy.nix
blobe15abb63aec493ada060b82afae2fc00a398da02
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
7 let
8   pkg = pkgs._3proxy;
9   cfg = config.services._3proxy;
10   optionalList = list: if list == [ ] then "*" else lib.concatMapStringsSep "," toString list;
13   options.services._3proxy = {
14     enable = lib.mkEnableOption "3proxy";
15     confFile = lib.mkOption {
16       type = lib.types.path;
17       example = "/var/lib/3proxy/3proxy.conf";
18       description = ''
19         Ignore all other 3proxy options and load configuration from this file.
20       '';
21     };
22     usersFile = lib.mkOption {
23       type = lib.types.nullOr lib.types.path;
24       default = null;
25       example = "/var/lib/3proxy/3proxy.passwd";
26       description = ''
27         Load users and passwords from this file.
29         Example users file with plain-text passwords:
31         ```
32           test1:CL:password1
33           test2:CL:password2
34         ```
36         Example users file with md5-crypted passwords:
38         ```
39           test1:CR:$1$tFkisVd2$1GA8JXkRmTXdLDytM/i3a1
40           test2:CR:$1$rkpibm5J$Aq1.9VtYAn0JrqZ8M.1ME.
41         ```
43         You can generate md5-crypted passwords via https://unix4lyfe.org/crypt/
44         Note that htpasswd tool generates incompatible md5-crypted passwords.
45         Consult [documentation](https://github.com/z3APA3A/3proxy/wiki/How-To-%28incomplete%29#USERS) for more information.
46       '';
47     };
48     services = lib.mkOption {
49       type = lib.types.listOf (
50         lib.types.submodule {
51           options = {
52             type = lib.mkOption {
53               type = lib.types.enum [
54                 "proxy"
55                 "socks"
56                 "pop3p"
57                 "ftppr"
58                 "admin"
59                 "dnspr"
60                 "tcppm"
61                 "udppm"
62               ];
63               example = "proxy";
64               description = ''
65                 Service type. The following values are valid:
67                 - `"proxy"`: HTTP/HTTPS proxy (default port 3128).
68                 - `"socks"`: SOCKS 4/4.5/5 proxy (default port 1080).
69                 - `"pop3p"`: POP3 proxy (default port 110).
70                 - `"ftppr"`: FTP proxy (default port 21).
71                 - `"admin"`: Web interface (default port 80).
72                 - `"dnspr"`: Caching DNS proxy (default port 53).
73                 - `"tcppm"`: TCP portmapper.
74                 - `"udppm"`: UDP portmapper.
75               '';
76             };
77             bindAddress = lib.mkOption {
78               type = lib.types.str;
79               default = "[::]";
80               example = "127.0.0.1";
81               description = ''
82                 Address used for service.
83               '';
84             };
85             bindPort = lib.mkOption {
86               type = lib.types.nullOr lib.types.int;
87               default = null;
88               example = 3128;
89               description = ''
90                 Override default port used for service.
91               '';
92             };
93             maxConnections = lib.mkOption {
94               type = lib.types.int;
95               default = 100;
96               example = 1000;
97               description = ''
98                 Maximum number of simulationeous connections to this service.
99               '';
100             };
101             auth = lib.mkOption {
102               type = lib.types.listOf (
103                 lib.types.enum [
104                   "none"
105                   "iponly"
106                   "strong"
107                 ]
108               );
109               example = [
110                 "iponly"
111                 "strong"
112               ];
113               description = ''
114                 Authentication type. The following values are valid:
116                 - `"none"`: disables both authentication and authorization. You can not use ACLs.
117                 - `"iponly"`: specifies no authentication. ACLs authorization is used.
118                 - `"strong"`: authentication by username/password. If user is not registered their access is denied regardless of ACLs.
120                 Double authentication is possible, e.g.
122                 ```
123                   {
124                     auth = [ "iponly" "strong" ];
125                     acl = [
126                       {
127                         rule = "allow";
128                         targets = [ "192.168.0.0/16" ];
129                       }
130                       {
131                         rule = "allow"
132                         users = [ "user1" "user2" ];
133                       }
134                     ];
135                   }
136                 ```
137                 In this example strong username authentication is not required to access 192.168.0.0/16.
138               '';
139             };
140             acl = lib.mkOption {
141               type = lib.types.listOf (
142                 lib.types.submodule {
143                   options = {
144                     rule = lib.mkOption {
145                       type = lib.types.enum [
146                         "allow"
147                         "deny"
148                       ];
149                       example = "allow";
150                       description = ''
151                         ACL rule. The following values are valid:
153                         - `"allow"`: connections allowed.
154                         - `"deny"`: connections not allowed.
155                       '';
156                     };
157                     users = lib.mkOption {
158                       type = lib.types.listOf lib.types.str;
159                       default = [ ];
160                       example = [
161                         "user1"
162                         "user2"
163                         "user3"
164                       ];
165                       description = ''
166                         List of users, use empty list for any.
167                       '';
168                     };
169                     sources = lib.mkOption {
170                       type = lib.types.listOf lib.types.str;
171                       default = [ ];
172                       example = [
173                         "127.0.0.1"
174                         "192.168.1.0/24"
175                       ];
176                       description = ''
177                         List of source IP range, use empty list for any.
178                       '';
179                     };
180                     targets = lib.mkOption {
181                       type = lib.types.listOf lib.types.str;
182                       default = [ ];
183                       example = [
184                         "127.0.0.1"
185                         "192.168.1.0/24"
186                       ];
187                       description = ''
188                         List of target IP ranges, use empty list for any.
189                         May also contain host names instead of addresses.
190                         It's possible to use wildmask in the beginning and in the the end of hostname, e.g. `*badsite.com` or `*badcontent*`.
191                         Hostname is only checked if hostname presents in request.
192                       '';
193                     };
194                     targetPorts = lib.mkOption {
195                       type = lib.types.listOf lib.types.int;
196                       default = [ ];
197                       example = [
198                         80
199                         443
200                       ];
201                       description = ''
202                         List of target ports, use empty list for any.
203                       '';
204                     };
205                   };
206                 }
207               );
208               default = [ ];
209               example = lib.literalExpression ''
210                 [
211                   {
212                     rule = "allow";
213                     users = [ "user1" ];
214                   }
215                   {
216                     rule = "allow";
217                     sources = [ "192.168.1.0/24" ];
218                   }
219                   {
220                     rule = "deny";
221                   }
222                 ]
223               '';
224               description = ''
225                 Use this option to limit user access to resources.
226               '';
227             };
228             extraArguments = lib.mkOption {
229               type = lib.types.nullOr lib.types.str;
230               default = null;
231               example = "-46";
232               description = ''
233                 Extra arguments for service.
234                 Consult "Options" section in [documentation](https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg) for available arguments.
235               '';
236             };
237             extraConfig = lib.mkOption {
238               type = lib.types.nullOr lib.types.lines;
239               default = null;
240               description = ''
241                 Extra configuration for service. Use this to configure things like bandwidth limiter or ACL-based redirection.
242                 Consult [documentation](https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg) for available options.
243               '';
244             };
245           };
246         }
247       );
248       default = [ ];
249       example = lib.literalExpression ''
250         [
251           {
252             type = "proxy";
253             bindAddress = "192.168.1.24";
254             bindPort = 3128;
255             auth = [ "none" ];
256           }
257           {
258             type = "proxy";
259             bindAddress = "10.10.1.20";
260             bindPort = 3128;
261             auth = [ "iponly" ];
262           }
263           {
264             type = "socks";
265             bindAddress = "172.17.0.1";
266             bindPort = 1080;
267             auth = [ "strong" ];
268           }
269         ]
270       '';
271       description = ''
272         Use this option to define 3proxy services.
273       '';
274     };
275     denyPrivate = lib.mkOption {
276       type = lib.types.bool;
277       default = true;
278       description = ''
279         Whether to deny access to private IP ranges including loopback.
280       '';
281     };
282     privateRanges = lib.mkOption {
283       type = lib.types.listOf lib.types.str;
284       default = [
285         "0.0.0.0/8"
286         "127.0.0.0/8"
287         "10.0.0.0/8"
288         "100.64.0.0/10"
289         "172.16.0.0/12"
290         "192.168.0.0/16"
291         "::"
292         "::1"
293         "fc00::/7"
294       ];
295       description = ''
296         What IP ranges to deny access when denyPrivate is set tu true.
297       '';
298     };
299     resolution = lib.mkOption {
300       type = lib.types.submodule {
301         options = {
302           nserver = lib.mkOption {
303             type = lib.types.listOf lib.types.str;
304             default = [ ];
305             example = [
306               "127.0.0.53"
307               "192.168.1.3:5353/tcp"
308             ];
309             description = ''
310               List of nameservers to use.
312               Up to 5 nservers may be specified. If no nserver is configured,
313               default system name resolution functions are used.
314             '';
315           };
316           nscache = lib.mkOption {
317             type = lib.types.int;
318             default = 65535;
319             description = "Set name cache size for IPv4.";
320           };
321           nscache6 = lib.mkOption {
322             type = lib.types.int;
323             default = 65535;
324             description = "Set name cache size for IPv6.";
325           };
326           nsrecord = lib.mkOption {
327             type = lib.types.attrsOf lib.types.str;
328             default = { };
329             example = lib.literalExpression ''
330               {
331                 "files.local" = "192.168.1.12";
332                 "site.local" = "192.168.1.43";
333               }
334             '';
335             description = "Adds static nsrecords.";
336           };
337         };
338       };
339       default = { };
340       description = ''
341         Use this option to configure name resolution and DNS caching.
342       '';
343     };
344     extraConfig = lib.mkOption {
345       type = lib.types.nullOr lib.types.lines;
346       default = null;
347       description = ''
348         Extra configuration, appended to the 3proxy configuration file.
349         Consult [documentation](https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg) for available options.
350       '';
351     };
352   };
354   config = lib.mkIf cfg.enable {
355     services._3proxy.confFile = lib.mkDefault (
356       pkgs.writeText "3proxy.conf" ''
357         # log to stdout
358         log
360         ${lib.concatMapStringsSep "\n" (x: "nserver " + x) cfg.resolution.nserver}
362         nscache ${toString cfg.resolution.nscache}
363         nscache6 ${toString cfg.resolution.nscache6}
365         ${lib.concatMapStringsSep "\n" (x: "nsrecord " + x) (
366           lib.mapAttrsToList (name: value: "${name} ${value}") cfg.resolution.nsrecord
367         )}
369         ${lib.optionalString (cfg.usersFile != null) ''users $"${cfg.usersFile}"''}
371         ${lib.concatMapStringsSep "\n" (service: ''
372           auth ${lib.concatStringsSep " " service.auth}
374           ${lib.optionalString (cfg.denyPrivate) "deny * * ${optionalList cfg.privateRanges}"}
376           ${lib.concatMapStringsSep "\n" (
377             acl:
378             "${acl.rule} ${
379               lib.concatMapStringsSep " " optionalList [
380                 acl.users
381                 acl.sources
382                 acl.targets
383                 acl.targetPorts
384               ]
385             }"
386           ) service.acl}
388           maxconn ${toString service.maxConnections}
390           ${lib.optionalString (service.extraConfig != null) service.extraConfig}
392           ${service.type} -i${toString service.bindAddress} ${
393             lib.optionalString (service.bindPort != null) "-p${toString service.bindPort}"
394           } ${lib.optionalString (service.extraArguments != null) service.extraArguments}
396           flush
397         '') cfg.services}
398         ${lib.optionalString (cfg.extraConfig != null) cfg.extraConfig}
399       ''
400     );
401     systemd.services."3proxy" = {
402       description = "Tiny free proxy server";
403       documentation = [ "https://github.com/z3APA3A/3proxy/wiki" ];
404       after = [ "network.target" ];
405       wantedBy = [ "multi-user.target" ];
406       serviceConfig = {
407         DynamicUser = true;
408         StateDirectory = "3proxy";
409         ExecStart = "${pkg}/bin/3proxy ${cfg.confFile}";
410         Restart = "on-failure";
411       };
412     };
413   };
415   meta.maintainers = with lib.maintainers; [ misuzu ];