diff --git a/doc/tproxy.jpg b/doc/tproxy.jpg new file mode 100644 index 0000000..44952f5 Binary files /dev/null and b/doc/tproxy.jpg differ diff --git a/doc/tproxy.md b/doc/tproxy.md new file mode 100644 index 0000000..4342e00 --- /dev/null +++ b/doc/tproxy.md @@ -0,0 +1,275 @@ +Transparent Proxy to Two Hosts +============================== + +Tutorial by Sean Warner. 19 June 2019 20:35 +  +Aim +--- + +* Here is my guide… the solution here only works for my very specific use-case but hopefully others can adapt it to suits their needs. +* Show that SSLH can transparently proxy requests from the internet to services on two separate hosts that are both on the same LAN. +* The IP address of the client initiating the request is what the destination should see… and not the IP address of the host that `sslh` is running on, which is what happens when `sslh` is not running in transparent mode. +  +Overview of my Network +---------------------- + +Two Raspberry Pis on my home LAN: +* Pi A: 192.168.1.124 – `sslh` (Port 4433), Apache2 web server for https (port 443), `stunnel` (port 4480) to decrypt ssh traffic and forward to SSH server (also on Pi A at Port 1022) +* Pi B: 192.168.1.123 - HTTP server (port 8000), SSH server (port 1022 on PiB). +  +I send traffic from the internet to my router's external port 443 then use a port forward rule in my router to map that to internal port 4433 where sslh is listening. + +![Architecture](tproxy.jpg) + +`sslh` build +------------ +  +`sslh` Version: sslh v1.19c-2-gf451cc8-dirty. + +I compiled sslh from sources giving the binary pretty much all possible options such as Posix capabilities and systemd support.. here are the first few lines of the makefile: +  +``` +# Configuration + +VERSION=$(shell ./genver.sh -r) +ENABLE_REGEX=1         # Enable regex probes +USELIBCONFIG=1         # Use libconfig? (necessary to use configuration files) +USELIBPCRE=1         # Use libpcre? (needed for regex on musl) +USELIBWRAP=1         # Use libwrap? +USELIBCAP=1         # Use libcap? +USESYSTEMD=1         # Make use of systemd socket activation +COV_TEST=         # Perform test coverage? +PREFIX=/usr/local +BINDIR=$(PREFIX)/sbin +MANDIR=$(PREFIX)/share/man/man8 +MAN=sslh.8.gz         # man page name + +# End of configuration -- the rest should take care of +# itself +``` +  +systemd setup +------------- + +Create an sslh systemd service file... +``` +# nano /lib/systemd/system/sslh.service +``` + +Paste in this contents… + +``` +[Unit] +Description=SSL/SSH multiplexer +After=network.target +Documentation=man:sslh(8) + +[Service] +#EnvironmentFile=/etc/default/sslh +#ExecStart=/usr/local/sbin/sslh $DAEMON_OPTS +ExecStart=/usr/local/sbin/sslh -F /etc/sslh/sslh.cfg +KillMode=process + +[Install] +WantedBy=multi-user.target +``` + +Save it and then… +``` +# systemctl daemon-reload +``` + +Start it again to test… +``` +# systemctl start sslh +``` +  +Configure `sslh` +---------------- + +First stop `sslh` then open the config file and replace with below, save and start `sslh` again +``` +# systemctl stop sslh +# nano /etc/sslh/sslh.cfg +# systemctl start sslh +``` + +``` +verbose: true; +foreground: true; +inetd: false; +numeric: true; +transparent: true; +timeout: 2; +user: "sslh"; +pidfile: "/var/run/sslh.pid"; +chroot: "/var/empty"; + +# You must have a port forward rule in the router: external port 443 <-> internal port 4433 +# Local ip address of PiA is: 192.168.1.124, sslh and stunnel4 are running on this Pi +# Local ip address of PiB is: 192.168.1.123, http server and ssh server on this Pi +listen: +( +{ host: "192.168.1.124"; port: "4433"; } +); + +# sslh demultiplexes based on the Protocol and Hostname +protocols: +( +{ name: "tls"; sni_hostnames: [ "www.example.com" ]; host: "192.168.1.124"; port: "443"; log_level: 1; }, +# This probe is for tls encrypted ssh. SSLH forwards it to stunnel on port 4480 which decrypts it and sends it to the ssh server on PiA port 1022 +{ name: "tls"; sni_hostnames: [ "ssh.example.com" ]; host: "192.168.1.124"; port: "4480"; log_level: 1; }, +{ name: "http"; host: "192.168.1.123"; port: "8000"; log_level: 1; }, +{ name: "ssh"; host: "192.168.1.123"; port: "1022"; log_level: 1; } +); +``` +  +Configure `stunnel` +------------------- + +First stop `stunnel` then open the config file and replace with below, save and start `stunnel` again +``` +# systemctl stop stunnel4 +# nano /etc/stunnel/stunnel.conf +# systemctl start stunnel4 +``` + +``` +# Debugging stuff (may be useful for troubleshooting) +foreground = yes +#debug = 5 # this is the default +debug = 7 +output = /var/log/stunnel4/stunnel.log +pid = /var/run/stunnel4/stunnel.pid +fips = no + +cert = /etc/letsencrypt/live/example.com/fullchain.pem +key = /etc/letsencrypt/live/example.com/privkey.pem + +[ssh] +accept = 192.168.1.124:4480 +connect = 192.168.1.124:1022 +TIMEOUTclose = 0 +``` +  +Configure iptables for piA +-------------------------- + +The `_add.sh` script creates the rules, the `_rm.sh` script removes the rules. +They will be lost if you reboot but there are ways to make them load again on start-up.. +``` +# nano /usr/local/sbin/[filename.sh] +``` + +`piA_tproxy_add.sh`: +``` piA_tproxy_add.sh +iptables -w -t mangle -N SSLH +iptables -w -t mangle -A PREROUTING -p tcp -m socket --transparent -j SSLH +iptables -w -t mangle -A OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH +iptables -w -t mangle -A SSLH --jump MARK --set-mark 0x1 +iptables -w -t mangle -A SSLH --jump ACCEPT +ip rule add fwmark 0x1 lookup 100 +ip route add local 0.0.0.0/0 dev lo table 100 +``` + +`piA_tproxy_rm.sh`: +``` piA_tproxy_rm.sh +iptables -w -t mangle -D PREROUTING -p tcp -m socket --transparent -j SSLH +iptables -w -t mangle -D OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH +iptables -w -t mangle -D SSLH --jump MARK --set-mark 0x1 +iptables -w -t mangle -D SSLH --jump ACCEPT +iptables -w -t mangle -X SSLH +ip rule del fwmark 0x1 lookup 100 +ip route del local 0.0.0.0/0 dev lo table 100 +``` + +Make them executable.. +``` +# chmod +rx piA_tproxy_add.sh +# chmod +rx piA_tproxy_rm.sh +``` + +Now run the "add" script on Pi A! +``` +# piA_tproxy_add.sh +# piA_tproxy_rm.sh +``` + +Configure iptables for piB +-------------------------- + +``` +# nano /usr/local/sbin/[filename.sh] +``` + +piB_tproxy_add.sh +``` +iptables -t mangle -N SSLHSSL +iptables -t mangle -A OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL +iptables -t mangle -A SSLHSSL --jump MARK --set-mark 0x1 +iptables -t mangle -A SSLHSSL --jump ACCEPT +ip rule add fwmark 0x1 lookup 100 +ip route add default via 192.168.1.124 table 100 +ip route flush cache +``` + +piB_tproxy_add.sh +``` +iptables -t mangle -D OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL +iptables -t mangle -D SSLHSSL --jump MARK --set-mark 0x1 +iptables -t mangle -D SSLHSSL --jump ACCEPT +iptables -t mangle -X SSLHSSL +ip rule del fwmark 0x1 lookup 100 +ip route del default via 192.168.1.124 table 100 +ip route flush cache +``` + +Make them executable.. +``` +# chmod +rx piB_tproxy_add.sh +# chmod +rx piB_tproxy_rm.sh +``` + +Now run the "add" script on Pi B! +``` +# piB_tproxy_add.sh +# piB_tproxy_rm.sh +``` +  +Testing +------- +  +* Getting to sshd on PiA + +I did this test using 4G from my phone (outside the LAN) + +To simulate this I use `proxytunnel`. External port 443 is forwarded by my router to 4433. I need to arrive at `sslh` (port 4433) with ssh encrypted as TLS (hence I use the -e switch) and the `sni_hostname` set to ssh.example.com so that `sslh` will demultiplex to `stunnel` (port 4480) which will decrypt and forward to ssh server on PiA… see `sslh.cfg` and `stunnel.conf`. + +The first IP:port is just a free HTTPS proxy I found on https://free-proxy-list.net +I execute this command from a terminal window.. + +``` +# proxytunnel -v -e -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443 +``` +  +* Getting to sshd on PiB + +I did this test using 4G from my phone (outside the LAN) + +My smartphone telecom provider blocks ssh over port 443 so I need to use `proxytunnel` to encrypt. +Use the Proxytunnel `-X` switch to encrypt from local proxy to destination only so by the time we get to the destination it is unencrypted and `sslh` will see the ssh protocol and demultiplex to PiB as per `sslh.cfg`. + +``` +# proxytunnel -v -X -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443 +``` +  +Now when you test it all look at the output in daemon.log like this: +``` +# grep -i 'ssl' /var/log/daemon.log +``` +  +You should see that the IP address and port from the “connection from” and “forwarded from” fields are the same. +  +Special thanks and appreciation to Michael Yelsukov without whom I would never have got this working. +  +Any feedback or corrections very welcome!