diff --git a/README.md b/README.md index 41e5cf8..774094e 100644 --- a/README.md +++ b/README.md @@ -24,431 +24,18 @@ systemd support, transparent proxying, chroot, logging, IPv4 and IPv6, a fork-based and a select-based model, and more. +Install +======= -Compile and install -=================== - -Dependencies ------------- - -`sslh` uses [libconfig](http://www.hyperrealm.com/libconfig/) -and [libwrap](http://packages.debian.org/source/unstable/tcp-wrappers). - -For Debian, these are contained in packages `libwrap0-dev` and -`libconfig8-dev`. - -For OpenSUSE, these are contained in packages libconfig9 and -libconfig-dev in repository - - -For Fedora, you'll need packages `libconfig` and -`libconfig-devel`: - - yum install libconfig libconfig-devel - -If you can't find `libconfig`, or just don't want a -configuration file, set `USELIBCONFIG=` in the Makefile. - -If you want to rebuild `sslh-conf.c` (after a `make -distclean` for example), you will also need to add -[conf2struct](https://www.rutschle.net/tech/conf2struct/README.html) -to your path. - -Compilation ------------ - -After this, the Makefile should work: - - make install - -There are a couple of configuration options at the beginning -of the Makefile: - -* `USELIBWRAP` compiles support for host access control (see - `hosts_access(3)`), you will need `libwrap` headers and - library to compile (`libwrap0-dev` in Debian). - -* `USELIBCONFIG` compiles support for the configuration - file. You will need `libconfig` headers to compile - (`libconfig8-dev` in Debian). - -* `USESYSTEMD` compiles support for using systemd socket activation. - You will need `systemd` headers to compile (`systemd-devel` in Fedora). - -Binaries --------- - -The Makefile produces two different executables: `sslh-fork` -and `sslh-select`: - -* `sslh-fork` forks a new process for each incoming connection. -It is well-tested and very reliable, but incurs the overhead -of many processes. -If you are going to use `sslh` for a "small" setup (less than -a dozen ssh connections and a low-traffic https server) then -`sslh-fork` is probably more suited for you. - -* `sslh-select` uses only one thread, which monitors all connections -at once. It is more recent and less tested, but only incurs a 16 -byte overhead per connection. Also, if it stops, you'll lose all -connections, which means you can't upgrade it remotely. -If you are going to use `sslh` on a "medium" setup (a few thousand ssh -connections, and another few thousand ssl connections), -`sslh-select` will be better. - -If you have a very large site (tens of thousands of connections), -you'll need a vapourware version that would use libevent or -something like that. - - -Installation ------------- - -* In general: - - make - cp sslh-fork /usr/local/sbin/sslh - cp basic.cfg /etc/sslh.cfg - vi /etc/sslh.cfg - -* For Debian: - - cp scripts/etc.init.d.sslh /etc/init.d/sslh - -* For CentOS: - - cp scripts/etc.rc.d.init.d.sslh.centos /etc/rc.d/init.d/sslh - - -You might need to create links in /etc/rc.d so that the server -start automatically at boot-up, e.g. under Debian: - - update-rc.d sslh defaults +Please refer to the [install guide](doc/INSTALL.md). Configuration ============= -If you use the scripts provided, sslh will get its -configuration from /etc/sslh.cfg. Please refer to -example.cfg for an overview of all the settings. +Please refer to the [configuration guide](doc/config.md). -A good scheme is to use the external name of the machine in -`listen`, and bind `httpd` to `localhost:443` (instead of all -binding to all interfaces): that way, HTTPS connections -coming from inside your network don't need to go through -`sslh`, and `sslh` is only there as a frontal for connections -coming from the internet. -Note that 'external name' in this context refers to the -actual IP address of the machine as seen from your network, -i.e. that that is not `127.0.0.1` in the output of -`ifconfig(8)`. - -Libwrap support ---------------- - -Sslh can optionally perform `libwrap` checks for the sshd -service: because the connection to `sshd` will be coming -locally from `sslh`, `sshd` cannot determine the IP of the -client. - -OpenVPN support ---------------- - -OpenVPN clients connecting to OpenVPN running with -`-port-share` reportedly take more than one second between -the time the TCP connection is established and the time they -send the first data packet. This results in `sslh` with -default settings timing out and assuming an SSH connection. -To support OpenVPN connections reliably, it is necessary to -increase `sslh`'s timeout to 5 seconds. - -Instead of using OpenVPN's port sharing, it is more reliable -to use `sslh`'s `--openvpn` option to get `sslh` to do the -port sharing. - -Using proxytunnel with sslh ---------------------------- - -If you are connecting through a proxy that checks that the -outgoing connection really is SSL and rejects SSH, you can -encapsulate all your traffic in SSL using `proxytunnel` (this -should work with `corkscrew` as well). On the server side you -receive the traffic with `stunnel` to decapsulate SSL, then -pipe through `sslh` to switch HTTP on one side and SSL on the -other. - -In that case, you end up with something like this: - - ssh -> proxytunnel -e ----[ssh/ssl]---> stunnel ---[ssh]---> sslh --> sshd - Web browser -------------[http/ssl]---> stunnel ---[http]--> sslh --> httpd - -Configuration goes like this on the server side, using `stunnel3`: - - stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- \ - sslh -i --http localhost:80 --ssh localhost:22 - -* stunnel options: - * `-f` for foreground/debugging - * `-p` for specifying the key and certificate - * `-d` for specifying which interface and port - we're listening to for incoming connexions - * `-l` summons `sslh` in inetd mode. - -* sslh options: - * `-i` for inetd mode - * `--http` to forward HTTP connexions to port 80, - and SSH connexions to port 22. - -Capabilities support --------------------- - -On Linux (only?), you can compile sslh with `USELIBCAP=1` to -make use of POSIX capabilities; this will save the required -capabilities needed for transparent proxying for unprivileged -processes. - -Alternatively, you may use filesystem capabilities instead -of starting sslh as root and asking it to drop privileges. -You will need `CAP_NET_BIND_SERVICE` for listening on port 443 -and `CAP_NET_ADMIN` for transparent proxying (see -`capabilities(7)`). - -You can use the `setcap(8)` utility to give these capabilities -to the executable: - - sudo setcap cap_net_bind_service,cap_net_admin+pe sslh-select - -Then you can run sslh-select as an unpriviledged user, e.g.: - - sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443 - -Caveat: `CAP_NET_ADMIN` does give sslh too many rights, e.g. -configuring the interface. If you're not going to use -transparent proxying, just don't use it (or use the libcap method). - -Transparent proxy support -------------------------- - -On Linux and FreeBSD you can use the `--transparent` option to -request transparent proxying. This means services behind `sslh` -(Apache, `sshd` and so on) will see the external IP and ports -as if the external world connected directly to them. This -simplifies IP-based access control (or makes it possible at -all). - -Linux: - -`sslh` needs extended rights to perform this: you'll need to -give it `CAP_NET_ADMIN` capabilities (see appropriate chapter) -or run it as root (but don't do that). - -The firewalling tables also need to be adjusted as follows. -I don't think it is possible to have `httpd` and `sslh` both listen to 443 in -this scheme -- let me know if you manage that: - - # 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 - -Tranparent proxying with IPv6 is similarly set up as follows: - - # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination - # Not sure if this is needed for ipv6 though - 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 - 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 - -Explanation: -To be able to use `localhost` as destination in your sslh config along with transparent proxying -you have to allow routing of loopback addresses as done above. -This is something you usually should not do (see [this stackoverflow post](https://serverfault.com/questions/656279/how-to-force-linux-to-accept-packet-with-loopback-ip/656484#656484)) -The two `DROP` iptables rules emulate the behaviour of `route_localnet` set to off (with one small difference: -allowing the reroute-check to happen after the fwmark is set on packets destined for sslh). -See [this diagram](https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg) for a good visualisation -showing how packets will traverse the iptables chains. - -Note: -You have to run `sslh` as dedicated user (in this example the user is also named `sslh`), to not mess up with your normal networking. -These rules will allow you to connect directly to ssh on port -22 (or to any other service behind sslh) as well as through sslh on port 443. - -Also remember that iptables configuration and ip routes and -rules won't be necessarily persisted after you reboot. Make -sure to save them properly. For example in CentOS7, you would -do `iptables-save > /etc/sysconfig/iptables`, and add both -`ip` commands to your `/etc/rc.local`. - -FreeBSD: - -Given you have no firewall defined yet, you can use the following configuration -to have ipfw properly redirect traffic back to sslh - - /etc/rc.conf - firewall_enable="YES" - firewall_type="open" - firewall_logif="YES" - firewall_coscripts="/etc/ipfw/sslh.rules" - - -/etc/ipfw/sslh.rules - - #! /bin/sh - - # ssl - ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out - ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out - - # ssh - ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out - ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out - - # xmpp - ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out - ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out - - # openvpn (running on other internal system) - ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out - ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out - -General notes: - - -This will only work if `sslh` does not use any loopback -addresses (no `127.0.0.1` or `localhost`), you'll need to use -explicit IP addresses (or names): - - sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --ssl 192.168.0.1:4443 - -This will not work: - - sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:4443 - -Transparent proxying means the target server sees the real -origin address, so it means if the client connects using -IPv6, the server must also support IPv6. It is easy to -support both IPv4 and IPv6 by configuring the server -accordingly, and setting `sslh` to connect to a name that -resolves to both IPv4 and IPv6, e.g.: - - sslh --transparent --listen :443 --ssh insideaddr:22 - - /etc/hosts: - 192.168.0.1 insideaddr - 201::::2 insideaddr - -Upon incoming IPv6 connection, `sslh` will first try to -connect to the IPv4 address (which will fail), then connect -to the IPv6 address. - -Systemd Socket Activation -------------------------- -If compiled with `USESYSTEMD` then it is possible to activate -the service on demand and avoid running any code as root. - -In this mode any listen configuration options are ignored and -the sockets are passed by systemd to the service. - -Example socket unit: - - [Unit] - Before=sslh.service - - [Socket] - ListenStream=1.2.3.4:443 - ListenStream=5.6.7.8:444 - ListenStream=9.10.11.12:445 - FreeBind=true - - [Install] - WantedBy=sockets.target - -Example service unit: - - [Unit] - PartOf=sslh.socket - - [Service] - ExecStart=/usr/sbin/sslh -v -f --ssh 127.0.0.1:22 --ssl 127.0.0.1:443 - KillMode=process - CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID - PrivateTmp=true - PrivateDevices=true - ProtectSystem=full - ProtectHome=true - User=sslh - - -With this setup only the socket needs to be enabled. The sslh service -will be started on demand and does not need to run as root to bind the -sockets as systemd has already bound and passed them over. If the sslh -service is started on its own without the sockets being passed by systemd -then it will look to use those defined on the command line or config -file as usual. Any number of ListenStreams can be defined in the socket -file and systemd will pass them all over to sslh to use as usual. - -To avoid inconsistency between starting via socket and starting directly -via the service Requires=sslh.socket can be added to the service unit to -mandate the use of the socket configuration. - -Rather than overwriting the entire socket file drop in values can be placed -in /etc/systemd/system/sslh.socket.d/.conf with additional ListenStream -values that will be merged. - -In addition to the above with manual .socket file configuration there is an -optional systemd generator which can be compiled - systemd-sslh-generator - -This parses the /etc/sslh.cfg (or /etc/sslh/sslh.cfg file if that exists -instead) configuration file and dynamically generates a socket file to use. - -This will also merge with any sslh.socket.d drop in configuration but will be -overriden by a /etc/systemd/system/sslh.socket file. - -To use the generator place it in /usr/lib/systemd/system-generators and then -call systemctl daemon-reload after any changes to /etc/sslh.cfg to generate -the new dynamic socket unit. - -Fail2ban --------- - -If using transparent proxying, just use the standard ssh -rules. If you can't or don't want to use transparent -proxying, you can set `fail2ban` rules to block repeated ssh -connections from an IP address (obviously this depends -on the site, there might be legitimate reasons you would get -many connections to ssh from the same IP address...) - -See example files in scripts/fail2ban. Comments? Questions? ====================