···
1
+
{ config, lib, pkgs, ... }:
5
+
cfg = config.services.redsocks;
10
+
services.redsocks = {
14
+
description = "Whether to enable redsocks.";
17
+
log_debug = mkOption {
20
+
description = "Log connection progress.";
23
+
log_info = mkOption {
26
+
description = "Log start and end of client sessions.";
36
+
Possible values are:
38
+
- file:/path/to/file
39
+
- syslog:FACILITY where FACILITY is any of "daemon", "local0",
45
+
type = with types; nullOr str;
49
+
Chroot under which to run redsocks. Log file is opened before
50
+
chroot, but if logging to syslog /etc/localtime may be required.
54
+
redsocks = mkOption {
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.
63
+
{ port = 23456; proxy = "1.2.3.4:8080"; type = "http-relay";
64
+
redirectCondition = "--dport 80";
65
+
doNotRedirect = [ "-d 1.2.0.0/16" ];
67
+
{ port = 23457; proxy = "1.2.3.4:8080"; type = "http-connect";
68
+
redirectCondition = true;
69
+
doNotRedirect = [ "-d 1.2.0.0/16" ];
72
+
type = types.listOf (types.submodule { options = {
75
+
default = "127.0.0.1";
78
+
IP on which redsocks should listen. Defaults to 127.0.0.1 for
86
+
description = "Port on which redsocks should listen.";
93
+
Proxy through which redsocks should forward incoming traffic.
94
+
Example: "example.org:8080"
99
+
type = types.enum [ "socks4" "socks5" "http-connect" "http-relay" ];
100
+
description = "Type of proxy.";
104
+
type = with types; nullOr str;
106
+
description = "Login to send to proxy.";
109
+
password = mkOption {
110
+
type = with types; nullOr str;
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.
120
+
disclose_src = mkOption {
121
+
type = types.enum [ "false" "X-Forwarded-For" "Forwarded_ip"
122
+
"Forwarded_ipport" ];
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"'
135
+
redirectInternetOnly = mkOption {
138
+
description = "Exclude all non-globally-routable IPs from redsocks";
141
+
doNotRedirect = mkOption {
142
+
type = with types; listOf str;
146
+
Iptables filters that if matched will get the packet off of
149
+
example = [ "-d 1.2.3.4" ];
152
+
redirectCondition = mkOption {
153
+
type = with types; either bool str;
157
+
Conditions to make outbound packets go through this redsocks
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
175
+
# TODO: Add support for redudp and dnstc
179
+
##### implementation
181
+
redsocks_blocks = concatMapStrings (block:
182
+
let proxy = splitString ":" block.proxy; in
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};
198
+
configfile = pkgs.writeText "redsocks.conf"
201
+
log_debug = ${if cfg.log_debug then "on" else "off" };
202
+
log_info = ${if cfg.log_info then "on" else "off" };
206
+
redirector = iptables;
210
+
${optionalString (cfg.chroot != null) "chroot = ${cfg.chroot};"}
215
+
internetOnly = [ # TODO: add ipv6-equivalent
219
+
"-d 169.254.0.0/16"
221
+
"-d 192.168.0.0/16"
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));
233
+
optionalString (block.redirectCondition != false)
235
+
ip46tables -t nat -F ${chain} 2>/dev/null || true
236
+
ip46tables -t nat -N ${chain} 2>/dev/null || true
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
242
+
ip46tables -t nat -A OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true
247
+
users.groups.redsocks = {};
248
+
users.users.redsocks = {
249
+
description = "Redsocks daemon";
250
+
group = "redsocks";
251
+
isSystemUser = true;
254
+
systemd.services.redsocks = {
255
+
description = "Redsocks";
256
+
after = [ "network.target" ];
257
+
wantedBy = [ "multi-user.target" ];
258
+
script = "${pkgs.redsocks}/bin/redsocks -c ${configfile}";
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"