diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking/sslh.nix')
-rw-r--r-- | nixpkgs/nixos/modules/services/networking/sslh.nix | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/sslh.nix b/nixpkgs/nixos/modules/services/networking/sslh.nix new file mode 100644 index 000000000000..0222e8ce8b58 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/sslh.nix @@ -0,0 +1,165 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.sslh; + user = "sslh"; + configFile = pkgs.writeText "sslh.conf" '' + verbose: ${boolToString cfg.verbose}; + foreground: true; + inetd: false; + numeric: false; + transparent: ${boolToString cfg.transparent}; + timeout: "${toString cfg.timeout}"; + + listen: + ( + { host: "${cfg.listenAddress}"; port: "${toString cfg.port}"; } + ); + + ${cfg.appendConfig} + ''; + defaultAppendConfig = '' + protocols: + ( + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, + { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, + { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; }, + { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, + { name: "ssl"; host: "localhost"; port: "443"; probe: "builtin"; }, + { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; } + ); + ''; +in +{ + options = { + services.sslh = { + enable = mkEnableOption "sslh"; + + verbose = mkOption { + type = types.bool; + default = false; + description = "Verbose logs."; + }; + + timeout = mkOption { + type = types.int; + default = 2; + description = "Timeout in seconds."; + }; + + transparent = mkOption { + type = types.bool; + default = false; + description = "Will the services behind sslh (Apache, sshd and so on) see the external IP and ports as if the external world connected directly to them"; + }; + + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Listening address or hostname."; + }; + + port = mkOption { + type = types.int; + default = 443; + description = "Listening port."; + }; + + appendConfig = mkOption { + type = types.str; + default = defaultAppendConfig; + description = "Verbatim configuration file."; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + users.users.${user} = { + description = "sslh daemon user"; + isSystemUser = true; + }; + + systemd.services.sslh = { + description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = user; + Group = "nogroup"; + PermissionsStartOnly = true; + Restart = "always"; + RestartSec = "1s"; + ExecStart = "${pkgs.sslh}/bin/sslh -F${configFile}"; + KillMode = "process"; + AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID"; + PrivateTmp = true; + PrivateDevices = true; + ProtectSystem = "full"; + ProtectHome = true; + }; + }; + }) + + # code from https://github.com/yrutschle/sslh#transparent-proxy-support + # the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module + (mkIf (cfg.enable && cfg.transparent) { + # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination + boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1; + boot.kernel.sysctl."net.ipv4.conf.all.route_localnet" = 1; + + systemd.services.sslh = let + iptablesCommands = [ + # DROP martian packets as they would have been if route_localnet was zero + # Note: packets not leaving the server aren't affected by this, thus sslh will still work + { table = "raw"; command = "PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP"; } + { table = "mangle"; command = "POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP"; } + # Mark all connections made by ssl for special treatment (here sslh is run as user ${user}) + { table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; } + # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) + { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; } + ]; + ip6tablesCommands = [ + { table = "raw"; command = "PREROUTING ! -i lo -d ::1/128 -j DROP"; } + { table = "mangle"; command = "POSTROUTING ! -o lo -s ::1/128 -j DROP"; } + { table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; } + { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; } + ]; + in { + path = [ pkgs.iptables pkgs.iproute pkgs.procps ]; + + preStart = '' + # Cleanup old iptables entries which might be still there + ${concatMapStringsSep "\n" ({table, command}: "while iptables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") iptablesCommands} + ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -A ${command}" ) iptablesCommands} + + # Configure routing for those marked packets + ip rule add fwmark 0x2 lookup 100 + ip route add local 0.0.0.0/0 dev lo table 100 + + '' + optionalString config.networking.enableIPv6 '' + ${concatMapStringsSep "\n" ({table, command}: "while ip6tables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") ip6tablesCommands} + ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -A ${command}" ) ip6tablesCommands} + + ip -6 rule add fwmark 0x2 lookup 100 + ip -6 route add local ::/0 dev lo table 100 + ''; + + postStop = '' + ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -D ${command}") iptablesCommands} + + ip rule del fwmark 0x2 lookup 100 + ip route del local 0.0.0.0/0 dev lo table 100 + '' + optionalString config.networking.enableIPv6 '' + ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -D ${command}") ip6tablesCommands} + + ip -6 rule del fwmark 0x2 lookup 100 + ip -6 route del local ::/0 dev lo table 100 + ''; + }; + }) + ]; +} |