1 { config, lib, pkgs, ... }:
5 cfg = config.services.redsocks;
14 description = lib.mdDoc "Whether to enable redsocks.";
17 log_debug = mkOption {
20 description = lib.mdDoc "Log connection progress.";
26 description = lib.mdDoc "Log start and end of client sessions.";
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.
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 = lib.mdDoc "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 = lib.mdDoc "Type of proxy.";
104 type = with types; nullOr str;
106 description = lib.mdDoc "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 = lib.mdDoc "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
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
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";
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"
271 meta.maintainers = with lib.maintainers; [ ekleog ];