Tuesday, January 10, 2017

Installing SSLH onto Synology DSM6 or DSM7 for easy HTTPS, OpenVPN and SSH through corporate firewall

When you are in a corporate LAN, access to the outside is often restricted. HTTPS is allowed, yet other applications like SSH or OpenVPN might not be. As well, you want to run several of these services onto your Synology box. Choosing which one will use TCP port 443 could be a hard judgement to make. Luckily, there is SSLH to the rescue.

Note: with DSM7, it is no longer possible by default to open port 443 (which is below 1024) with the provided user (sh-sslh). You will get a "0.0.0.0:https:bind: Permission denied" error when changing port 30000 to 443. The fix is to change your portforwarding in your router to NAS_IP:30000 instead. The below tutorial was making the SSLH service listen to port 443 with the other services running on localhost:443.

 As a consequence, changing the sslh.cfg file and the NGINX files is no longer needed, so everything below can be ignorred.

Caution! Playing around with your SSL port could possibly break the access to your DSM if nginx fails to restart.
Therefore, I strongly recommend the following actions:

  • Never put your SSH server to listen ONLY onto 127.0.0.1. Never.
  • Never change all of your services (HTTPS, SSH, OpenVPN) at the same time, unless your 100% sure your config is correct.
  • Open up temporarily the Telnet service, just in case
  • Make sure you don't lock yourself out with your firewall rules onto your Synology
  • Backup the original config files before starting making changes. cp -p config.file config.file.ori will do.

0. Make sure you have the Synocommunity repository installed under your Package Center. Open up an SSH connection to your Synology and make yourself root.
1. Download and install the SSLH package. By default, the configuration file is at /usr/local/sslh/var/sslh.cfg and needs to be adapted. Make a backup copy of the file first.
2. Go to your terminal and edit the file with vi
vi /usr/local/sslh/var/sslh.cfg
3. Change the IP address (under host: "0.0.0.0") to your IPv4 address of the Synology. Do NOT yet change the port. Leave it onto 30000 as default.
4. Check if the services listed are using the correct port numbers and adapt if needed. Save the file.
5. Now go to the Package Center of DSM again and stop and start SSLH.
6. Go to your terminal and verify if SSLH is running properly by running the command:
netstat -an | grep 30000
Expected output:
root@server:/# netstat -an | grep 30000
tcp        0      0 192.168.0.5:30000       0.0.0.0:*               LISTEN
7. Check if HTTPS is currently running and listening to the IPv4 address:
netstat -an | grep 443
Expected output:
root@server:/# netstat -an | grep 443
tcp        0      0 0.0.0.0:443           0.0.0.0:*               LISTEN
tcp6       0      0 :::443                  :::*                    LISTEN
This now means that nginx is still listening onto the IPv4 address for HTTPS. Let's change that.
8. Open your web browser and browse to your Synology IP for both HTTP as for HTTPS: http://yourip and https://yourip
Confirm that this is working properly.
9. Go to the nginx config directory (/usr/syno/share/nginx) and backup the following files: DSM.mustache, WWWService.mustache and server.mustache
10. Use vi to change nginx from listening to 0.0.0.0:443 to 127.0.0.1:443 only, by making it look like this:
listen 127.0.0.1:443

DSM.mustache
WWWService.mustache
server.mustache
Repeat this for all 3 files and save your changes.
11. Restart nginx from the command line:
synoservicecfg --restart nginx
You can monitor into /var/log/synoservice.log if things restarted properly. Typical output should be:
2017-01-10T19:04:33+01:00 server synoservicecfg: service_restart.c:21 synoservice: restart [nginx] ...
2017-01-10T19:04:34+01:00 server synoservicecfg: service_restart.c:52 synoservice: finish restart [nginx].
12. Verify that HTTPS is only listening onto localhost (127.0.0.1)
root@server:/usr/syno/share/nginx# netstat -an | grep 443
tcp        0      0 127.0.0.1:443           0.0.0.0:*               LISTEN
tcp6       0      0 :::443                  :::*                    LISTEN
13. Refresh the browser screen for your HTTPS. It should not show any website anymore.
14. As a final step, change your SSLH config file and set the port to listen from 30000 to 443. Restart SSLH in the Package Center by doing a stop and start. Or do it command line:
synoservicecfg --restart pkgctl-sslh
15. Verify that SSLH is now listening onto your IPv4 address with port 443.
root@server:/usr/syno/share/nginx# netstat -an | grep 443
tcp        0      0 192.168.0.5:443         0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:443           0.0.0.0:*               LISTEN
tcp6       0      0 :::443                  :::*                    LISTEN
16. You can test your HTTPS, OpenVPN and SSH. All should perfectly route through the SSLH multiplexer.




