Merge pull request #22356 from Ekleog/redsocks

Redsocks

authored by

Joachim F and committed by
GitHub
ca8fb930 3455bd6f

+306
+1
nixos/modules/module-list.nix
··· 446 ./services/networking/radicale.nix 447 ./services/networking/radvd.nix 448 ./services/networking/rdnssd.nix 449 ./services/networking/rpcbind.nix 450 ./services/networking/sabnzbd.nix 451 ./services/networking/searx.nix
··· 446 ./services/networking/radicale.nix 447 ./services/networking/radvd.nix 448 ./services/networking/rdnssd.nix 449 + ./services/networking/redsocks.nix 450 ./services/networking/rpcbind.nix 451 ./services/networking/sabnzbd.nix 452 ./services/networking/searx.nix
+270
nixos/modules/services/networking/redsocks.nix
···
··· 1 + { config, lib, pkgs, ... }: 2 + 3 + with lib; 4 + let 5 + cfg = config.services.redsocks; 6 + in 7 + { 8 + ##### interface 9 + options = { 10 + services.redsocks = { 11 + enable = mkOption { 12 + type = types.bool; 13 + default = false; 14 + description = "Whether to enable redsocks."; 15 + }; 16 + 17 + log_debug = mkOption { 18 + type = types.bool; 19 + default = false; 20 + description = "Log connection progress."; 21 + }; 22 + 23 + log_info = mkOption { 24 + type = types.bool; 25 + default = false; 26 + description = "Log start and end of client sessions."; 27 + }; 28 + 29 + log = mkOption { 30 + type = types.str; 31 + default = "stderr"; 32 + description = 33 + '' 34 + Where to send logs. 35 + 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 + }; 43 + 44 + chroot = mkOption { 45 + type = with types; nullOr str; 46 + default = null; 47 + description = 48 + '' 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 + }; 53 + 54 + redsocks = mkOption { 55 + description = 56 + '' 57 + Local port to proxy associations to be performed. 58 + 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 + '' 78 + IP on which redsocks should listen. Defaults to 127.0.0.1 for 79 + security reasons. 80 + ''; 81 + }; 82 + 83 + port = mkOption { 84 + type = types.int; 85 + default = 12345; 86 + description = "Port on which redsocks should listen."; 87 + }; 88 + 89 + proxy = mkOption { 90 + type = types.str; 91 + description = 92 + '' 93 + Proxy through which redsocks should forward incoming traffic. 94 + Example: "example.org:8080" 95 + ''; 96 + }; 97 + 98 + type = mkOption { 99 + type = types.enum [ "socks4" "socks5" "http-connect" "http-relay" ]; 100 + description = "Type of proxy."; 101 + }; 102 + 103 + login = mkOption { 104 + type = with types; nullOr str; 105 + default = null; 106 + description = "Login to send to proxy."; 107 + }; 108 + 109 + password = mkOption { 110 + type = with types; nullOr str; 111 + default = null; 112 + description = 113 + '' 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 + }; 119 + 120 + disclose_src = mkOption { 121 + type = types.enum [ "false" "X-Forwarded-For" "Forwarded_ip" 122 + "Forwarded_ipport" ]; 123 + default = "false"; 124 + description = 125 + '' 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 + }; 134 + 135 + redirectInternetOnly = mkOption { 136 + type = types.bool; 137 + default = true; 138 + description = "Exclude all non-globally-routable IPs from redsocks"; 139 + }; 140 + 141 + doNotRedirect = mkOption { 142 + type = with types; listOf str; 143 + default = []; 144 + description = 145 + '' 146 + Iptables filters that if matched will get the packet off of 147 + redsocks. 148 + ''; 149 + example = [ "-d 1.2.3.4" ]; 150 + }; 151 + 152 + redirectCondition = mkOption { 153 + type = with types; either bool str; 154 + default = false; 155 + description = 156 + '' 157 + Conditions to make outbound packets go through this redsocks 158 + instance. 159 + 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). 163 + 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 + }; 174 + 175 + # TODO: Add support for redudp and dnstc 176 + }; 177 + }; 178 + 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}; 187 + 188 + ip = ${elemAt proxy 0}; 189 + port = ${elemAt proxy 1}; 190 + type = ${block.type}; 191 + 192 + ${optionalString (block.login != null) "login = \"${block.login}\";"} 193 + ${optionalString (block.password != null) "password = \"${block.password}\";"} 194 + 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}; 204 + 205 + daemon = off; 206 + redirector = iptables; 207 + 208 + user = redsocks; 209 + group = redsocks; 210 + ${optionalString (cfg.chroot != null) "chroot = ${cfg.chroot};"} 211 + } 212 + 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} 239 + 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 + }; 253 + 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 + }; 260 + 261 + networking.firewall.extraCommands = iptables; 262 + 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 + }; 270 + }
+33
pkgs/tools/networking/redsocks/default.nix
···
··· 1 + { stdenv, fetchFromGitHub, libevent }: 2 + 3 + let 4 + pkg = "redsocks"; 5 + version = "0.5"; 6 + in 7 + stdenv.mkDerivation rec { 8 + name = "${pkg}-${version}"; 9 + 10 + src = fetchFromGitHub { 11 + owner = "darkk"; 12 + repo = pkg; 13 + rev = "release-${version}"; 14 + sha256 = "170cpvvivb6y2kwsqj9ppx5brgds9gkn8mixrnvj8z9c15xhvplm"; 15 + }; 16 + 17 + installPhase = 18 + '' 19 + mkdir -p $out/{bin,share} 20 + mv redsocks $out/bin 21 + mv doc $out/share 22 + ''; 23 + 24 + buildInputs = [ libevent ]; 25 + 26 + meta = { 27 + description = "Transparent redirector of any TCP connection to proxy"; 28 + homepage = http://darkk.net.ru/redsocks/; 29 + license = stdenv.lib.licenses.asl20; 30 + maintainers = [ ]; 31 + platforms = stdenv.lib.platforms.all; 32 + }; 33 + }
+2
pkgs/top-level/all-packages.nix
··· 3583 3584 redmine = callPackage ../applications/version-management/redmine { }; 3585 3586 rt = callPackage ../servers/rt { }; 3587 3588 rtmpdump = callPackage ../tools/video/rtmpdump { };
··· 3583 3584 redmine = callPackage ../applications/version-management/redmine { }; 3585 3586 + redsocks = callPackage ../tools/networking/redsocks { }; 3587 + 3588 rt = callPackage ../servers/rt { }; 3589 3590 rtmpdump = callPackage ../tools/video/rtmpdump { };