python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / networking / redsocks.nix
blob85ae3125ded8060e27333e3bbf15a4122349b449
1 { config, lib, pkgs, ... }:
3 with lib;
4 let
5   cfg = config.services.redsocks;
6 in
8   ##### interface
9   options = {
10     services.redsocks = {
11       enable = mkOption {
12         type = types.bool;
13         default = false;
14         description = lib.mdDoc "Whether to enable redsocks.";
15       };
17       log_debug = mkOption {
18         type = types.bool;
19         default = false;
20         description = lib.mdDoc "Log connection progress.";
21       };
23       log_info = mkOption {
24         type = types.bool;
25         default = false;
26         description = lib.mdDoc "Log start and end of client sessions.";
27       };
29       log = mkOption {
30         type = types.str;
31         default = "stderr";
32         description =
33           lib.mdDoc ''
34             Where to send logs.
36             Possible values are:
37               - stderr
38               - file:/path/to/file
39               - syslog:FACILITY where FACILITY is any of "daemon", "local0",
40               etc.
41           '';
42       };
44       chroot = mkOption {
45         type = with types; nullOr str;
46         default = null;
47         description =
48           lib.mdDoc ''
49             Chroot under which to run redsocks. Log file is opened before
50             chroot, but if logging to syslog /etc/localtime may be required.
51           '';
52       };
54       redsocks = mkOption {
55         description =
56           lib.mdDoc ''
57             Local port to proxy associations to be performed.
59             The example shows how to configure a proxy to handle port 80 as HTTP
60             relay, and all other ports as HTTP connect.
61           '';
62         example = [
63           { port = 23456; proxy = "1.2.3.4:8080"; type = "http-relay";
64             redirectCondition = "--dport 80";
65             doNotRedirect = [ "-d 1.2.0.0/16" ];
66           }
67           { port = 23457; proxy = "1.2.3.4:8080"; type = "http-connect";
68             redirectCondition = true;
69             doNotRedirect = [ "-d 1.2.0.0/16" ];
70           }
71         ];
72         type = types.listOf (types.submodule { options = {
73           ip = mkOption {
74             type = types.str;
75             default = "127.0.0.1";
76             description =
77               lib.mdDoc ''
78                 IP on which redsocks should listen. Defaults to 127.0.0.1 for
79                 security reasons.
80               '';
81           };
83           port = mkOption {
84             type = types.int;
85             default = 12345;
86             description = lib.mdDoc "Port on which redsocks should listen.";
87           };
89           proxy = mkOption {
90             type = types.str;
91             description =
92               lib.mdDoc ''
93                 Proxy through which redsocks should forward incoming traffic.
94                 Example: "example.org:8080"
95               '';
96           };
98           type = mkOption {
99             type = types.enum [ "socks4" "socks5" "http-connect" "http-relay" ];
100             description = lib.mdDoc "Type of proxy.";
101           };
103           login = mkOption {
104             type = with types; nullOr str;
105             default = null;
106             description = lib.mdDoc "Login to send to proxy.";
107           };
109           password = mkOption {
110             type = with types; nullOr str;
111             default = null;
112             description =
113               lib.mdDoc ''
114                 Password to send to proxy. WARNING, this will end up
115                 world-readable in the store! Awaiting
116                 https://github.com/NixOS/nix/issues/8 to be able to fix.
117               '';
118           };
120           disclose_src = mkOption {
121             type = types.enum [ "false" "X-Forwarded-For" "Forwarded_ip"
122                                 "Forwarded_ipport" ];
123             default = "false";
124             description =
125               lib.mdDoc ''
126                 Way to disclose client IP to the proxy.
127                   - "false": do not disclose
128                 http-connect supports the following ways:
129                   - "X-Forwarded-For": add header "X-Forwarded-For: IP"
130                   - "Forwarded_ip": add header "Forwarded: for=IP" (see RFC7239)
131                   - "Forwarded_ipport": add header 'Forwarded: for="IP:port"'
132               '';
133           };
135           redirectInternetOnly = mkOption {
136             type = types.bool;
137             default = true;
138             description = lib.mdDoc "Exclude all non-globally-routable IPs from redsocks";
139           };
141           doNotRedirect = mkOption {
142             type = with types; listOf str;
143             default = [];
144             description =
145               lib.mdDoc ''
146                 Iptables filters that if matched will get the packet off of
147                 redsocks.
148               '';
149             example = [ "-d 1.2.3.4" ];
150           };
152           redirectCondition = mkOption {
153             type = with types; either bool str;
154             default = false;
155             description =
156               lib.mdDoc ''
157                 Conditions to make outbound packets go through this redsocks
158                 instance.
160                 If set to false, no packet will be forwarded. If set to true,
161                 all packets will be forwarded (except packets excluded by
162                 redirectInternetOnly).
164                 If set to a string, this is an iptables filter that will be
165                 matched against packets before getting them into redsocks. For
166                 example, setting it to "--dport 80" will only send
167                 packets to port 80 to redsocks. Note "-p tcp" is always
168                 implicitly added, as udp can only be proxied through redudp or
169                 the like.
170               '';
171           };
172         };});
173       };
175       # TODO: Add support for redudp and dnstc
176     };
177   };
179   ##### implementation
180   config = let
181     redsocks_blocks = concatMapStrings (block:
182       let proxy = splitString ":" block.proxy; in
183       ''
184         redsocks {
185           local_ip = ${block.ip};
186           local_port = ${toString block.port};
188           ip = ${elemAt proxy 0};
189           port = ${elemAt proxy 1};
190           type = ${block.type};
192           ${optionalString (block.login != null) "login = \"${block.login}\";"}
193           ${optionalString (block.password != null) "password = \"${block.password}\";"}
195           disclose_src = ${block.disclose_src};
196         }
197       '') cfg.redsocks;
198     configfile = pkgs.writeText "redsocks.conf"
199       ''
200         base {
201           log_debug = ${if cfg.log_debug then "on" else "off" };
202           log_info = ${if cfg.log_info then "on" else "off" };
203           log = ${cfg.log};
205           daemon = off;
206           redirector = iptables;
208           user = redsocks;
209           group = redsocks;
210           ${optionalString (cfg.chroot != null) "chroot = ${cfg.chroot};"}
211         }
213         ${redsocks_blocks}
214       '';
215     internetOnly = [ # TODO: add ipv6-equivalent
216       "-d 0.0.0.0/8"
217       "-d 10.0.0.0/8"
218       "-d 127.0.0.0/8"
219       "-d 169.254.0.0/16"
220       "-d 172.16.0.0/12"
221       "-d 192.168.0.0/16"
222       "-d 224.168.0.0/4"
223       "-d 240.168.0.0/4"
224     ];
225     redCond = block:
226       optionalString (isString block.redirectCondition) block.redirectCondition;
227     iptables = concatImapStrings (idx: block:
228       let chain = "REDSOCKS${toString idx}"; doNotRedirect =
229         concatMapStringsSep "\n"
230           (f: "ip46tables -t nat -A ${chain} ${f} -j RETURN 2>/dev/null || true")
231           (block.doNotRedirect ++ (optionals block.redirectInternetOnly internetOnly));
232       in
233       optionalString (block.redirectCondition != false)
234         ''
235           ip46tables -t nat -F ${chain} 2>/dev/null || true
236           ip46tables -t nat -N ${chain} 2>/dev/null || true
237           ${doNotRedirect}
238           ip46tables -t nat -A ${chain} -p tcp -j REDIRECT --to-ports ${toString block.port}
240           # TODO: show errors, when it will be easily possible by a switch to
241           # iptables-restore
242           ip46tables -t nat -A OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true
243         ''
244     ) cfg.redsocks;
245   in
246     mkIf cfg.enable {
247       users.groups.redsocks = {};
248       users.users.redsocks = {
249         description = "Redsocks daemon";
250         group = "redsocks";
251         isSystemUser = true;
252       };
254       systemd.services.redsocks = {
255         description = "Redsocks";
256         after = [ "network.target" ];
257         wantedBy = [ "multi-user.target" ];
258         script = "${pkgs.redsocks}/bin/redsocks -c ${configfile}";
259       };
261       networking.firewall.extraCommands = iptables;
263       networking.firewall.extraStopCommands =
264         concatImapStringsSep "\n" (idx: block:
265           let chain = "REDSOCKS${toString idx}"; in
266           optionalString (block.redirectCondition != false)
267             "ip46tables -t nat -D OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true"
268         ) cfg.redsocks;
269     };
271   meta.maintainers = with lib.maintainers; [ ekleog ];