···
cfg = config.services.sslh;
configFile = pkgs.writeText "sslh.conf" ''
verbose: ${boolToString cfg.verbose};
13
+
transparent: ${boolToString cfg.transparent};
timeout: "${toString cfg.timeout}";
15
-
pidfile: "${cfg.pidfile}";
···
description = "Timeout in seconds.";
53
-
pidfile = mkOption {
55
-
default = "/run/sslh.pid";
56
-
description = "PID file path for sslh daemon.";
52
+
transparent = mkOption {
55
+
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 {
61
-
default = config.networking.hostName;
62
-
description = "Listening hostname.";
60
+
default = "0.0.0.0";
61
+
description = "Listening address or hostname.";
···
79
-
config = mkIf cfg.enable {
80
-
systemd.services.sslh = {
81
-
description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)";
82
-
after = [ "network.target" ];
83
-
wantedBy = [ "multi-user.target" ];
84
-
serviceConfig.ExecStart = "${pkgs.sslh}/bin/sslh -F${configFile}";
85
-
serviceConfig.KillMode = "process";
86
-
serviceConfig.PIDFile = "${cfg.pidfile}";
80
+
users.users.${user} = {
81
+
description = "sslh daemon user";
82
+
isSystemUser = true;
85
+
systemd.services.sslh = {
86
+
description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)";
87
+
after = [ "network.target" ];
88
+
wantedBy = [ "multi-user.target" ];
93
+
PermissionsStartOnly = true;
96
+
ExecStart = "${pkgs.sslh}/bin/sslh -F${configFile}";
97
+
KillMode = "process";
98
+
AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID";
100
+
PrivateDevices = true;
101
+
ProtectSystem = "full";
102
+
ProtectHome = true;
107
+
# code from https://github.com/yrutschle/sslh#transparent-proxy-support
108
+
# the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module
109
+
(mkIf (cfg.enable && cfg.transparent) {
110
+
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
111
+
boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1;
112
+
boot.kernel.sysctl."net.ipv4.conf.all.route_localnet" = 1;
114
+
systemd.services.sslh = let
115
+
iptablesCommands = [
116
+
# DROP martian packets as they would have been if route_localnet was zero
117
+
# Note: packets not leaving the server aren't affected by this, thus sslh will still work
118
+
{ table = "raw"; command = "PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP"; }
119
+
{ table = "mangle"; command = "POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP"; }
120
+
# Mark all connections made by ssl for special treatment (here sslh is run as user ${user})
121
+
{ table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; }
122
+
# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
123
+
{ table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; }
125
+
ip6tablesCommands = [
126
+
{ table = "raw"; command = "PREROUTING ! -i lo -d ::1/128 -j DROP"; }
127
+
{ table = "mangle"; command = "POSTROUTING ! -o lo -s ::1/128 -j DROP"; }
128
+
{ table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; }
129
+
{ table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; }
132
+
path = [ pkgs.iptables pkgs.iproute pkgs.procps ];
135
+
# Cleanup old iptables entries which might be still there
136
+
${concatMapStringsSep "\n" ({table, command}: "while iptables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") iptablesCommands}
137
+
${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -A ${command}" ) iptablesCommands}
139
+
# Configure routing for those marked packets
140
+
ip rule add fwmark 0x2 lookup 100
141
+
ip route add local 0.0.0.0/0 dev lo table 100
143
+
'' + optionalString config.networking.enableIPv6 ''
144
+
${concatMapStringsSep "\n" ({table, command}: "while ip6tables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") ip6tablesCommands}
145
+
${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -A ${command}" ) ip6tablesCommands}
147
+
ip -6 rule add fwmark 0x2 lookup 100
148
+
ip -6 route add local ::/0 dev lo table 100
152
+
${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -D ${command}") iptablesCommands}
154
+
ip rule del fwmark 0x2 lookup 100
155
+
ip route del local 0.0.0.0/0 dev lo table 100
156
+
'' + optionalString config.networking.enableIPv6 ''
157
+
${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -D ${command}") ip6tablesCommands}
159
+
ip -6 rule del fwmark 0x2 lookup 100
160
+
ip -6 route del local ::/0 dev lo table 100