Sish is a great
ngrok alternative to expose any locally hosted app over the web. With Sish you create temporary secure tunnels to locally hosted applications and allow others to intereact with the app with a publicly accessible web address. Its great to quickly expose a development app server without needing to deploy it and setup reverse proxies or mess around with firewalls. Sish can also be used to expose any TCP connection - e.g MySQL DB.
ssh and its port-forwarding features to establish the required tunnels to expose the internal app. This is a big difference from
ngrok where you don’t need a dedicated client to make the tunnel work.
- A registered domain. I will be using mysish.co for my deployment.
- A DNS Hosting service thats support wildcard subdomain. I will be using cloudflare
- API Access to the DNS records to automate the generation of LetsEncrypt SSL Certs.
- A Linux VPS or some kind of cloud instance with a public IP address.
docker-composeinstalled on the linux server.
- Inbound access to port 443 and one port for SSH (typically 22 but any other port will work)
ssh to the VPS or cloud host where you plan to deploy
Create Working directory
1 2 $ mkdir ~/sish $ cd ~/sish
All other commands below will be relative to this working directory.
Checkout the Sish Git
1 $ git clone https://github.com/antoniomika/sish src
This will create a new directory
src under our working directory
Prepare working directory
1 2 3 $ mkdir dnsrobocert keys letsencrypt log pubkeys ssl $ cp src/deploy/docker-compose.yml . $ cp src/deploy/le-config.yml dnsrobocert/dnsrobocert-config.yml
DnsRobocert is a dockeried app to request and manage LetsEncrypt SSL certs using the DNS Challenge method. For our use-case we will use DnsRobocert to get wild-card certs for the domain
NOTE: Sish has support for getting certs from LetsEncrypt, but it requests certs everytime someone uses the service to setup a tunnel with a new subdomain. Depending on how heavy your deployment gets used, you may run into LetsEncrypt quota limits with this method. So its better to get wild-card certs for your domain and not have to go back to LetsEncrypt for every new sub-domain
Since we are using Cloudflare for DNS, we first need to generate an API Token that has Zone Edit access for our domain. Follow the Cloudflare Docs to create the token using the “Edit Zone DNS” template.
Edit the file
dnsrobocert/dnsrobocert-config.yml to look like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 acme: email_account: email@example.com staging: false certificates: - autorestart: - containers: - sish domains: - mysish.co - '*.mysish.co' name: mysish.co profile: cloudflare profiles: - name: cloudflare provider: cloudflare provider_options: auth_token: <CLOUDFLARE API TOKEN>
The email address
firstname.lastname@example.org doesn’t necessarily have to be a valid email address.
Create cert symlinks
This part is important to ensure that
sish can find the right certs generated by
1 2 $ ln -sf /etc/letsencrypt/live/mysish.co/fullchain.pem ssl/mysish.co.crt $ ln -sf /etc/letsencrypt/live/mysish.co/privkey.pem ssl/mysish.co.key
The symlinks will not make sense on the host filesystem. But our
docker-compose.yml will mount certain folders inside each container and this will resolve correctly inside the container.
Add your initial SSH public key
We will be setting up
sish to only use public key authentication. The app automatically loads any public keys it finds in a designated
pubkeys folder. To start off copy your ssh public key to the pubkey folder
1 2 3 4 $ cp /path/to/ssh_key.pub pubkeys ### OR you can get pubkeys from github if you have added them there ### Replace antoniomika with the github username you want to give access to $ curl https://github.com/antoniomika.keys > pubkeys/antoniomika
sish doesn’t really care about the username you use for the
ssh auth. It only authenticates the ssh key. At any time you can add additional ssh keys to
pubkeys folder and
sish will automatically load them.
Setup DNS Records
- Add a DNS A record for
mysish.coto point to the public IP address of your VPS or cloud instance.
- Add a DNS A record for
*.mysish.coto point to the same IP address.
sish to work it needs to be able to listen on three ports for the three services
- http: typically port 80, but can be changed to any other port. Its best to block inbound access to this port at the firewall and only use https.
- https: typically port 443, but can be changed.
- ssh: typically port 22. But since the host
sshdaemon is already using this we will have to use an alternative port. In our case will be using 2222.
For docker apps, there are typically two ways for a container to receive outside traffic on a port
- Port forwarding from the host to the container using docker args
- Run the container in host network mode and have it directly bind to the ports on the host.
For this deployment I went with the latter since this is the only service I plan to run on this host.
docker-compose.yml to look like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 version: '3.7' services: letsencrypt: image: adferrand/dnsrobocert:latest container_name: dnsrobocert volumes: - ./letsencrypt:/etc/letsencrypt - ./dnsrobocert/dnsrobocert-config.yml:/etc/dnsrobocert/config.yml restart: always sish: image: antoniomika/sish:main container_name: sish depends_on: - letsencrypt volumes: - ./letsencrypt:/etc/letsencrypt - ./log:/var/log/sish - ./pubkeys:/pubkeys - ./keys:/keys - ./ssl:/ssl command: | --ssh-address=:2222 --http-address=:80 --https-address=:443 --https=true --https-certificate-directory=/ssl --log-to-file --log-to-file-path=/var/log/sish/sish.log --log-to-file-max-size=200 --authentication=true --authentication-keys-directory=/pubkeys --private-key-location=/keys/ssh_key --bind-random-ports=false --bind-random-subdomains=false --service-console --domain=mysish.co network_mode: host restart: always
Start it up
1 $ docker-compose up -d
To check the logs from the app
1 $ tail -f log/sish.log
Test it out.
Lets says you have a simple python http server running on port 4000 on your laptop that would like to give access to someone. First ensure that you have the private key corresponding to one of the public keys published in the
pubkeys folder on the
On your laptop where you have the development app server running
1 2 $ ssh -p 2222 -R hostit:80:localhost:4000 mysish.co
The important part is this
-R hostit:80:localhost:4000. Lets break this down:
-Ris telling ssh to create a reverse port forward over the ssh connection
hostitis the subdomain we are requesting.
80indicates that the app on the laptop is using http and not https. This has nothing to do with the final public address that
sishwill provide. That can still be accessed over https.
localhostis pointing to your laptop. This can also be pointing to some other host thats reachable from your laptop. For e.g. you may have a RaspberryPi running the http server at
192.168.10.23:4000and this can be accessed from your laptop. To have
https://hostit.mysish.somap to this, change
4000is the port on which the local http server is listening on.
If everything was setup correctly the ssh command will spit out something like this
1 2 3 4 5 6 7 Press Ctrl-C to close the session. Starting SSH Forwarding service for https:443. Forwarded connections can be accessed via the following methods: Service console can be accessed here: https://hostit.mysish.co/_sish/console?x-authorization=akdjfasdFHJh34785 HTTP: http://hostit.mysish.co HTTPS: https://hostit.mysish.co
Now anyone can access https://hostit.mysish.co and to the local http server.
fail2ban filter to ban bots and scanners
sish mostly as a fun challenge to see if it can be done. If you use
ngrok a lot then maybe running
sish might be worth your time. Another option that be used the same result is Cloudflare’s Argo Tunnel.