vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / sslh.nix
blobaad9e284d92ceb1d9e353e41da7d508fc3402906
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.sslh;
7   user = "sslh";
9   configFormat = pkgs.formats.libconfig {};
10   configFile = configFormat.generate "sslh.conf" cfg.settings;
14   imports = [
15     (mkRenamedOptionModule [ "services" "sslh" "listenAddress" ] [ "services" "sslh" "listenAddresses" ])
16     (mkRenamedOptionModule [ "services" "sslh" "timeout" ] [ "services" "sslh" "settings" "timeout" ])
17     (mkRenamedOptionModule [ "services" "sslh" "transparent" ] [ "services" "sslh" "settings" "transparent" ])
18     (mkRemovedOptionModule [ "services" "sslh" "appendConfig" ] "Use services.sslh.settings instead")
19     (mkChangedOptionModule [ "services" "sslh" "verbose" ] [ "services" "sslh" "settings" "verbose-connections" ]
20       (config: if config.services.sslh.verbose then 1 else 0))
21   ];
23   meta.buildDocsInSandbox = false;
25   options.services.sslh = {
26     enable = mkEnableOption "sslh, protocol demultiplexer";
28     method = mkOption {
29       type = types.enum [ "fork" "select" "ev" ];
30       default = "fork";
31       description = ''
32         The method to use for handling connections:
34           - `fork` forks a new process for each incoming connection. It is
35           well-tested and very reliable, but incurs the overhead of many
36           processes.
38           - `select` uses only one thread, which monitors all connections at once.
39           It has lower overhead per connection, but if it stops, you'll lose all
40           connections.
42           - `ev` is implemented using libev, it's similar to `select` but
43             scales better to a large number of connections.
44       '';
45     };
47     listenAddresses = mkOption {
48       type = with types; coercedTo str singleton (listOf str);
49       default = [ "0.0.0.0" "[::]" ];
50       description = "Listening addresses or hostnames.";
51     };
53     port = mkOption {
54       type = types.port;
55       default = 443;
56       description = "Listening port.";
57     };
59     settings = mkOption {
60       type = types.submodule {
61         freeformType = configFormat.type;
63         options.timeout = mkOption {
64           type = types.ints.unsigned;
65           default = 2;
66           description = "Timeout in seconds.";
67         };
69         options.transparent = mkOption {
70           type = types.bool;
71           default = false;
72           description = ''
73             Whether the services behind sslh (Apache, sshd and so on) will see the
74             external IP and ports as if the external world connected directly to
75             them.
76           '';
77         };
79         options.verbose-connections = mkOption {
80           type = types.ints.between 0 4;
81           default = 0;
82           description = ''
83             Where to log connections information. Possible values are:
85              0. don't log anything
86              1. write log to stdout
87              2. write log to syslog
88              3. write log to both stdout and syslog
89              4. write to a log file ({option}`sslh.settings.logfile`)
90           '';
91         };
93         options.numeric = mkOption {
94           type = types.bool;
95           default = true;
96           description = ''
97             Whether to disable reverse DNS lookups, thus keeping IP
98             address literals in the log.
99           '';
100         };
102         options.protocols = mkOption {
103           type = types.listOf configFormat.type;
104           default = [
105             { name = "ssh";     host = "localhost"; port =  "22"; service= "ssh"; }
106             { name = "openvpn"; host = "localhost"; port = "1194"; }
107             { name = "xmpp";    host = "localhost"; port = "5222"; }
108             { name = "http";    host = "localhost"; port =   "80"; }
109             { name = "tls";     host = "localhost"; port =  "443"; }
110             { name = "anyprot"; host = "localhost"; port =  "443"; }
111           ];
112           description = ''
113             List of protocols sslh will probe for and redirect.
114             Each protocol entry consists of:
116               - `name`: name of the probe.
118               - `service`: libwrap service name (see {manpage}`hosts_access(5)`),
120               - `host`, `port`: where to connect when this probe succeeds,
122               - `log_level`: to log incoming connections,
124               - `transparent`: proxy this protocol transparently,
126               - etc.
128             See the documentation for all options, including probe-specific ones.
129           '';
130         };
131       };
132       description = "sslh configuration. See {manpage}`sslh(8)` for available settings.";
133     };
134   };
136   config = mkMerge [
137     (mkIf cfg.enable {
138       systemd.services.sslh = {
139         description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)";
140         after = [ "network.target" ];
141         wantedBy = [ "multi-user.target" ];
143         serviceConfig = {
144           DynamicUser          = true;
145           User                 = "sslh";
146           PermissionsStartOnly = true;
147           Restart              = "always";
148           RestartSec           = "1s";
149           ExecStart            = "${pkgs.sslh}/bin/sslh-${cfg.method} -F${configFile}";
150           KillMode             = "process";
151           AmbientCapabilities  = ["CAP_NET_BIND_SERVICE" "CAP_NET_ADMIN" "CAP_SETGID" "CAP_SETUID"];
152           PrivateTmp           = true;
153           PrivateDevices       = true;
154           ProtectSystem        = "full";
155           ProtectHome          = true;
156         };
157       };
159       services.sslh.settings = {
160         # Settings defined here are not supposed to be changed: doing so will
161         # break the module, as such you need `lib.mkForce` to override them.
162         foreground = true;
163         inetd = false;
164         listen = map (addr: { host = addr; port = toString cfg.port; }) cfg.listenAddresses;
165       };
167     })
169     # code from https://github.com/yrutschle/sslh#transparent-proxy-support
170     # the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module
171     (mkIf (cfg.enable && cfg.settings.transparent) {
172       # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
173       boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1;
174       boot.kernel.sysctl."net.ipv4.conf.all.route_localnet"     = 1;
176       systemd.services.sslh = let
177         iptablesCommands = [
178           # DROP martian packets as they would have been if route_localnet was zero
179           # Note: packets not leaving the server aren't affected by this, thus sslh will still work
180           { table = "raw";    command = "PREROUTING  ! -i lo -d 127.0.0.0/8 -j DROP"; }
181           { table = "mangle"; command = "POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP"; }
182           # Mark all connections made by ssl for special treatment (here sslh is run as user ${user})
183           { table = "nat";    command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; }
184           # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
185           { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; }
186         ];
187         ip6tablesCommands = [
188           { table = "raw";    command = "PREROUTING  ! -i lo -d ::1/128     -j DROP"; }
189           { table = "mangle"; command = "POSTROUTING ! -o lo -s ::1/128     -j DROP"; }
190           { table = "nat";    command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; }
191           { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; }
192         ];
193       in {
194         path = [ pkgs.iptables pkgs.iproute2 pkgs.procps ];
196         preStart = ''
197           # Cleanup old iptables entries which might be still there
198           ${concatMapStringsSep "\n" ({table, command}: "while iptables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") iptablesCommands}
199           ${concatMapStringsSep "\n" ({table, command}:       "iptables -w -t ${table} -A ${command}"                           ) iptablesCommands}
201           # Configure routing for those marked packets
202           ip rule  add fwmark 0x2 lookup 100
203           ip route add local 0.0.0.0/0 dev lo table 100
205         '' + optionalString config.networking.enableIPv6 ''
206           ${concatMapStringsSep "\n" ({table, command}: "while ip6tables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") ip6tablesCommands}
207           ${concatMapStringsSep "\n" ({table, command}:       "ip6tables -w -t ${table} -A ${command}"                           ) ip6tablesCommands}
209           ip -6 rule  add fwmark 0x2 lookup 100
210           ip -6 route add local ::/0 dev lo table 100
211         '';
213         postStop = ''
214           ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -D ${command}") iptablesCommands}
216           ip rule  del fwmark 0x2 lookup 100
217           ip route del local 0.0.0.0/0 dev lo table 100
218         '' + optionalString config.networking.enableIPv6 ''
219           ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -D ${command}") ip6tablesCommands}
221           ip -6 rule  del fwmark 0x2 lookup 100
222           ip -6 route del local ::/0 dev lo table 100
223         '';
224       };
225     })
226   ];