I put my SSH app on the internet.
Yes, I admit that I am highly inspired by Terminal.shop. It really is a great product and stimulates my creativity.
So, what to do with SSH? Here are my initial thoughts:
- SSH app should be faster than web app - it only sends blocks of Unicode characters over TCP (no JavaScript, no images, etc.)
- And because of that, it would be more difficult to make visually rich apps like ones with React or Vue - at the same time, it would be more visually appealing to those who love retro style/ascii art/terminal.
- Exposing SSH app to the internet (still) sounds scary 😨
Bubbletea and Wish
Bubbletea is a great TUI framework for Go, built by Charm. It is so easy to use and has a lot of features. I have been using it for a while in my site projects and also at work. I really like it. It's also a good way to learn about how it's like to build an app with the Elm architecture. Go itself doesn't have good mutation stories in my opinion, and sometimes I'm confused about how to manage state in a Bubbletea app. After all bubbletea is a great way to make a TUI app in Go. Although it doesn't have informative API docs, its GitHub repository provides a number of example codes that are very helpful.
Wish is a framework for building SSH apps. And since it's also built by Charm, it's so easy to integrate with Bubbletea.
The framework itself is very similar to web frameworks like I have used in the past. So if you are like and know routing, middleware, etc. The barrier to entry is very low.
My app
I created a simple portflolio app with the libraries above. If you are interested in it, open your terminal and run the following command:
ssh ssh.hiro.one
For the first time, you shold be asked to add thwe host to your known_hosts
(that's how SSH works my friend) so please hit "yes". If you want to exit from the app, just hit Ctrl+c
, Esc
, or q
.
As I mentioned above, building an app with Wish is very similar to building web one. But building a Bubbletea app is a bit different from the one with React. I spent a month to get it working and I still have no idea how to fill the inside of a textarea in contact form 🥲. I'll figure it out later anyway.
Deployment
So I have this Raspberry pi 4 device at my house that runs Dokploy on it + Cloudflare Tunnel to expose my apps. This set up enables me to avoid paying for any cloud infra (I want to achieve this for free). My initial idea was to run my brand-new SSH app on top of my Dokploy and expose it over Cloudflare Tunnel. But it turns out that Cloudflare Tunnel doesn't support TCP protocol (only HTTP/HTTPS). Technically you can use the tunnel to expose SSH server, but it becomes browser-based SSH app, which is not what I want.
So I had to seek other cheaper way to expose my SSH app to the internet (ideally for free). After days of research, I have concluded that GCP Compute Engine with e2-micro instance (which is free forever if you are within the free tier limit) is the way to go. Technically I need to pay for its storage fee monthly. But I could't find any other cheaper way to achieve my goal (exposing my home server is not allowed by my ISP).
At the moment, my idea was to put a VM on GCP and use it as an SSH reverse proxy, then I reverse SSH from my home server to the VM then expose arbitrary TCP port.
Here is a diagram that depects my initial plan:
User ----> | VM | ----> | SSH bridge ----> SSH app |
-- Dokploy --
-- GCP -- -- HOME ------------------------
However, I had a few issues with this setup.
Latency Issue
GCP Compute Engine free-tier is only available for a server in the US region. And I live in Tokyo. So there is a definite distance between the two locations that causes this latency. In my case my SSH request goes all the way from Tokyo to the US datacenter, it goes to my home server in Tokyo, then my server sends a response data that goes again from the Tokyo, the U.S., and Tokyo. I can see the difference between my local dev server and the one with GCP reverse proxy. It seems it takes 0.3-4 ms to move to one page to another when you hit a key.
me (JP) --> proxy (US) --> app(JP) --> proxy (US) --> me (JP)
---------- request --------- ---------- response ----------
🤔
You can see terminal.shop also has a similar issue when you visit the app (this is probably negligible for them as their customers are based in the U.S.).
I think it shouldn't be a problem as it's just my hobby project.
Color Issue
My SSH app uses catppuccin as color theme of the entire app. The problem is Docker container, by default, doesn't have TTY enabled, so that my app will be just black and white (of course! Almost all web apps don't need it). You can enable it by providing -t
or --tty
flag (it allocates a pseudo-TTY).
However, it looks like enabling PTY on Docker container doesn't provide 256-bit color capability. The color I see in the production app is slightly different from what I see in my local development environment. I still don't know how to work this around and wonder how the terminal folks solved this.
One way to solve the color issue is to deploy my app to the VM on GCP (This should also alleviate the latency issue). But this way makes me feel like I'm vulnerable.
Closing Note
So what's my server set up? I run my app as a Docker container on my VM. Basically an app built with Wish framework is not an SSH server, but just a terminal app that communicate with remote client over SSH protocol - meaning there is no way for an attacker to get a shell on my machine (my app doesn't have any way to interact with the shell). Even if my app gets compromised in any way, it's still secure as the container is isolated (+ I use Distroless image, which doesn't have sh/bash).
This is just my hobby project. No one cares about the poor color quality and the slowness.
Anyway, if you are interested in what I have done, you can visit my SSH site. I hope you enjoy it.
Top comments (0)