mirror of
https://github.com/yrutschle/sslh.git
synced 2025-04-28 06:12:13 +03:00
273 lines
8.2 KiB
Markdown
273 lines
8.2 KiB
Markdown
Transparent Proxy to Two Hosts
|
||
==============================
|
||
|
||
Tutorial by Sean Warner. 19 June 2019 20:35
|
||
|
||
Aim
|
||
---
|
||
|
||
* 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.
|
||
* The solution here only works for my very specific use-case but hopefully others can adapt it to suits their needs.
|
||
|
||
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.
|
||
|
||

|
||
|
||
`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 Pi A
|
||
--------------------------
|
||
|
||
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/piA_tproxy_add.sh
|
||
```
|
||
``` piA_tproxy_add.sh
|
||
iptables -t mangle -N SSLH
|
||
iptables -t mangle -A PREROUTING -p tcp -m socket --transparent -j SSLH
|
||
iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH
|
||
iptables -t mangle -A SSLH --jump MARK --set-mark 0x1
|
||
iptables -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
|
||
```
|
||
|
||
```
|
||
# nano /usr/local/sbin/piA_tproxy_rm.sh
|
||
```
|
||
``` piA_tproxy_rm.sh
|
||
iptables -t mangle -D PREROUTING -p tcp -m socket --transparent -j SSLH
|
||
iptables -t mangle -D OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH
|
||
iptables -t mangle -D SSLH --jump MARK --set-mark 0x1
|
||
iptables -t mangle -D SSLH --jump ACCEPT
|
||
iptables -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 Pi B
|
||
--------------------------
|
||
|
||
```
|
||
# nano /usr/local/sbin/piB_tproxy_add.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
|
||
```
|
||
|
||
```
|
||
# nano /usr/local/sbin/piB_tproxy_rm.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!
|