Version 2 (Stand 17.04.21)
Einleitung
Zuletzt war ich an einem internationalen Flughafen unterwegs und wollte etwas im Internet über das Flughafen-WLAN surfen. Nach allen möglich Angaben über meine Haustiere, näheren Verwandten und Lieblingsbars, die ich selbstverständlich absolut wahrheitsgemäß beantwortet habe, war ich dann auch letztendlich mit dem Internet verbunden. Jetzt gehört meine Wenigkeit jedoch zu jenen paranoiden, die grundsätzlich bei öffentlichen WLANs, bei denen ich den Administrator nicht kenne, bzw. auch nicht weiß, welche Sicherheitsmaßnahmen dieser integriert hat, ein VPN benutzt. Also öffne ich eine x-beliebige VPN-App, möchte mich verbinden, kann aber nicht. Nach einigen Sekunden wird mir dann auch klar warum. Alle Ports außer der http (80) und https (443) sind gesperrt. Nun hatte ich das Glück, dass mein VPN-Betreiber auch diese Ports unterstützen konnte, letztendlich fragte ich mich jedoch, ob es möglich wäre in Zukunft für so eine Eventualität auch mit dem heimischen VPN gerüstet zu sein. Auf meinem Server lief bereits ein OpenVPN über den gewohnten Port 1194, der mir in dieser Situation natürlich nicht viel nutzte. Gab es also eine Möglichkeit, den Port 443 für mehrere Anwendungen zu benutzen? Nach etwas Recherche fand ich dann auch, was ich brauchte - einen sog. Protocol Multiplexer namens SSLH. Die Konfiguration des Ganzen war nicht allzu einfach, daher hier mein Konfigurationsvorschlag für jene, die Ähnliches vorhaben.
Konfigurationsdateien
/etc/sslh.cfg
# Default Arch configuration
# You can find more examples in /usr/share/doc/sslh
timeout: 2;
transparent: true;
listen:
(
{ host: "internal-network-adress"; port: "443"; }
);
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: "tls"; host: "localhost"; port: "443"; probe: "builtin"; },
{ name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; }
);
# vim:set ts=4 sw=4 et:
Ergänzend kann man auch die internal-network-adress
in /etc/hosts
festlegen.
/etc/systemd/system/sslh.service.d
[Unit]
Description=SSL/SSH multiplexer (socket mode)
Conflicts=sslh-fork.service sslh-select.service
After=network.target network-online.target nss-lookup.target
After=sslh-iptables.service
Requires=sslh.socket
PartOf=sslh.socket
[Service]
KillMode=process
ProtectSystem=strict
ProtectHome=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
PrivateTmp=true
PrivateDevices=true
SecureBits=noroot-locked
MountFlags=private
NoNewPrivileges=true
CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_NET_BIND_SERVICE CAP_NET_ADMIN
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
MemoryDenyWriteExecute=true
User=sslh
DynamicUser=true
[Install]
WantedBy=multi-user.target
Nginx umkonfigurieren
Nachdem mein Nginx standardmäßig auf dem 443 Port lauscht, muss hier einiges verändert werden:
/etc/ngins/sites-enabled/site-xyz.conf
server {
listen 80;
listen [::]:80;
server_name domain.com;
return 301 https://domain.com$request_uri;
}
server {
listen 127.0.0.1:443 ssl http2;
listen [::1]:443 ssl http2;
server_name domain.com;
server_tokens off;
...
}
sudo systemctl restart nginx
Externe IP-Adresse an den Server weitergeben (transparent mode)
/etc/sslh/iptables.sh
!/bin/bash
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
sysctl -w net.ipv4.conf.default.route_localnet=1
sysctl -w net.ipv4.conf.all.route_localnet=1
# 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
iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -A 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 "sslh")
iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
# Configure routing for those marked packets
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
# 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
ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP
ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP
# Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
# Configure routing for those marked packets
ip -6 rule add fwmark 0x1 lookup 100
ip -6 route add local ::/0 dev lo table 100
/usr/lib/systemd/system/sslh-iptables.service
[Unit]
Description=iptables for transparent proxy
After=network.target
[Service]
ExecStart=/etc/sslh/iptables.sh
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
und sudo systemctl start sslh-iptables.service
, sowie sudo systemctl enable sslh-iptables.service
Als letzten Schritt dann:
sudo systemctl enable sslh.service
, sowie sudo systemctl start sslh.service
Quellen
- github (Stand: 03.07.2020)
- arch-linux (Stand: 03.07.2020)
- iptables (Stand: 03.07.2020)