{"id":332,"date":"2016-04-11T20:10:23","date_gmt":"2016-04-12T03:10:23","guid":{"rendered":"http:\/\/www.mjblythe.com\/hacks\/?p=332"},"modified":"2021-12-22T00:01:01","modified_gmt":"2021-12-22T07:01:01","slug":"better-sslh","status":"publish","type":"post","link":"http:\/\/www.mjblythe.com\/hacks\/2016\/04\/better-sslh\/","title":{"rendered":"Better sslh"},"content":{"rendered":"<p>For those that don&#8217;t know, <a href=\"https:\/\/github.com\/yrutschle\/sslh\">sslh<\/a> is a TCP port multiplexer. This basically means that you can serve both <code>https<\/code> and <code>ssh<\/code> traffic from the same port. It&#8217;s most useful for circumventing corporate firewalls that block TCP port 22 (i.e. <code>ssh<\/code>), but allow TCP port 443 (i.e. <code>https<\/code>) by serving both on TCP port 443.<\/p>\n<p>In the default configuration, however, all connections that go through <code>sslh<\/code> look to <code>ssh<\/code> or <code>apache<\/code> as if they came from <code>localhost<\/code>. This isn&#8217;t ideal if you want to run something like <code>denyhosts<\/code> or <code>fail2ban<\/code> to block malicious <code>ssh<\/code> login attempts.<\/p>\n<p><code>sslh<\/code> does have an option to do &#8220;transparent&#8221; proxying so <code>ssh<\/code> and <code>apache<\/code> think that the connections have come from the right place. In this post, I&#8217;ll describe how I set this up on my machine.<\/p>\n<p><!--more--><\/p>\n\n<h2>Initial Setup<\/h2>\n<p>In Ubuntu, <code>sslh<\/code> can be installed by running this:<\/p>\n<blockquote><p><code>&gt; sudo apt-get install sslh<\/code><\/p><\/blockquote>\n<p>When installed, the default options (in <code>\/etc\/default\/sslh<\/code>) look like this:<\/p>\n<blockquote><p><code>DAEMON_OPTS=\"--user sslh --listen &lt;change-me&gt;:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:443 --pidfile \/var\/run\/sslh\/sslh.pid\"<\/code><\/p><\/blockquote>\n<p>To get it running at all, you need to switch <code>apache<\/code> (or whatever web server you&#8217;re using) to serve https on a different port. I&#8217;ve selected 8443. Once this change is made (in <code>\/etc\/apache2\/ports.conf<\/code> and <code>\/etc\/apache2\/sites-enabled\/default-ssl.conf<\/code> for me), then you&#8217;ll need to update the sslh config too.<\/p>\n<p><code>sslh<\/code> config (<code>\/etc\/default\/sslh<\/code>) without transparent proxying:<\/p>\n<blockquote><p><code>DAEMON=\/usr\/sbin\/sslh<br \/>\nDAEMON_OPTS=\"--user sslh --listen 0.0.0.0:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:8443 --pidfile \/var\/run\/sslh\/sslh.pid\"<\/code><\/p><\/blockquote>\n<p>However, all <code>https<\/code> and <code>ssh<\/code> traffic (on port 443) looks like it comes from <code>localhost<\/code>, since we haven&#8217;t enabled transparent proxying yet.<\/p>\n<h2>Getting Transparent Proxying working<\/h2>\n<p>As will become obvious, I had some difficulty getting transparent proxying working. This section consists mostly of the notes I took while trying various things, and steps I took to debug why things aren&#8217;t working. If you&#8217;re only interested in copying my working configuration, skip to the <a href=\"#Final_working_configuration\">next section<\/a>.<\/p>\n<p>First step, is to have ssh listen on another port. From the <a href=\"https:\/\/github.com\/yrutschle\/sslh#transparent-proxy-support\">github page<\/a>:<\/p>\n<blockquote><p>Note that these rules will prevent from connecting directly to ssh on the port 22, as packets coming out of sshd will be tagged. If you need to retain direct access to ssh on port 22 as well as through sslh, you can make sshd listen to 22 AND another port (e.g. 2222), and change the above rules accordingly.<\/p><\/blockquote>\n<p><code>\/etc\/ssh\/sshd_config<\/code> before:<\/p>\n<blockquote><p><code>Port 22<\/code><\/p><\/blockquote>\n<p>after:<\/p>\n<blockquote><p><code>Port 22<br \/>\nPort 2222<\/code><\/p><\/blockquote>\n<p>And corresponding change to <code>\/etc\/default\/sslh<\/code>:<\/p>\n<blockquote><p><code>DAEMON_OPTS=\"--user sslh --listen 0.0.0.0:443 --ssh 127.0.0.1:2222 --ssl 127.0.0.1:8443 --pidfile \/var\/run\/sslh\/sslh.pid\"<\/code><\/p><\/blockquote>\n<p>I tested port 22 from my offsite webhosting server, and it still works.\u00a0 I couldn&#8217;t test port 2222 (because didn&#8217;t set up port forwarding rules), but I confirmed that sslh is using port 2222:<\/p>\n<blockquote><p><code>&gt; pgrep sslh |xargs ps<br \/>\nPID TTY\u00a0\u00a0\u00a0\u00a0\u00a0 STAT\u00a0\u00a0 TIME COMMAND<br \/>\n20397 ?\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Ss\u00a0\u00a0\u00a0\u00a0 0:00 \/usr\/sbin\/sslh --foreground --user sslh --listen 0.0.0.0 443 --ssh 127.0.0.1 2222 --ssl 12<br \/>\n20398 ?\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 S\u00a0\u00a0\u00a0\u00a0\u00a0 0:00 \/usr\/sbin\/sslh --foreground --user sslh --listen 0.0.0.0 443 --ssh 127.0.0.1 2222 --ssl 12<\/code><\/p><\/blockquote>\n<p>Next change, don&#8217;t use the loopback address (my machine&#8217;s hostname is &#8220;bruce&#8221;&#8230;I use <a href=\"http:\/\/www.imdb.com\/title\/tt0266543\/fullcredits?ref_=tt_cl_sm#cast\">characters from Finding Nemo<\/a> for my machines):<\/p>\n<blockquote><p><code>DAEMON_OPTS=\"--user sslh --listen 0.0.0.0:443 --ssh bruce:2222 --ssl bruce:8443 --pidfile \/var\/run\/sslh\/sslh.pid\"<\/code><\/p><\/blockquote>\n<p>&#8230;still works&#8230;<\/p>\n<p>ok, moment of truth&#8230;let&#8217;s add the <code>--transparent<\/code> switch and set up the <code>iptables<\/code> rules.<\/p>\n<blockquote><p><code># iptables -t mangle -N SSLH<br \/>\n# iptables -t mangle -A  OUTPUT --protocol tcp --out-interface eth0 --sport 2222 --jump SSLH<br \/>\n# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 8443 --jump SSLH<br \/>\n# iptables -t mangle -A SSLH --jump MARK --set-mark 0x1<br \/>\n# iptables -t mangle -A SSLH --jump ACCEPT<br \/>\n# ip rule add fwmark 0x1 lookup 100<br \/>\n# ip route add local 0.0.0.0\/0 dev lo table 100<\/code><\/p><\/blockquote>\n<p>&#8230;and it doesn&#8217;t work.<\/p>\n<p>Maybe it&#8217;s a capabilities issue?<\/p>\n<blockquote><p><code>&gt; getcap \/usr\/sbin\/sslh<\/code><\/p><\/blockquote>\n<p>&#8230;strong possibility<\/p>\n<blockquote><p><code>&gt; sudo setcap cap_net_admin+ep \/usr\/sbin\/sslh<br \/>\n&gt; getcap \/usr\/sbin\/sslh<br \/>\n\/usr\/sbin\/sslh = cap_net_admin+ep<\/code><\/p><\/blockquote>\n<p>&#8230;still doesn&#8217;t work.<\/p>\n<p>Turns out that &#8220;bruce&#8221; doesn&#8217;t resolve the way I expect&#8230;need &#8220;bruce.&#8221; like this:<\/p>\n<blockquote><p><code>DAEMON_OPTS=\"--transparent --user sslh --listen 0.0.0.0:443 --ssh bruce.:2222 --ssl bruce.:8443 --pidfile \/var\/run\/sslh\/sslh.pid\"<\/code><\/p><\/blockquote>\n<p>works now!<\/p>\n<p>Fails after a reboot&#8230;I guess <code>iptables<\/code> loses the settings?\u00a0 Works again after re-running the <code>iptables<\/code> commands and restarting <code>sslh<\/code> and <code>apache2<\/code><\/p>\n<p>Hmmm&#8230;getting this on reboot (from <code>\/var\/log\/syslog<\/code>):<\/p>\n<blockquote><p>Sep 3 18:02:00 bruce sslh[1050]: Temporary failure in name resolution `bruce.:2222&#8242;<br \/>\nSep 3 18:02:00 bruce sslh[1050]: Temporary failure in name resolution `bruce.&#8217;<\/p><\/blockquote>\n<p>Switching &#8220;bruce.&#8221; to &#8220;127.0.0.1&#8221; makes sslh not work for some reason, so I guess this just has to start after some other service.<\/p>\n<p><a href=\"https:\/\/fedoramagazine.org\/systemd-unit-dependencies-and-order\/\">Here&#8217;s<\/a> how we&#8217;ll do it, I think.<\/p>\n<p>The file lives here: <code>lib\/systemd\/system\/sslh.service<\/code><\/p>\n<p><a href=\"https:\/\/www.freedesktop.org\/wiki\/Software\/systemd\/NetworkTarget\/\">According to this<\/a>, it sounds like we might want <code>After=network-online.target<\/code>?<\/p>\n<p>Yes, making that change seems to fix it!<\/p>\n<h2>Final working configuration<\/h2>\n<p>First, let&#8217;s recap what we&#8217;ve done up to this point:<\/p>\n<ol>\n<li>We&#8217;ve set Apache\/nginx\/whatever to listen on port 8443 for https<\/li>\n<li>sslh is installed<\/li>\n<\/ol>\n<p>Next, change <code>ssh<\/code> to listen on port 22 and 2222 by editing <code>\/etc\/ssh\/sshd_config<\/code>:<\/p>\n<blockquote><p><code>Port 22<br \/>\nPort 2222<\/code><\/p><\/blockquote>\n<p>Run <code>sudo service ssh restart<\/code> to make that change take effect.<\/p>\n<p>Modify the command line options in <code>\/etc\/default\/sslh<\/code> (substituting your hostname instead of <code>bruce<\/code>):<\/p>\n<blockquote><p><code>DAEMON_OPTS=\"--transparent --user sslh --listen 0.0.0.0:443 --ssh bruce.:2222 --ssl bruce.:8443 --pidfile \/var\/run\/sslh\/sslh.pid\"<\/code><\/p><\/blockquote>\n<p>Also, since that command wants to resolve the &#8220;bruce.&#8221; domain name, we need to wait until the network comes online, not just after the networking stack comes up. So, modify this line in <code>lib\/systemd\/system\/sslh.service<\/code>:<\/p>\n<blockquote><p><code>After=network-online.target<\/code><\/p><\/blockquote>\n<p>Finally, the <code>sslh<\/code> executable needs some additional permissions, so add those:<\/p>\n<blockquote><p><code>&gt; sudo setcap cap_net_admin+ep \/usr\/sbin\/sslh<\/code><\/p><\/blockquote>\n<p>Put the <code>iptables<\/code> rules in place (as root):<\/p>\n<blockquote><p><code># iptables -t mangle -N SSLH<br \/>\n# iptables -t mangle -A  OUTPUT --protocol tcp --out-interface eth0 --sport 2222 --jump SSLH<br \/>\n# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 8443 --jump SSLH<br \/>\n# iptables -t mangle -A SSLH --jump MARK --set-mark 0x1<br \/>\n# iptables -t mangle -A SSLH --jump ACCEPT<br \/>\n<\/code><\/p><\/blockquote>\n<p><a href=\"http:\/\/askubuntu.com\/questions\/119393\/how-to-save-rules-of-the-iptables\">Install software to load our firewall rules at boot-up<\/a>:<\/p>\n<blockquote><p><code>sudo apt-get install iptables-persistent<\/code><\/p><\/blockquote>\n<p>At installation time, it prompts to save current <code>iptables<\/code> rules.<\/p>\n<p>Alternatively, if <code>iptables-persistent<\/code> is already installed, run this as root:<\/p>\n<blockquote><p><code># iptables-save &gt; \/etc\/iptables\/rules.v4<\/code><\/p><\/blockquote>\n<p>If you&#8217;re using IPv6, you&#8217;ll also need to repeat these steps with <code>ip6tables<\/code>.<\/p>\n<p>Get the <code>ip<\/code> commands to run at boot-up&#8230;(similar to <a href=\"https:\/\/debian-administration.org\/article\/445\/Getting_IPTables_to_survive_a_reboot\">this<\/a>)<\/p>\n<p>I created the following file as <code>\/etc\/network\/if-up.d\/sslh<\/code>:<\/p>\n<blockquote><p><code>#!\/bin\/sh<br \/>\nip rule add fwmark 0x1 lookup 100<br \/>\nip route add local 0.0.0.0\/0 dev lo table 100<\/code><\/p><\/blockquote>\n<p>It should be owned by root and be executable.<\/p>\n<p>Set <code>sslh<\/code> to start by default:<\/p>\n<blockquote><p><code>&gt; sudo systemctl enable sslh<\/code><\/p><\/blockquote>\n<p>Ok, that should be all the configuration! Reboot and verify:<\/p>\n<blockquote><p><code>&gt; sudo iptables-save<br \/>\n# Generated by iptables-save v1.4.21 on Sat Sep 3 17:44:08 2016<br \/>\n*mangle<br \/>\n:PREROUTING ACCEPT [203520132:32153396803]<br \/>\n:INPUT ACCEPT [203513474:32153036883]<br \/>\n:FORWARD ACCEPT [0:0]<br \/>\n:OUTPUT ACCEPT [309094595:519342799063]<br \/>\n:POSTROUTING ACCEPT [310985125:519865479412]<br \/>\n:SSLH - [0:0]<br \/>\n-A OUTPUT -o eth0 -p tcp -m tcp --sport 2222 -j SSLH<br \/>\n-A OUTPUT -o eth0 -p tcp -m tcp --sport 8443 -j SSLH<br \/>\n-A SSLH -j MARK --set-xmark 0x1\/0xffffffff<br \/>\n-A SSLH -j ACCEPT<br \/>\nCOMMIT<br \/>\n# Completed on Sat Sep 3 17:44:08 2016<br \/>\n&gt; ip rule list<br \/>\n0: from all lookup local<br \/>\n32765: from all fwmark 0x1 lookup 100<br \/>\n32766: from all lookup main<br \/>\n32767: from all lookup default<\/code><\/p><\/blockquote>\n<p>I&#8217;m not sure how to check the <code>ip route<\/code> command, but if that <code>fwmark<\/code> rule is there, then we can be confident that the <code>ip route<\/code> command ran as well.<\/p>\n<p>Note:On my system, it looks like the <code>ip rule<\/code> command is run several times during boot-up (I guess I have several if-up events?) This doesn&#8217;t seem to have any negative impact.<\/p>\n<hr \/>\n<p>Update Feb 6 2018:<\/p>\n<p>I&#8217;ve tried several times to tweak things so that it all works properly on reboot, but it seem that I never was able to get sslh to start after any domain resolution stuff, so <code>bruce<\/code> still resolves to <code>127.0.0.1<\/code> or something.\u00a0 I always have to run <code>service sslh restart<\/code> after reboot to get things working.<\/p>\n<p>Also, now that I&#8217;m using haproxy running on a different host (FIXME add link) to do this ssl\/ssh multiplexing, I don&#8217;t actually need this anymore.\u00a0 So I&#8217;m removing it.<\/p>\n<hr \/>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For those that don&#8217;t know, sslh is a TCP port multiplexer. This basically means that you can serve both https and ssh traffic from the same port. It&#8217;s most useful for circumventing corporate firewalls that block TCP port 22 (i.e. ssh), but allow TCP port 443 (i.e. https) by serving both on TCP port 443. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[7,11,15],"tags":[],"class_list":["post-332","post","type-post","status-publish","format-standard","hentry","category-howto","category-linux","category-trial-and-error"],"_links":{"self":[{"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/posts\/332","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/comments?post=332"}],"version-history":[{"count":23,"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/posts\/332\/revisions"}],"predecessor-version":[{"id":835,"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/posts\/332\/revisions\/835"}],"wp:attachment":[{"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/media?parent=332"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/categories?post=332"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.mjblythe.com\/hacks\/wp-json\/wp\/v2\/tags?post=332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}