From 7a32b9669734ea5d284592f61ec922daf487ac97 Mon Sep 17 00:00:00 2001 From: Léo Gaspard Date: Wed, 1 Feb 2017 14:31:19 +0100 Subject: redsocks module: initialize redsocks module: use separate user for redsocks daemon --- nixos/modules/services/networking/redsocks.nix | 270 +++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 nixos/modules/services/networking/redsocks.nix (limited to 'nixos/modules/services/networking/redsocks.nix') diff --git a/nixos/modules/services/networking/redsocks.nix b/nixos/modules/services/networking/redsocks.nix new file mode 100644 index 000000000000..a47a78f1005e --- /dev/null +++ b/nixos/modules/services/networking/redsocks.nix @@ -0,0 +1,270 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.redsocks; +in +{ + ##### interface + options = { + services.redsocks = { + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable redsocks."; + }; + + log_debug = mkOption { + type = types.bool; + default = false; + description = "Log connection progress."; + }; + + log_info = mkOption { + type = types.bool; + default = false; + description = "Log start and end of client sessions."; + }; + + log = mkOption { + type = types.str; + default = "stderr"; + description = + '' + Where to send logs. + + Possible values are: + - stderr + - file:/path/to/file + - syslog:FACILITY where FACILITY is any of "daemon", "local0", + etc. + ''; + }; + + chroot = mkOption { + type = with types; nullOr str; + default = null; + description = + '' + Chroot under which to run redsocks. Log file is opened before + chroot, but if logging to syslog /etc/localtime may be required. + ''; + }; + + redsocks = mkOption { + description = + '' + Local port to proxy associations to be performed. + + The example shows how to configure a proxy to handle port 80 as HTTP + relay, and all other ports as HTTP connect. + ''; + example = [ + { port = 23456; proxy = "1.2.3.4:8080"; type = "http-relay"; + redirectCondition = "--dport 80"; + doNotRedirect = [ "-d 1.2.0.0/16" ]; + } + { port = 23457; proxy = "1.2.3.4:8080"; type = "http-connect"; + redirectCondition = true; + doNotRedirect = [ "-d 1.2.0.0/16" ]; + } + ]; + type = types.listOf (types.submodule { options = { + ip = mkOption { + type = types.str; + default = "127.0.0.1"; + description = + '' + IP on which redsocks should listen. Defaults to 127.0.0.1 for + security reasons. + ''; + }; + + port = mkOption { + type = types.int; + default = 12345; + description = "Port on which redsocks should listen."; + }; + + proxy = mkOption { + type = types.str; + description = + '' + Proxy through which redsocks should forward incoming traffic. + Example: "example.org:8080" + ''; + }; + + type = mkOption { + type = types.enum [ "socks4" "socks5" "http-connect" "http-relay" ]; + description = "Type of proxy."; + }; + + login = mkOption { + type = with types; nullOr str; + default = null; + description = "Login to send to proxy."; + }; + + password = mkOption { + type = with types; nullOr str; + default = null; + description = + '' + Password to send to proxy. WARNING, this will end up + world-readable in the store! Awaiting + https://github.com/NixOS/nix/issues/8 to be able to fix. + ''; + }; + + disclose_src = mkOption { + type = types.enum [ "false" "X-Forwarded-For" "Forwarded_ip" + "Forwarded_ipport" ]; + default = "false"; + description = + '' + Way to disclose client IP to the proxy. + - "false": do not disclose + http-connect supports the following ways: + - "X-Forwarded-For": add header "X-Forwarded-For: IP" + - "Forwarded_ip": add header "Forwarded: for=IP" (see RFC7239) + - "Forwarded_ipport": add header 'Forwarded: for="IP:port"' + ''; + }; + + redirectInternetOnly = mkOption { + type = types.bool; + default = true; + description = "Exclude all non-globally-routable IPs from redsocks"; + }; + + doNotRedirect = mkOption { + type = with types; listOf str; + default = []; + description = + '' + Iptables filters that if matched will get the packet off of + redsocks. + ''; + example = [ "-d 1.2.3.4" ]; + }; + + redirectCondition = mkOption { + type = with types; either bool str; + default = false; + description = + '' + Conditions to make outbound packets go through this redsocks + instance. + + If set to false, no packet will be forwarded. If set to true, + all packets will be forwarded (except packets excluded by + redirectInternetOnly). + + If set to a string, this is an iptables filter that will be + matched against packets before getting them into redsocks. For + example, setting it to "--dport 80" will only send + packets to port 80 to redsocks. Note "-p tcp" is always + implicitly added, as udp can only be proxied through redudp or + the like. + ''; + }; + };}); + }; + + # TODO: Add support for redudp and dnstc + }; + }; + + ##### implementation + config = let + redsocks_blocks = concatMapStrings (block: + let proxy = splitString ":" block.proxy; in + '' + redsocks { + local_ip = ${block.ip}; + local_port = ${toString block.port}; + + ip = ${elemAt proxy 0}; + port = ${elemAt proxy 1}; + type = ${block.type}; + + ${optionalString (block.login != null) "login = \"${block.login}\";"} + ${optionalString (block.password != null) "password = \"${block.password}\";"} + + disclose_src = ${block.disclose_src}; + } + '') cfg.redsocks; + configfile = pkgs.writeText "redsocks.conf" + '' + base { + log_debug = ${if cfg.log_debug then "on" else "off" }; + log_info = ${if cfg.log_info then "on" else "off" }; + log = ${cfg.log}; + + daemon = off; + redirector = iptables; + + user = redsocks; + group = redsocks; + ${optionalString (cfg.chroot != null) "chroot = ${cfg.chroot};"} + } + + ${redsocks_blocks} + ''; + internetOnly = [ # TODO: add ipv6-equivalent + "-d 0.0.0.0/8" + "-d 10.0.0.0/8" + "-d 127.0.0.0/8" + "-d 169.254.0.0/16" + "-d 172.16.0.0/12" + "-d 192.168.0.0/16" + "-d 224.168.0.0/4" + "-d 240.168.0.0/4" + ]; + redCond = block: + optionalString (isString block.redirectCondition) block.redirectCondition; + iptables = concatImapStrings (idx: block: + let chain = "REDSOCKS${toString idx}"; doNotRedirect = + concatMapStringsSep "\n" + (f: "ip46tables -t nat -A ${chain} ${f} -j RETURN 2>/dev/null || true") + (block.doNotRedirect ++ (optionals block.redirectInternetOnly internetOnly)); + in + optionalString (block.redirectCondition != false) + '' + ip46tables -t nat -F ${chain} 2>/dev/null || true + ip46tables -t nat -N ${chain} 2>/dev/null || true + ${doNotRedirect} + ip46tables -t nat -A ${chain} -p tcp -j REDIRECT --to-ports ${toString block.port} + + # TODO: show errors, when it will be easily possible by a switch to + # iptables-restore + ip46tables -t nat -A OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true + '' + ) cfg.redsocks; + in + mkIf cfg.enable { + users.groups.redsocks = {}; + users.users.redsocks = { + description = "Redsocks daemon"; + group = "redsocks"; + isSystemUser = true; + }; + + systemd.services.redsocks = { + description = "Redsocks"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.redsocks}/bin/redsocks -c ${configfile}"; + }; + + networking.firewall.extraCommands = iptables; + + networking.firewall.extraStopCommands = + concatImapStringsSep "\n" (idx: block: + let chain = "REDSOCKS${toString idx}"; in + optionalString (block.redirectCondition != false) + "ip46tables -t nat -D OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true" + ) cfg.redsocks; + }; +} -- cgit 1.4.1