at 23.11-pre 6.8 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.sslh; 7 user = "sslh"; 8 configFile = pkgs.writeText "sslh.conf" '' 9 verbose: ${boolToString cfg.verbose}; 10 foreground: true; 11 inetd: false; 12 numeric: false; 13 transparent: ${boolToString cfg.transparent}; 14 timeout: "${toString cfg.timeout}"; 15 16 listen: 17 ( 18 ${ 19 concatMapStringsSep ",\n" 20 (addr: ''{ host: "${addr}"; port: "${toString cfg.port}"; }'') 21 cfg.listenAddresses 22 } 23 ); 24 25 ${cfg.appendConfig} 26 ''; 27 defaultAppendConfig = '' 28 protocols: 29 ( 30 { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, 31 { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, 32 { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; }, 33 { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, 34 { name: "tls"; host: "localhost"; port: "443"; probe: "builtin"; }, 35 { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; } 36 ); 37 ''; 38in 39{ 40 imports = [ 41 (mkRenamedOptionModule [ "services" "sslh" "listenAddress" ] [ "services" "sslh" "listenAddresses" ]) 42 ]; 43 44 options = { 45 services.sslh = { 46 enable = mkEnableOption (lib.mdDoc "sslh"); 47 48 verbose = mkOption { 49 type = types.bool; 50 default = false; 51 description = lib.mdDoc "Verbose logs."; 52 }; 53 54 timeout = mkOption { 55 type = types.int; 56 default = 2; 57 description = lib.mdDoc "Timeout in seconds."; 58 }; 59 60 transparent = mkOption { 61 type = types.bool; 62 default = false; 63 description = lib.mdDoc "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"; 64 }; 65 66 listenAddresses = mkOption { 67 type = types.coercedTo types.str singleton (types.listOf types.str); 68 default = [ "0.0.0.0" "[::]" ]; 69 description = lib.mdDoc "Listening addresses or hostnames."; 70 }; 71 72 port = mkOption { 73 type = types.port; 74 default = 443; 75 description = lib.mdDoc "Listening port."; 76 }; 77 78 appendConfig = mkOption { 79 type = types.str; 80 default = defaultAppendConfig; 81 description = lib.mdDoc "Verbatim configuration file."; 82 }; 83 }; 84 }; 85 86 config = mkMerge [ 87 (mkIf cfg.enable { 88 systemd.services.sslh = { 89 description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)"; 90 after = [ "network.target" ]; 91 wantedBy = [ "multi-user.target" ]; 92 93 serviceConfig = { 94 DynamicUser = true; 95 User = "sslh"; 96 PermissionsStartOnly = true; 97 Restart = "always"; 98 RestartSec = "1s"; 99 ExecStart = "${pkgs.sslh}/bin/sslh -F${configFile}"; 100 KillMode = "process"; 101 AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID"; 102 PrivateTmp = true; 103 PrivateDevices = true; 104 ProtectSystem = "full"; 105 ProtectHome = true; 106 }; 107 }; 108 }) 109 110 # code from https://github.com/yrutschle/sslh#transparent-proxy-support 111 # the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module 112 (mkIf (cfg.enable && cfg.transparent) { 113 # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination 114 boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1; 115 boot.kernel.sysctl."net.ipv4.conf.all.route_localnet" = 1; 116 117 systemd.services.sslh = let 118 iptablesCommands = [ 119 # DROP martian packets as they would have been if route_localnet was zero 120 # Note: packets not leaving the server aren't affected by this, thus sslh will still work 121 { table = "raw"; command = "PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP"; } 122 { table = "mangle"; command = "POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP"; } 123 # Mark all connections made by ssl for special treatment (here sslh is run as user ${user}) 124 { table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; } 125 # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) 126 { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; } 127 ]; 128 ip6tablesCommands = [ 129 { table = "raw"; command = "PREROUTING ! -i lo -d ::1/128 -j DROP"; } 130 { table = "mangle"; command = "POSTROUTING ! -o lo -s ::1/128 -j DROP"; } 131 { table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; } 132 { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; } 133 ]; 134 in { 135 path = [ pkgs.iptables pkgs.iproute2 pkgs.procps ]; 136 137 preStart = '' 138 # Cleanup old iptables entries which might be still there 139 ${concatMapStringsSep "\n" ({table, command}: "while iptables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") iptablesCommands} 140 ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -A ${command}" ) iptablesCommands} 141 142 # Configure routing for those marked packets 143 ip rule add fwmark 0x2 lookup 100 144 ip route add local 0.0.0.0/0 dev lo table 100 145 146 '' + optionalString config.networking.enableIPv6 '' 147 ${concatMapStringsSep "\n" ({table, command}: "while ip6tables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") ip6tablesCommands} 148 ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -A ${command}" ) ip6tablesCommands} 149 150 ip -6 rule add fwmark 0x2 lookup 100 151 ip -6 route add local ::/0 dev lo table 100 152 ''; 153 154 postStop = '' 155 ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -D ${command}") iptablesCommands} 156 157 ip rule del fwmark 0x2 lookup 100 158 ip route del local 0.0.0.0/0 dev lo table 100 159 '' + optionalString config.networking.enableIPv6 '' 160 ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -D ${command}") ip6tablesCommands} 161 162 ip -6 rule del fwmark 0x2 lookup 100 163 ip -6 route del local ::/0 dev lo table 100 164 ''; 165 }; 166 }) 167 ]; 168}