12 comments:

  1. Hello,
    could you explain the followig lines in detail?
    10. Use vi to change nginx from listening to 0.0.0.0:443 to 127.0.0.1:443 only, by making it look like this:
    listen 127.0.0.1:443 default_server
    Repeat this for all 3 files and save your changes.
    How exactly do these 3 files DSM.mustache, WWWService.mustache and server.mustache look like?

    ReplyDelete
  2. Try doing it under sudo -i
    Here it worked...

    ReplyDelete
  3. To whom it may still concern.

    If you use builtin reverse proxy and set anything to listen to 443, you also have to edit Portal.mustache, setting

    listen {{port}}{{#https}}

    to

    listen 127.0.0.1:{{port}}{{#https}}

    This will block usage of other ports, though.

    ReplyDelete
  4. Yet another addition — you might need to also edit /volume1/@appstore/WebStation/misc/VirtualHost-nginx.mustache, for some reason (might be new WebStation version 2.1.9) it is used instead of WWWServer.mustache. You'll need

    {{#port.https}}
    listen 127.0.0.1:{{.}}

    ReplyDelete
  5. Thanks for the detailed guide. Unfortunately it doesn't really work for me, even when I consider the additional info from @Mangetsu. Once I've setup everything as described, my DS File / DS Note App keeps telling that the SSL certi in use is not trustworthy which is not true. Once I revert the whole thing, it's working fine but of course w/o sslh enabled which is a bit a pity. Any advice? I am running latest DSM version 6.2.3-25426 Update 2 and WebStation 2.1.9. I tried with sslh from the synocommunity (v1.21c-5). Thanks.

    ReplyDelete
  6. Thanks works like a charm for me.
    Great documentation - easy to follow.

    I have IPv4 and IPv6. How to install the helper so IPv6 443 port gains the same feature ?
    [e.g. OpenVPN server is accessible via IPv6 address Port 443 ?]

    Thanks for your help

    ReplyDelete
    Replies
    1. I found following solution:

      In my case the IPv6 address is ever changing;
      In case you are using DHCP your IPv4 might changes as well.
      Replacing the 0.0.0.0 to your {local IP} causes effort all the time to adapt.

      So it might be better to shift the services using the 443 port to another port (e.g. 444), so I did

      Make sure your firewall allows traffic on 443 the new 444 port.
      Make sure your telnet port is open so you can recover in case something goes down the drain.
      At the same time avoid someone can access telnet from the outside world.
      Go step by step and make sure you understand what is happening at that time.

      First shift the services from 443 to 444

      Change DSM.mustache to

      ...
      server {
      listen 444 ssl{{#spdy}} http2{{/spdy}}{{#reuseport}} reuseport{{/reuseport}};
      listen [::]:444 ssl{{#spdy}} http2{{/spdy}}{{#reuseport}} reuseport{{/reuseport}};
      ...

      WWWService.mustache to
      ...
      server {
      listen 444 default_server ssl{{#reuseport}} reuseport{{/reuseport}};
      listen [::]:444 default_server ssl{{#reuseport}} reuseport{{/reuseport}};
      ...

      server.mustache
      ...
      server {
      listen 444 ssl{{#https.http2}} http2{{/https.http2}}{{#reuseport}} reuseport{{/reuseport}};
      listen [::]:444 ssl{{#https.http2}} http2{{/https.http2}}{{#reuseport}} reuseport{{/reuseport}};
      ...

      Restart the service

      synoservicecfg --restart nginx

      Now you should observe the https service is on port 444

      /usr/local/sslh/var$ netstat -anp | grep 444
      Password:
      tcp 0 0 0.0.0.0:444 0.0.0.0:* LISTEN 25785/nginx: master
      tcp6 0 0 :::444 :::* LISTEN 25785/nginx: master

      You should be able to use your browser to access the https now both ipv4 and ipv6 on port 444

      Now adapt the sslh. It
      - shall listen to port 443 from ANY ipv4 and Ipv6
      - forward to port 444 for https (tls) service

      /usr/local/sslh/var$ cat sslh.cfg
      ...
      # Change hostname with your external address name.
      listen:
      (
      { host: "0.0.0.0"; port: "443"; },
      { host: "::"; port: "443"; }
      );

      protocols:
      (
      ...
      { name: "tls"; host: "localhost"; port: "444"; log_level: 0; },
      ...
      );

      After restarting the sslh package

      synoservicecfg --restart pkgctl-sslh

      you should see sslh is listening to both IPv4 and IPv6

      /usr/local/sslh/var$ sudo netstat -anp | grep 443
      tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 26404/sslh
      tcp6 0 0 :::443 :::* LISTEN 26404/sslh

      That is all.

      Delete
  7. great write up. I think it's safe and more future proof to just forward port 443 from the modem to sslh's default port 30000 without changing nginx config.

    ReplyDelete
    Replies
    1. Yes, that would be best — unless you want to use same port in lan and outside (with hairpinning or split horizon dns).

      Delete
  8. Yes, that would be best — unless you want to use same port in lan and outside (with hairpinning or split horizon dns).

    ReplyDelete
  9. Your guide is AWESOME! I've been looking for a solution that works with all for so loooooong. Cheers!
    The only one that doesn't work is OpenVPN.
    My company has ONLY ports 80/443 open. I see in the OpenVPN server log that a random port is used for TLS even though I supplied only port 1194:
    Aug 10 17:11:04 vpnserver1[26417]: 192.168.x.x:51870 [UNDEF]

    ReplyDelete
  10. Another useful idea I just had. Posting here for sake of completeness.

    One problem with this setup is that you can't use Access Control Profile feature at all, since nginx sees that all traffic comes from 127.0.0.1. Another is that you might not need traffic from LAN going through sslh.

    Prerequisites: router with port forwarding and split-horizon DNS.
    You have to set DNS in LAN to point mydomain.tld → local_ip

    Then you _do not_ edit .mustache files. Nginx continues to listen on 0.0.0.0:443
    SSLH set up to listen on e.g. 442.
    Router forwards 443 port to 442.

    This way from inside you go directly to local_ip:443, and from outside to external_ip:443 → local_ip:442 → local_ip:443. You can use either server eth address or 127.0.0.1.

    This also gives limited ACP functionality — you can block access from outside to some critical services, like DSM UI or PiHole. ACP rules:
    local_server_ip — Deny //Or 127.0.0.1 if you use that. Everything that goes through sslh is blocked.
    LAN and VPN — Allow
    All — Deny

    ReplyDelete